LuaSandbox memory limits should limit the amount of memory actually in use.
@Surjection proposes running a full GC from the allocation hook as a last resort prior to reporting an out-of-memory error.
Note that it's only safe to run the GC from the allocation hook if the reference tree is in a well-defined state at that time, i.e. contains no dangling pointers or the like. After reviewing the code for a while, I'm about 80% convinced. It would be safer if we zero-filled newly-allocated memory.
An example of the kind of thing I'm looking for:
void luaS_resize (lua_State *L, int newsize) { ... if (G(L)->gcstate == GCSsweepstring) return; /* cannot resize during GC traverse */
The converse is also true — you can't GC during a resize of the global string table. But it doesn't allocate memory during the critical section.
We don't allow users to catch a memory allocation failure with pcall(), although that would be possible in base Lua. So there is a motive for upstream to ensure that the global state is consistent when allocation is done.
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { ... if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { /* memory allocation error: free partial state */ close_state(L); L = NULL; }
This is a concerning form of protection, and it indicates that it's unsafe for us to do a GC during lua_newstate(). This needs special handling since the custom allocator is active at this point.
There are three other calls to luaD_rawrunprotected(), which all look fine.
The libraries should be fine. The lexer looks fine. A problem would look like:
- Write garbage to global state or a GC object
- Allocate memory
- Fix the garbage
Maybe we have that in lua_newstate(). It's hard to be sure since an actual crash would depend on one of about 50 properties not being initialised in the lua_State. Zero-filling would make it much harder to turn such an issue into code execution.