mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 07:28:22 -10:00
After getting the main state from the Lua registry, this function had two Lua thread values on its Lua stack but we only removed one which lead to a value being left on the stack. Why did this not blow up before? Because undefined behavior. The only place where this was called was when creating a Lua reference value where luaL_ref is called in the same expression (see UniqueLuaReference::create). The evaluation order of the parameters for the make_shared call is not defined by the standard and for some reason all major compilers (except clang) choose to execute the luaL_ref call before the getMainThread call. For those compilers, everything is fine. We have some extra values on the Lua stack but that gets cleaned up after the API function finishes. However, clang first evaluates the getMainThread which leaves an extra Lua thread on the Lua stack. The luaL_ref call then references this thread value instead of the intended value. In the concrete case, this meant that LuaFunction saw that the reference points to a thread and rightfully threw an exception. This fixes #5904.
67 lines
1.3 KiB
C++
67 lines
1.3 KiB
C++
|
|
#include "LuaUtil.h"
|
|
|
|
namespace {
|
|
const char* mainStateRefName = "_lua_mainthread";
|
|
}
|
|
|
|
namespace luacpp {
|
|
namespace util {
|
|
const char* getValueName(ValueType type)
|
|
{
|
|
switch (type) {
|
|
case ValueType::NONE:
|
|
return "none";
|
|
case ValueType::NIL:
|
|
return "nil";
|
|
case ValueType::BOOLEAN:
|
|
return "boolean";
|
|
case ValueType::LIGHTUSERDATA:
|
|
return "light userdata";
|
|
case ValueType::STRING:
|
|
return "string";
|
|
case ValueType::NUMBER:
|
|
return "number";
|
|
case ValueType::TABLE:
|
|
return "table";
|
|
case ValueType::FUNCTION:
|
|
return "function";
|
|
case ValueType::USERDATA:
|
|
return "userdata";
|
|
case ValueType::THREAD:
|
|
return "thread";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
void initializeLuaSupportLib(lua_State* L)
|
|
{
|
|
auto mainThread = lua_pushthread(L);
|
|
Assertion(mainThread, "Must be called with the main thread as the parameter!");
|
|
|
|
lua_setfield(L, LUA_REGISTRYINDEX, mainStateRefName);
|
|
}
|
|
|
|
lua_State* getMainThread(lua_State* L)
|
|
{
|
|
if (lua_pushthread(L)) {
|
|
// State is the main thread, just return that
|
|
lua_pop(L, 1);
|
|
return L;
|
|
}
|
|
|
|
lua_pushstring(L, mainStateRefName);
|
|
lua_rawget(L, LUA_REGISTRYINDEX);
|
|
|
|
auto state = lua_tothread(L, -1);
|
|
|
|
// Need to pop both the thread from L and the main thread to return the stack back to neutral
|
|
lua_pop(L, 2);
|
|
|
|
return state;
|
|
}
|
|
|
|
} // namespace util
|
|
} // namespace luacpp
|