lua-users home
lua-l archive

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


On Tue, Oct 16, 2012 at 5:55 AM, Roberto Ierusalimschy
<roberto@inf.puc-rio.br> wrote:
> So, in that case, as only the iterator function can access the object,
> and the object cannot be collected (and finalized) while the iterator
> function exists, there is no possibility of an access after the object
> has been finalized.

While that holds in this case, I can modify my test program to trigger
the call on the gc'd object even when the userdata is not exposed to Lua
(see attached).  My example differs from the PIL example only in the
order of object creation.

The reference manual does state that __gc methods are called in reverse
order of creation, so you could say that the C code must take this into
account and only have non-guarded access to userdata that were created
before the associated closure.

Also, in the other example from the book, it appears that lxp_parse()
accesses xpu->parser without a guard, which comes from a userdata that
is exposed to Lua.  Actually in this case it appears you could trigger
a crash just by calling close() explicitly and then parse()?  Maybe I
am missing something here.  http://www.lua.org/pil/29.2.html

Anyway, I wanted to mention it since it caught me off-guard.  That a
userdata could be accessible to C and Lua after being finalized surprised
me.

Josh

--

#include "lauxlib.h"
#include <stdio.h>

#if LUA_VERSION_NUM == 501
#define setfuncs(L, l) luaL_register(L, NULL, l)
#define luaL_setmetatable(L, name) \
  luaL_getmetatable(L, name); \
  lua_setmetatable(L, -2)
#elif LUA_VERSION_NUM == 502
#define setfuncs(L, l) luaL_setfuncs(L, l, 0)
#endif

static const char *mytype = "mytype";

static int newobj(lua_State *L) {
  void *ud = lua_newuserdata(L, 0);
  printf("new userdata=%p\n", ud);
  luaL_setmetatable(L, mytype);
  return 1;
}

static int call(lua_State *L) {
  int upvalue = lua_upvalueindex(1);
  if (!lua_touserdata(L, upvalue)) {
    // We need to defer creation of the userdata to trigger the bad
    // finalization order.
    newobj(L);
    lua_replace(L, upvalue);
  } else {
    printf("call userdata=%p\n", luaL_checkudata(L, upvalue, mytype));
  }
  return 0;
}

static int gc(lua_State *L) {
  printf("__gc userdata=%p\n", luaL_checkudata(L, 1, mytype));
  return 0;
}

static const struct luaL_Reg mm[] = {
  {"__gc", gc},
  {NULL, NULL}
};

int luaopen_ext(lua_State *L) {
  luaL_newmetatable(L, mytype);
  setfuncs(L, mm);
  lua_pushnil(L);
  lua_pushcclosure(L, &call, 1);
  return 1;
}

--

local ext = require "ext"

if _VERSION >= 'Lua 5.2' then
  function defer(fn)
    setmetatable({}, { __gc = fn })
  end
else
  function defer(fn)
    getmetatable(newproxy(true)).__gc = fn
  end
end

defer(ext)
ext()

--

Output:

new userdata=0x6356a8
__gc userdata=0x6356a8
call userdata=0x6356a8