commit 069045d59af704cf8f0bf27432436e27b5712ede from: Sergey Bronnikov date: Wed Jul 10 15:18:15 2024 UTC luzer: fix stack overflow due to recursive traceback Lua 5.1 has no support of `luaL_traceback` function. Previously, it was implemented using Lua function `debug.traceback` and this leads to recursive calls and finished with stack overflow. The patch backports implementation of `luaL_traceback` from a late Lua versions. commit - 442f7ad87d59fba01c2e517a75032dfb69ccbd4b commit + 069045d59af704cf8f0bf27432436e27b5712ede blob - 39e35003aeb751ee86d026778836562e89c82661 blob + dc5c92c1a8050487908134d6172d991753dc5081 --- CHANGELOG.md +++ CHANGELOG.md @@ -23,3 +23,4 @@ and this project adheres to [Semantic Versioning](http ### Fixed - Fix searching Clang RT. +- Stack overflow due to recursive traceback calls. blob - 4f76421a49c64be403566d3f75ae3e1a88932534 blob + ac026f1d081536b6d23d317b6584b7a304a66405 --- luzer/CMakeLists.txt +++ luzer/CMakeLists.txt @@ -7,6 +7,7 @@ configure_file( ) set(LUZER_SOURCES luzer.c + compat.c fuzzed_data_provider.cc tracer.c counters.c blob - /dev/null blob + 2bc398c993cfe44c971913beff31b3c6ba368a9b (mode 644) --- /dev/null +++ luzer/compat.c @@ -0,0 +1,151 @@ +#include +#include +#include + +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501 + +static int +countlevels(lua_State *L) { + lua_Debug ar; + int li = 1, le = 1; + /* Find an upper bound. */ + while (lua_getstack(L, le, &ar)) + { li = le; le *= 2; } + /* Do a binary search. */ + while (li < le) { + int m = (li + le) / 2; + if (lua_getstack(L, m, &ar)) + li = m + 1; + else + le = m; + } + return le - 1; +} + +static int +findfield(lua_State *L, int objidx, int level) { + if (level == 0 || !lua_istable(L, -1)) + /* Not found. */ + return 0; + + /* Start 'next' loop. */ + lua_pushnil(L); + /* For each pair in table. */ + while (lua_next(L, -2)) { + /* Ignore non-string keys. */ + if (lua_type(L, -2) == LUA_TSTRING) { + /* Found an object? */ + if (lua_rawequal(L, objidx, -1)) { + /* Remove value (but keep name). */ + lua_pop(L, 1); + return 1; + } + /* Try recursively. */ + else if (findfield(L, objidx, level - 1)) { + /* Remove table (but keep name). */ + lua_remove(L, -2); + lua_pushliteral(L, "."); + /* Place '.' between the two names. */ + lua_insert(L, -2); + lua_concat(L, 3); + return 1; + } + } + /* Remove value. */ + lua_pop(L, 1); + } + /* Not found. */ + return 0; +} + +int +lua_absindex(lua_State *L, int i) { + if (i < 0 && i > LUA_REGISTRYINDEX) + i += lua_gettop(L) + 1; + return i; +} + +void +lua_copy(lua_State *L, int from, int to) { + int abs_to = lua_absindex(L, to); + luaL_checkstack(L, 1, "not enough stack slots"); + lua_pushvalue(L, from); + lua_replace(L, abs_to); +} + +static int +pushglobalfuncname(lua_State *L, lua_Debug *ar) { + int top = lua_gettop(L); + /* Push function. */ + lua_getinfo(L, "f", ar); + lua_pushvalue(L, LUA_GLOBALSINDEX); + if (findfield(L, top + 1, 2)) { + /* Move name to proper place. */ + lua_copy(L, -1, top + 1); + /* Remove pushed values. */ + lua_pop(L, 2); + return 1; + } + else { + /* Remove function and global table. */ + lua_settop(L, top); + return 0; + } +} + +static void +pushfuncname(lua_State *L, lua_Debug *ar) { + /* Is there a name? */ + if (*ar->namewhat != '\0') + lua_pushfstring(L, "function " LUA_QS, ar->name); + else if (*ar->what == 'm') /* Main? */ + lua_pushliteral(L, "main chunk"); + else if (*ar->what == 'C') { + if (pushglobalfuncname(L, ar)) { + lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1)); + /* Remove name. */ + lua_remove(L, -2); + } + else + lua_pushliteral(L, "?"); + } + else + lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); +} + +/* Size of the first part of the stack. */ +#define LEVELS1 12 +/* Size of the second part of the stack. */ +#define LEVELS2 10 + +void +luaL_traceback(lua_State *L, lua_State *L1, + const char *msg, int level) { + lua_Debug ar; + int top = lua_gettop(L); + int numlevels = countlevels(L1); + int mark = (numlevels > LEVELS1 + LEVELS2) ? LEVELS1 : 0; + if (msg) lua_pushfstring(L, "%s\n", msg); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + /* Too many levels? */ + if (level == mark) { + /* Add a '...'. */ + lua_pushliteral(L, "\n\t..."); + /* And skip to last ones. */ + level = numlevels - LEVELS2; + } + else { + lua_getinfo(L1, "Slnt", &ar); + lua_pushfstring(L, "\n\t%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + lua_pushliteral(L, " in "); + pushfuncname(L, &ar); + lua_concat(L, lua_gettop(L) - top); + } + } + lua_concat(L, lua_gettop(L) - top); +} + +#endif blob - /dev/null blob + 7859dbb4055b9153a40bf7f544d0a3b8a50a0285 (mode 644) --- /dev/null +++ luzer/compat.h @@ -0,0 +1,7 @@ +#ifndef LUZER_COMPAT_H_ +#define LUZER_COMPAT_H_ + +void luaL_traceback(lua_State *L, lua_State *L1, + const char *msg, int level); + +#endif // LUZER_COMPAT_H_ blob - 4943749b01ea81ef9a32314750447497e724f0a8 blob + e740944321eb16fa400d8e86fb3e6cba3688772d --- luzer/luzer.c +++ luzer/luzer.c @@ -21,6 +21,7 @@ #include "fuzzed_data_provider.h" #include "counters.h" +#include "compat.h" #include "macros.h" #include "tracer.h" #include "version.h" @@ -48,27 +49,6 @@ get_global_lua_state(void) return LL; } -#if LUA_VERSION_NUM < 502 -static int -luaL_traceback(lua_State *L) { - lua_getfield(L, LUA_GLOBALSINDEX, "debug"); - if (!lua_istable(L, -1)) { - lua_pop(L, 1); - return 1; - } - lua_getfield(L, -1, "traceback"); - if (!lua_isfunction(L, -1)) { - lua_pop(L, 2); - return 1; - } - lua_pushvalue(L, 1); - lua_pushinteger(L, 2); - lua_call(L, 2, 1); - fprintf(stderr, "%s\n", lua_tostring(L, -1)); - return 1; -} -#endif - #ifdef __cplusplus extern "C" { #endif @@ -109,20 +89,17 @@ __sanitizer_acquire_crash_state(void) } /** - * Print the stack trace leading to this call. Useful for debugging user code. - * See: - * - https://github.com/keplerproject/lua-compat-5.2/blob/master/c-api/compat-5.2.c#L229 - * - http://www.lua.org/manual/5.2/manual.html#luaL_traceback + * Print a Lua stack trace leading to this call. + * Useful for debugging user code. + * See http://www.lua.org/manual/5.2/manual.html#luaL_traceback */ NO_SANITIZE void __sanitizer_print_stack_trace(void) { lua_State *L = get_global_lua_state(); -#if LUA_VERSION_NUM < 502 - luaL_traceback(L); -#else - luaL_traceback(L, L, "traceback", 3); -#endif + lua_State *L1 = luaL_newstate(); + luaL_traceback(L, L1, "traceback", 3); + lua_close(L1); } #ifdef __cplusplus } /* extern "C" */