Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions depends/lua/src/ldo.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,33 @@
#define errorstatus(s) ((s) > LUA_YIELD)


/*
** {======================================================
** DFHACK: runtime sanity checks around C function results
** =======================================================
*/

/*
** api_checknelems() compiles to nothing in release builds, so a C
** function (or a continuation function) that returns a result count
** inconsistent with the number of values actually on the stack would
** make luaD_poscall()/moveresults() read and write outside the stack,
** hard-crashing the host process. Convert that condition into a
** catchable Lua error instead. This check is two comparisons on a path
** already dominated by the call itself, so it is cheap enough to run on
** every call, including hot paths like the overlay render loop.
*/
#define DFHack_checkcresults(L,ci,n,f) \
if ((n) < 0 || (n) >= (L)->top - (ci)->func) \
luaG_runerror(L, \
"lua_CFunction %p reported invalid result count %d", (void *)(f), (n));


/*
** }======================================================
*/


/*
** {======================================================
** Error-recovery functions
Expand Down Expand Up @@ -386,6 +413,15 @@ int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, int nres) {
}
res = ci->func; /* res == final position of 1st result */
L->ci = ci->previous; /* back to caller */
/* DFHACK: sanity-check the pointers moveresults() is about to use. A
corrupted pointer here would otherwise read or write outside the
stack and hard-crash (or silently corrupt) the host process. */
if (firstResult < L->stack || firstResult > L->top ||
res < L->stack || res >= L->stack_last) {
if (L->top > L->ci->top)
L->top = L->ci->top; /* make sure the error value has a safe home */
luaG_runerror(L, "corrupted Lua call frame");
Comment thread
SilasD marked this conversation as resolved.
}
/* move results to proper place */
return moveresults(L, firstResult, res, nres, wanted);
}
Expand Down Expand Up @@ -434,6 +470,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) {
n = (*f)(L); /* do the actual call */
lua_lock(L);
api_checknelems(L, n);
DFHack_checkcresults(L, ci, n, f); /* DFHACK */
luaD_poscall(L, ci, L->top - n, n);
return 1;
}
Expand Down Expand Up @@ -533,6 +570,7 @@ static void finishCcall (lua_State *L, int status) {
n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation function */
lua_lock(L);
api_checknelems(L, n);
DFHack_checkcresults(L, ci, n, ci->u.c.k); /* DFHACK */
luaD_poscall(L, ci, L->top - n, n); /* finish 'luaD_precall' */
}

Expand Down Expand Up @@ -636,6 +674,7 @@ static void resume (lua_State *L, void *ud) {
n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */
lua_lock(L);
api_checknelems(L, n);
DFHack_checkcresults(L, ci, n, ci->u.c.k); /* DFHACK */
firstArg = L->top - n; /* yield results come from continuation */
}
luaD_poscall(L, ci, firstArg, n); /* finish 'luaD_precall' */
Expand Down
2 changes: 2 additions & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ Template for new versions:
## Fixes

## Misc Improvements
- `overlay`: errors raised by a bad C function result count or corrupted Lua call stack are now reported as Lua errors instead of crashing the game
- crash reports now include a Lua stack traceback when the game crashes while Lua code is executing (``crashlog/crash_*.txt`` on Linux, ``stderr.log`` on Windows)

## Documentation

Expand Down
2 changes: 2 additions & 0 deletions library/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ set(MAIN_HEADERS
include/CoordTemplate.h
include/Core.h
include/CoreDefs.h
include/Crashlog.h
include/DataDefs.h
include/DataFuncs.h
include/DataIdentity.h
Expand Down Expand Up @@ -131,6 +132,7 @@ endif()

set(MAIN_SOURCES_WINDOWS
${CONSOLE_SOURCES}
Crashlog-windows.cpp
)

if(WIN32)
Expand Down
19 changes: 15 additions & 4 deletions library/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ distribution.
*/

#include "Core.h"
#include "Crashlog.h"

#include "Internal.h"

Expand Down Expand Up @@ -1096,8 +1097,7 @@ bool Core::InitMainThread(std::filesystem::path path) {

Filesystem::init();

#ifdef LINUX_BUILD
extern void dfhack_crashlog_init();
#if defined(LINUX_BUILD) || defined(WIN32)
dfhack_crashlog_init();
#endif

Expand Down Expand Up @@ -1517,6 +1517,18 @@ bool Core::isSuspended(void)

void Core::doUpdate(color_ostream &out)
{
#ifdef WIN32
// Re-chain the top-level exception filter once DF is fully running,
// in case DF installed its own crash filter after our init. If ours
// is still installed, this is a no-op.
static bool crashlog_rearmed = false;
if (!crashlog_rearmed)
{
crashlog_rearmed = true;
dfhack_crashlog_init();
}
#endif

Lua::Core::Reset(out, "DF code execution");

// find the current viewscreen
Expand Down Expand Up @@ -1914,8 +1926,7 @@ void Core::onStateChange(color_ostream &out, state_change_event event)

int Core::Shutdown ( void )
{
#ifdef LINUX_BUILD
extern void dfhack_crashlog_shutdown();
#if defined(LINUX_BUILD) || defined(WIN32)
dfhack_crashlog_shutdown();
#endif

Expand Down
Loading
Loading