fix(lifecycle): Lua state not freed if free_all_mem() is re-entered #41366

Problem:
Leak reported by ASAN CI, always at Lua state init.

      ERROR: LeakSanitizer: detected memory leaks
          strbuf_init src/cjson/strbuf.c:62:22
          json_create_config src/cjson/lua_cjson.c:635:5
          lua_cjson_new src/cjson/lua_cjson.c:2060:5
          nlua_state_add_stdlib src/nvim/lua/stdlib.c:840:3
          nlua_state_init src/nvim/lua/executor.c:973:3
      ...
      SUMMARY: AddressSanitizer: 1055 byte(s) leaked in 2 allocation(s).

Analysis:
`getout()` may run during `free_all_mem()` (that's why it checks
`!entered_free_all_mem`). If that happens, the second `os_exit()`
returns early from `free_all_mem()` then `exit()` is called and Lua
state is never closed.

Solution:
Always call `nlua_free_all_mem()` in the early-return case.
This commit is contained in:
Justin M. Keyes
2026-08-18 08:30:42 -04:00
committed by GitHub
parent f6bf814378
commit 1eae512285
2 changed files with 5 additions and 2 deletions

View File

@@ -1061,12 +1061,14 @@ static lua_State *nlua_init_state(bool thread)
return lstate;
}
/// Disposes global Lua state. Idempotent, harmless if called multiple times.
void nlua_free_all_mem(void)
{
if (!global_lstate) {
return;
}
lua_State *lstate = global_lstate;
global_lstate = NULL;
nlua_unref_global(lstate, require_ref);
nlua_common_free_all_mem(lstate);
nlua_treesitter_free();

View File

@@ -861,9 +861,10 @@ void free_all_mem(void)
{
buf_T *buf, *nextbuf;
// When we cause a crash here it is caught and Vim tries to exit cleanly.
// Don't try freeing everything again.
// If a routine below recurses into free_all_mem, don't try freeing everything again.
if (entered_free_all_mem) {
// Except the Lua state. #39675
nlua_free_all_mem();
return;
}
entered_free_all_mem = true;