From 9e9083d0e2fd586dcd526d2180ecda533cd187ab Mon Sep 17 00:00:00 2001 From: Aurelien DARRAGON Date: Fri, 23 Jan 2026 10:30:08 +0100 Subject: [PATCH] BUG/MEDIUM: hlua: fix invalid lua_pcall() usage in hlua_traceback() Since commit 365ee28 ("BUG/MINOR: hlua: prevent LJMP in hlua_traceback()") we now use lua_pcall() to protect sensitive parts of hlua_traceback() function, and this to prevent Lua from crashing the process in case of unexpected Lua error. This is still relevant, but an error was made, as lua_pcall() was given the nresult argument '1' when _hlua_traceback() internal function doesn't push any argument on the stack. Because of this, it seems Lua API still tries to push garbage object on top of the stack before returning. This may cause functions that leverage hlua_traceback() in the middle of stack manipulation to end up having a corrupted stack when continuing after the hlua_traceback(). There doesn't seem to be many places where this could be a problem, as this was discovered using the reproducer documented in f535d3e ("BUG/MEDIUM: debug: only dump Lua state when panicking"). Indeed, when hlua_traceback() was used from the signal handler while the thread was previously executing Lua, when returning to Lua after the handler the Lua stack would be corrupted. To fix the issue, we emphasize on the fact that the _hlua_traceback() function doesn't push anything on the stack, returns 0, thus lua_pcall() is given 0 'nresult' argument to prevent anything from being pushed after the execution, preserving the original stack state. This should be backported to all stable versions (because 365ee28 was backported there) --- src/hlua.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/hlua.c b/src/hlua.c index bb73b377c..4ef5a8cf5 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -870,6 +870,7 @@ void hlua_unref(lua_State *L, int ref) __LJMP static int _hlua_traceback(lua_State *L) { lua_Debug *ar = lua_touserdata(L, 1); + int ret; /* Fill fields: * 'S': fills in the fields source, short_src, linedefined, lastlinedefined, and what; @@ -877,7 +878,10 @@ __LJMP static int _hlua_traceback(lua_State *L) * 'n': fills in the field name and namewhat; * 't': fills in the field istailcall; */ - return lua_getinfo(L, "Slnt", ar); + ret = lua_getinfo(L, "Slnt", ar); + if (!ret) + WILL_LJMP(luaL_error(L, "unexpected")); + return 0; } @@ -896,7 +900,7 @@ const char *hlua_traceback(lua_State *L, const char* sep) lua_pushlightuserdata(L, &ar); /* safe getinfo */ - switch (lua_pcall(L, 1, 1, 0)) { + switch (lua_pcall(L, 1, 0, 0)) { case LUA_OK: break; default: -- 2.47.3