lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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