[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: LuaRC userdata stack corruption
- From: "Powers, Drew" <dpowers@...>
- Date: Sun, 19 Sep 2004 04:28:50 -0700
Hello,
I apologize if this is the wrong place to post this and I hope the
formatting
is more readable than my last post :/
I have found another issue with the latest version of LuaRC
(from Subversion as of 9/15/04). Essentially, the collection of
userdata
with __gc metatables can corrupt existing stack elements.
If you have a userdata with __gc metatable at the stack top (L->top)
with a
RC of 1, then push anything on the stack LuaRC asserts. The following
happens
in the process of replacing the userdata with a string from
lua_pushstring:
* userdata info is copied into a temp variable via lua_makeobjectbackup
* string object is created and placed at stack top (L->top) and its RC
incremented.
* userdata RC (through temp var) is decremented because string is in
the
process of replacing it.
* lua_releaseobject (a LuaRC call) is called on userdata (through temp
var)
because RC = 0.
* lua_releaseobject calls do1gcTM which checks if the userdata contains
a
metatable (which it does).
* the metatable is placed at the stack top (replacing the string we
just
put there <--- BAD) and userdata goes at top+1.
* the stack top is adjusted by +2 and the metatable __gc is called
popping
the arg and function off and setting stack top back -2.
* the type of the object at stack top is LUA_TNIL (due to cleanup), but
the
gc still points to the __gc function object.
* we resume with the rest of the string move and assert because the gc
type
is not LUA_TSTRING.
My quick and dirty hack increments L->top before calling do1gcTM in
lua_releaseobject, then decrementing it afterwards which seems to
prevent
the corruption but it'll break the first time something puts two or more
elements there.
Here's a repro:
int my_gc( lua_State* L )
{
printf("my_gc\n");
return 0;
}
int main()
{
lua_State* L = lua_open();
// create a userdata
void* buf = lua_newuserdata(L, 4);
*(unsigned int*)buf = 0x12345678;
// create a table to become the metatable
lua_newtable(L);
// add a gc function
lua_pushstring(L, "__gc");
lua_pushcclosure(L, my_gc, 0);
lua_settable(L, -3);
// add the table as a metatable
lua_setmetatable(L, -2);
// put a copy of the userdata at L->top
lua_insert(L, -1);
// pop the original userdata off L->top-1 to get the RC of the one
// at L->top down to 1 (priming the condition my app is
experiencing)
lua_pop(L,1);
// push something on stack to get the userdata back to L->top
lua_pushnumber(L,1);
// now push anything on the stack and it will assert.
lua_pushstring( L, "test" );
lua_close(L);
}
Thanks,
Drew Powers