summaryrefslogtreecommitdiff
path: root/indra/llcommon/lua_function.h
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-08-20 21:12:25 -0400
committerNat Goodspeed <nat@lindenlab.com>2024-08-20 21:12:25 -0400
commit376c890c095fbc59b83402255cc1036c411150b9 (patch)
tree7c15346acb6121e5a3312ffe945f4a419f849307 /indra/llcommon/lua_function.h
parent68313aa2defcc7d6e5ebbd96344d78f3a31fdb9a (diff)
Fix for #2237: intermittent Lua data stack overflow.
Use a static unordered_map to allow a function receiving (lua_State* L) to look up the LuaState instance managing that lua_State. We've thought about this from time to time already. LuaState's constructor creates the map entry; its destructor removes it; the new static getParent(lua_State* L) method performs the lookup. Migrate lluau::set_interrupts_counter() and check_interrupts_counter() into LuaState member functions. Add a new mInterrupts counter for them. Importantly, LuaState::check_interrupts_counter(), which is indirectly called by a lua_callbacks().interrupt function, no longer performs any Lua stack operations. Empirically, it seems the Lua engine is capable of interrupting itself at a moment when re-entry confuses it. Change previous lluau::set_interrupts_counter(L, 0) calls to LuaState::getParent(L).set_interrupts_counter(0). Also add LuaStackDelta class, and a lua_checkdelta() helper macro, to verify that the Lua data stack depth on exit from a block differs from the depth on entry by exactly the expected amount. Sprinkle lua_checkdelta() macros in likely places.
Diffstat (limited to 'indra/llcommon/lua_function.h')
-rw-r--r--indra/llcommon/lua_function.h47
1 files changed, 42 insertions, 5 deletions
diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h
index c1a4d736a0..6965e206ab 100644
--- a/indra/llcommon/lua_function.h
+++ b/indra/llcommon/lua_function.h
@@ -60,13 +60,10 @@ namespace lluau
int loadstring(lua_State* L, const std::string& desc, const std::string& text);
fsyspath source_path(lua_State* L);
-
- void set_interrupts_counter(lua_State *L, S32 counter);
- void check_interrupts_counter(lua_State* L);
} // namespace lluau
-// must be a macro because __FUNCTION__ is context-sensitive
-#define lluau_checkstack(L, n) luaL_checkstack((L), (n), __FUNCTION__)
+// must be a macro because LL_PRETTY_FUNCTION is context-sensitive
+#define lluau_checkstack(L, n) luaL_checkstack((L), (n), LL_PRETTY_FUNCTION)
std::string lua_tostdstring(lua_State* L, int index);
void lua_pushstdstring(lua_State* L, const std::string& str);
@@ -110,10 +107,18 @@ public:
// Find or create LuaListener for passed lua_State.
static LuaListener& obtainListener(lua_State* L);
+ // Given lua_State* L, return the LuaState object managing (the main Lua
+ // thread for) L.
+ static LuaState& getParent(lua_State* L);
+
+ void set_interrupts_counter(S32 counter);
+ void check_interrupts_counter();
+
private:
script_finished_fn mCallback;
lua_State* mState;
std::string mError;
+ S32 mInterrupts{ 0 };
};
/*****************************************************************************
@@ -173,6 +178,32 @@ private:
};
/*****************************************************************************
+* LuaStackDelta
+*****************************************************************************/
+/**
+ * Instantiate LuaStackDelta in a block to compare the Lua data stack depth on
+ * entry (LuaStackDelta construction) and exit. Optionally, pass the expected
+ * depth increment. (But be aware that LuaStackDelta cannot observe the effect
+ * of a LuaPopper or LuaRemover declared previously in the same block.)
+ */
+class LuaStackDelta
+{
+public:
+ LuaStackDelta(lua_State* L, const std::string& where, int delta=0);
+ LuaStackDelta(const LuaStackDelta&) = delete;
+ LuaStackDelta& operator=(const LuaStackDelta&) = delete;
+
+ ~LuaStackDelta();
+
+private:
+ lua_State* L;
+ std::string mWhere;
+ int mDepth, mDelta;
+};
+
+#define lua_checkdelta(L, ...) LuaStackDelta delta(L, LL_PRETTY_FUNCTION, ##__VA_ARGS__)
+
+/*****************************************************************************
* lua_push() wrappers for generic code
*****************************************************************************/
inline
@@ -297,6 +328,7 @@ auto lua_to<void*>(lua_State* L, int index)
template <typename T>
auto lua_getfieldv(lua_State* L, int index, const char* k)
{
+ lua_checkdelta(L);
lluau_checkstack(L, 1);
lua_getfield(L, index, k);
LuaPopper pop(L, 1);
@@ -307,6 +339,7 @@ auto lua_getfieldv(lua_State* L, int index, const char* k)
template <typename T>
auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value)
{
+ lua_checkdelta(L);
lluau_checkstack(L, 1);
lua_push(L, value);
lua_setfield(L, index, k);
@@ -316,6 +349,7 @@ auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value)
template <typename T>
auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k)
{
+ lua_checkdelta(L);
lluau_checkstack(L, 1);
lua_pushlstring(L, k.data(), k.length());
lua_rawget(L, index);
@@ -327,6 +361,7 @@ auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k)
template <typename T>
void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T& value)
{
+ lua_checkdelta(L);
lluau_checkstack(L, 2);
lua_pushlstring(L, k.data(), k.length());
lua_push(L, value);
@@ -433,6 +468,7 @@ DistinctInt TypeTag<T>::value;
template <class T, typename... ARGS>
void lua_emplace(lua_State* L, ARGS&&... args)
{
+ lua_checkdelta(L, 1);
lluau_checkstack(L, 1);
int tag{ TypeTag<T>::value };
if (! lua_getuserdatadtor(L, tag))
@@ -467,6 +503,7 @@ void lua_emplace(lua_State* L, ARGS&&... args)
template <class T>
T* lua_toclass(lua_State* L, int index)
{
+ lua_checkdelta(L);
// get void* pointer to userdata (if that's what it is)
void* ptr{ lua_touserdatatagged(L, index, TypeTag<T>::value) };
// Derive the T* from ptr. If in future lua_emplace() must manually