mGBA crashes when executing `error({})` in lua
I was testing out compiling Haxe scripts to Lua to run in mGBA, and it generates the following code:
-- ...
function _hx_handle_error(obj)
local message = tostring(obj)
if _G.debug and _G.debug.traceback then
-- level 2 to skip _hx_handle_error
message = _G.debug.traceback(message, 2)
end
return setmetatable({}, { __tostring = function() return message end })
end
_hx_static_init();
local success, err = _G.xpcall(function()
Example.main();
_hx_luv.run();
end, _hx_handle_error)
if not success then _G.error(err) endI'm not super familiar with either Lua or Haxe, but it seems Haxe uses xpcall to register an error handler which will change the error value into an object with a __tostring implementation and then let that go to the error call. It is valid in Lua to pass non-string values to error.
I've minimized this to the following repro:
error({})This crashes both mGBA 10.5 and the head of the main branch (both ran via a Nix flake).
KDE's crash manager reports the following stacktrace:
Stack trace of thread 271131:
#0 0x00007fbfdb581e5d __strlen_avx2 (libc.so.6 + 0x181e5d)
#1 0x00007fbfdb4b56b3 __strdup (libc.so.6 + 0xb56b3)
#2 0x00007fbfe01b7f4b _luaRun (libmgba.so.0.11 + 0x1b7f4b)
#3 0x0000561bc2dc6b12 _ZN4QGBA19ScriptingController4loadERNS_11VFileDeviceERK7QString (.mgba-qt-wrapped + 0x1fbb12)
#4 0x0000561bc2dc8577 _ZN4QGBA19ScriptingController8loadFileERK7QString (.mgba-qt-wrapped + 0x1fd577)
#5 0x0000561bc2dc86a7 _ZN4QGBA13ScriptingView4loadEv (.mgba-qt-wrapped + 0x1fd6a7)
#6 0x00007fbfdbf48ae1 _Z10doActivateILb0EEvP7QObjectiPPv (libQt5Core.so.5 + 0x348ae1)
#7 0x00007fbfdf969dd0 _ZN7QAction9triggeredEb (libQt5Widgets.so.5 + 0x169dd0)
#8 0x00007fbfdf96cc94 _ZN7QAction8activateENS_11ActionEventE (libQt5Widgets.so.5 + 0x16cc94)
#9 0x00007fbfdfb0d0a2 _ZN12QMenuPrivate19activateCausedStackERK7QVectorI8QPointerI7QWidgetEEP7QActionNS7_11ActionEventEb (libQt5Widgets.so.5 + 0x30d0a2)
#10 0x00007fbfdfb1547a _ZN12QMenuPrivate14activateActionEP7QActionNS0_11ActionEventEb (libQt5Widgets.so.5 + 0x31547a)
#11 0x00007fbfdf9b7508 _ZN7QWidget5eventEP6QEvent (libQt5Widgets.so.5 + 0x1b7508)
#12 0x00007fbfdf972277 _ZN19QApplicationPrivate13notify_helperEP7QObjectP6QEvent (libQt5Widgets.so.5 + 0x172277)
#13 0x00007fbfdf979f0f _ZN12QApplication6notifyEP7QObjectP6QEvent (libQt5Widgets.so.5 + 0x179f0f)
#14 0x00007fbfdbf0b0c8 _ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent (libQt5Core.so.5 + 0x30b0c8)
#15 0x00007fbfdf977dba _ZN19QApplicationPrivate14sendMouseEventEP7QWidgetP11QMouseEventS1_S1_PS1_R8QPointerIS0_Ebb (libQt5Widgets.so.5 + 0x177dba)
#16 0x00007fbfdf9d3310 _ZN13QWidgetWindow16handleMouseEventEP11QMouseEvent (libQt5Widgets.so.5 + 0x1d3310)
#17 0x00007fbfdf9d624f _ZN13QWidgetWindow5eventEP6QEvent (libQt5Widgets.so.5 + 0x1d624f)
#18 0x00007fbfdf972277 _ZN19QApplicationPrivate13notify_helperEP7QObjectP6QEvent (libQt5Widgets.so.5 + 0x172277)
#19 0x00007fbfdbf0b0c8 _ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent (libQt5Core.so.5 + 0x30b0c8)
#20 0x00007fbfdc336b77 _ZN22QGuiApplicationPrivate17processMouseEventEPN29QWindowSystemInterfacePrivate10MouseEventE (libQt5Gui.so.5 + 0x136b77)
#21 0x00007fbfdc304da4 _ZN22QWindowSystemInterface22sendWindowSystemEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE (libQt5Gui.so.5 + 0x104da4)
#22 0x00007fbfd29603d0 _ZL23userEventSourceDispatchP8_GSourcePFiPvES1_ (libQt5WaylandClient.so.5 + 0xb93d0)
#23 0x00007fbfdbb00c0b g_main_context_dispatch_unlocked (libglib-2.0.so.0 + 0x62c0b)
#24 0x00007fbfdbb041d8 g_main_context_iterate_unlocked.isra.0 (libglib-2.0.so.0 + 0x661d8)
#25 0x00007fbfdbb04a9f g_main_context_iteration (libglib-2.0.so.0 + 0x66a9f)
#26 0x00007fbfdbf6a463 _ZN20QEventDispatcherGlib13processEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE (libQt5Core.so.5 + 0x36a463)
#27 0x00007fbfdbf098ab _ZN10QEventLoop4execE6QFlagsINS_17ProcessEventsFlagEE (libQt5Core.so.5 + 0x3098ab)
#28 0x00007fbfdbf12b34 _ZN16QCoreApplication4execEv (libQt5Core.so.5 + 0x312b34)
#29 0x0000561bc2c5fd77 main (.mgba-qt-wrapped + 0x94d77)
#30 0x00007fbfdb42b285 __libc_start_call_main (libc.so.6 + 0x2b285)
#31 0x00007fbfdb42b338 __libc_start_main@@GLIBC_2.34 (libc.so.6 + 0x2b338)
#32 0x0000561bc2c614c5 _start (.mgba-qt-wrapped + 0x964c5) As a local workaround, I let an LLM generate the following patch which seems to fix it on my end. I don't know if it covers all cases where mGBA interprets a Lua object as a string value, though.
diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c
index 27b52ae3..e2b9b2c6 100644
--- a/src/script/engines/lua.c
+++ b/src/script/engines/lua.c
@@ -1041,8 +1041,11 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi
luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX);
return true;
case LUA_ERRSYNTAX:
- luaContext->lastError = strdup(lua_tostring(luaContext->lua, -1));
- lua_pop(luaContext->lua, 1);
+ int errorIndex = lua_gettop(luaContext->lua);
+ const char* error = _luaTolstring(luaContext->lua, errorIndex, NULL);
+ luaContext->lastError = strdup(error ? error : "Unknown Lua error");
+ /* _luaTolstring pushes a string, in addition to the original error. */
+ lua_pop(luaContext->lua, 2);
_luaError(luaContext);
break;
default:
@@ -1208,8 +1211,11 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame*
lua_rawset(luaContext->lua, LUA_REGISTRYINDEX);
if (ret == LUA_ERRRUN) {
- luaContext->lastError = strdup(lua_tostring(luaContext->lua, -1));
- lua_pop(luaContext->lua, 1);
+ int errorIndex = lua_gettop(luaContext->lua);
+ const char* error = _luaTolstring(luaContext->lua, errorIndex, NULL);
+ luaContext->lastError = strdup(error ? error : "Unknown Lua error");
+ /* _luaTolstring pushes a string, in addition to the original error. */
+ lua_pop(luaContext->lua, 2);
_luaError(luaContext);
}Source: mgba-emu/mgba