From 1eae512285e7131f4bf48416f9d4b8b1124e223a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 18 Aug 2026 08:30:42 -0400 Subject: [PATCH] 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. --- src/nvim/lua/executor.c | 2 ++ src/nvim/memory.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index bffa924d8f..402fbcd52f 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -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(); diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 0b1b884b11..e9dbd28210 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -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;