|
I have stumbled across a situation that causes Lua to crash. I am wondering if this is a bug.
If a coroutine is created and immediately yields, then using the coroutine’s lua_State for a ‘lua_pcall’ to debug.traceback() will work fine. However, if a coroutine is created and is assigned a debug hook which causes it to yield, then doing the same thing will cause the program to crash. If the main lua_State performs the call to ‘debug.traceback()’, passing the coroutine as a parameter, then the debug.traceback() will work fine. If the coroutine’s lua_State is used to invoke the C API ‘lua_getstack’ and ‘lua_getinfo’ then no crash will occur, though occasionally the currentline of the lua_Debug structure will be incorrect. I have include a test case to reproduce the bug. Compiling and running this code as it is will reproduce the bug.
#include <lua.h> #include <lualib.h> #include <lauxlib.h> #include <stdio.h> #define TEST1 //#define TEST2 //#define TEST3 static void yield_me(lua_State* L, lua_Debug* ar) { if (ar->event == LUA_HOOKCOUNT || ar->event == LUA_HOOKLINE) { lua_yield(L, 0); } } void test() { lua_State* Lmain = luaL_newstate(); luaL_openlibs(Lmain); //running arbitrary code and yielding from a hook causes this crash if (luaL_loadstring(Lmain, "for i=1,100 do end") != LUA_OK) { printf("luaL_loadstring failed\n"); return; } lua_State* Lco = lua_newthread(Lmain); //Lmain: main, Lco lua_insert(Lmain, lua_gettop(Lmain) - 1); //Lmain: Lco, main lua_xmove(Lmain, Lco, 1); //Lmain: Lco, Lco: main lua_sethook(Lco, yield_me, LUA_MASKCOUNT, 10); int status = lua_resume(Lco, Lmain, 0); if (status != LUA_YIELD) { printf("expected us to be yielded, got status %d\n", status); return; } lua_Hook func = lua_gethook(Lco); int mask = lua_gethookmask(Lco); int count = lua_gethookcount(Lco); lua_sethook(Lco, NULL, 0, 0); #ifdef TEST1 //running a traceback using the coroutine ... crashes if (luaL_loadstring(Lco, "print(debug.traceback())") != LUA_OK) { printf("error\n"); return; } if (lua_pcall(Lco, 0, 0, 0) != LUA_OK) { printf("error\n"); return; } #endif #ifdef TEST2 //calling debug.traceback() from the main lua_State, but passing 'Lco' as a parameter to debug.traceback() ... works lua_getglobal(Lmain, "debug"); lua_getfield(Lmain, -1, "traceback"); lua_pushthread(Lco); lua_xmove(Lco, Lmain, 1); if (lua_pcall(Lmain, 1, 0, 0) != LUA_OK) { printf("error\n"); return; } lua_pop(Lmain, 1); #endif #ifdef TEST3 //Performing the traceback using the C API from the coroutine ... works, albeit with a bad line value
lua_Debug ar; if (!lua_getstack(Lco, 0, &ar)) { printf("lua_getstack failed\n"); return; } else { int status = lua_getinfo(Lco, "Sln", &ar); if (!status) { printf("lua_getinfo failed\n"); return; } printf("line: %d file: %s func: %s\n", ar.currentline, ar.short_src, ar.name ? ar.name : "(null)" ); } #endif printf("worked\n"); lua_close(Lmain); } int main() { test(); } |