diff --git a/src/lapi.c b/src/lapi.c index bb07ca8..cdfa967 100644 --- a/src/lapi.c +++ b/src/lapi.c @@ -1233,6 +1233,21 @@ LUA_API int lua_error (lua_State *L) { lua_lock(L); LUAI_TRY_BLOCK(L) { api_checknelems(L, 1); + if (!lj.previous) { + /* Log only when there is no outer error handler -- i.e. the error + * will ultimately reach the panic branch. We check lj.previous + * (not L->errorJmp) because LUAI_TRY_BLOCK already set + * L->errorJmp = &lj, so L->errorJmp is always non-NULL here. + * + * Use raw accessors (ttisstring/svalue) instead of the public API + * (lua_isstring/lua_tostring). lua_isstring returns true for + * numbers, and lua_tostring -> lua_tolstring calls lua_lock for + * number-to-string conversion, which would deadlock since we + * already hold the lock from lua_error's lua_lock above. */ + StkId o = L->top - 1; + const char *msg = ttisstring(o) ? svalue(o) : "(non-string error)"; + thrlua_log(L, DCRITICAL, "lua_error (unhandled): %s\n", msg); + } luaG_errormsg(L); } LUAI_TRY_FINALLY(L) { lua_unlock(L); diff --git a/src/ldebug.c b/src/ldebug.c index ec60a37..e17b490 100644 --- a/src/ldebug.c +++ b/src/ldebug.c @@ -631,6 +631,11 @@ void luaG_errormsg (lua_State *L) { incr_top(L); luaD_call(L, L->top - 2, 1); /* call it */ } + if (!L->errorJmp) { + const char *msg = (L->top > L->base && ttisstring(L->top - 1)) + ? svalue(L->top - 1) : "(non-string error)"; + thrlua_log(L, DCRITICAL, "luaG_errormsg (unhandled): %s\n", msg); + } luaD_throw(L, LUA_ERRRUN); } diff --git a/src/ldo.c b/src/ldo.c index a64e320..9b2db4c 100644 --- a/src/ldo.c +++ b/src/ldo.c @@ -69,10 +69,23 @@ void luaD_throw (lua_State *L, int errcode) { LUAI_THROW(L, L->errorJmp); } else { + thrlua_log(L, DCRITICAL, + "luaD_throw: no error handler, errcode=%d, L=%p, invoking panic\n", + errcode, (void *)L); L->status = cast_byte(errcode); if (G(L)->panic) { resetstack(L, errcode); - lua_unlock(L); + /* Do NOT call lua_unlock(L) here. When luaD_throw is reached + * via LUAI_TRY_END re-throw, the LUAI_TRY_FINALLY block has + * already run and the lock state depends on the call site: + * some FINALLY blocks call lua_unlock (lapi.c, lauxlib.c) while + * others call lua_lock (ldo.c callhook/precall). Unconditionally + * unlocking here caused a double-unlock that corrupted the + * pthread mutex on Linux/glibc, making every subsequent + * lua_lock() on this lua_State fail with EINVAL and abort(). + * All direct callers of luaD_throw (lmem.c, ldebug.c, lundump.c, + * llex.c) always have an outer errorJmp so they never reach + * this panic branch. */ G(L)->panic(L); } exit(EXIT_FAILURE); diff --git a/src/lstrlib.c b/src/lstrlib.c index 6693eb3..35bfd81 100644 --- a/src/lstrlib.c +++ b/src/lstrlib.c @@ -138,8 +138,16 @@ static int str_dump (lua_State *L) { luaL_checktype(L, 1, LUA_TFUNCTION); lua_settop(L, 1); luaL_buffinit(L,&b); - if (lua_dump(L, writer, &b) != 0) - luaL_error(L, "unable to dump given function"); + if (lua_dump(L, writer, &b) != 0) { + lua_Debug ar; + lua_pushvalue(L, 1); + lua_getinfo(L, ">nS", &ar); + luaL_error(L, "unable to dump given function (%s '%s' defined at %s:%d)", + ar.what ? ar.what : "?", + ar.name ? ar.name : "(anonymous)", + ar.short_src ? ar.short_src : "?", + ar.linedefined); + } luaL_pushresult(&b); return 1; } diff --git a/src/thrlua.h b/src/thrlua.h index 3f39d8f..aa8a217 100644 --- a/src/thrlua.h +++ b/src/thrlua.h @@ -254,6 +254,16 @@ extern void lua_do_longjmp(luai_jmpbuf env, int val) #define LUAI_TRY_END(L) \ (L)->errorJmp = lj.previous; \ if (lj.status) { \ + /* Only log when there is NO outer handler -- that is the \ + * dangerous path that hits luaD_throw's panic branch. \ + * Normal error propagation (with an outer handler) is \ + * high-frequency and must not be logged. */ \ + if (!lj.previous) { \ + thrlua_log((L), DCRITICAL, \ + "LUAI_TRY_END: error status=%d in TRY block at %s:%d, " \ + "re-throwing with NO outer handler, L=%p\n", \ + lj.status, lj.file, lj.line, (void *)(L)); \ + } \ luaD_throw((L), lj.status); \ } \ } while(0)