Wrap Lua pcall() and xpcall() to rethrow jailbreak exceptions

Jailbreak exceptions thrown in Wesnoth C++ code called under Lua pcall()
or xpcall() aren't handled immediately, allowing such exceptions to
potentially accumulate and cause a failed assertion.

For example, if a user tries to quit while Lua code is running under
pcall() or xpcall(), LUAI_TRY() stores and consumes the jailbreak
exception and the rest of the Lua code continues running.  If the user
tries to quit again before the Lua code finishes, LUAI_TRY() attempts to
store another jailbreak exception, which causes wesnoth to abort.

This is a longstanding bug (since jailbreak exceptions were introduced
in commit d6512a0ef5, version 1.9.5) that could affect World Conquest
and 16 of the 588 addons in 1.16 and 7 of the 141 in 1.17.

Fix this by wrapping pcall() and xpcall() to rethrow jailbreak
exceptions.
This commit is contained in:
P. J. McDermott 2024-01-23 09:05:54 -05:00 committed by Pentarctagon
parent bcec952e1e
commit 83ad348037
2 changed files with 28 additions and 0 deletions

View file

@ -0,0 +1,3 @@
### Miscellaneous and Bug Fixes
* Fix delayed handling of Lua jailbreak exceptions (quit to menu or desktop, wesnothd connection errors, etc.) thrown during Lua `pcall()` and `xpcall()`, which could accumulate and cause wesnoth to abort. (PR #8234)
* This bug has existed since jailbreak exceptions were introduced in version 1.9.5.

View file

@ -302,6 +302,22 @@ static int intf_load(lua_State* L)
return 1;
}
/**
* Wrapper for pcall and xpcall functions to rethrow jailbreak exceptions
*/
static int intf_pcall(lua_State *L)
{
lua_CFunction function = lua_tocfunction(L, lua_upvalueindex(1));
assert(function); // The upvalue should be Lua's pcall or xpcall, or else something is very wrong.
int nRets = function(L);
// If a jailbreak exception was stored while running (x)pcall, rethrow it so Lua doesn't continue.
lua_jailbreak_exception::rethrow();
return nRets;
}
// The show lua console callback is similarly a method of lua kernel
int lua_kernel_base::intf_show_lua_console(lua_State *L)
{
@ -833,6 +849,15 @@ lua_kernel_base::lua_kernel_base()
lua_pushnil(L);
lua_setglobal(L, "loadstring");
// Wrap the pcall and xpcall functions
cmd_log_ << "Wrapping pcall and xpcall functions...\n";
lua_getglobal(L, "pcall");
lua_pushcclosure(L, intf_pcall, 1);
lua_setglobal(L, "pcall");
lua_getglobal(L, "xpcall");
lua_pushcclosure(L, intf_pcall, 1);
lua_setglobal(L, "xpcall");
cmd_log_ << "Initializing package repository...\n";
// Create the package table.
lua_getglobal(L, "wesnoth");