diff --git a/README b/README index 191b7c7..36f9e65 100644 --- a/README +++ b/README @@ -88,6 +88,10 @@ directory from package.cpath). iconv.ERROR_INCOMPLETE An incomplete character was found in the input sequence. + iconv.ERROR_FINALIZED + Trying to use an already-finalized converter. This usually means + that the user was tweaking the garbage collector private methods. + iconv.ERROR_UNKNOWN There was an unknown error. diff --git a/luaiconv.c b/luaiconv.c index f54a621..e2e0697 100644 --- a/luaiconv.c +++ b/luaiconv.c @@ -53,6 +53,7 @@ #define ERROR_INVALID 2 #define ERROR_INCOMPLETE 3 #define ERROR_UNKNOWN 4 +#define ERROR_FINALIZED 5 @@ -66,9 +67,7 @@ static void push_iconv_t(lua_State *L, iconv_t cd) { static iconv_t get_iconv_t(lua_State *L, int i) { if (luaL_checkudata(L, i, ICONV_TYPENAME) != NULL) { iconv_t cd = UNBOXPTR(L, i); - if (cd == (iconv_t) NULL) - luaL_error(L, "attempt to use an invalid " ICONV_TYPENAME); - return cd; + return cd; /* May be NULL. This must be checked by the caller. */ } luaL_argerror(L, i, lua_pushfstring(L, ICONV_TYPENAME " expected, got %s", luaL_typename(L, i))); @@ -100,6 +99,12 @@ static int Liconv(lua_State *L) { size_t ret = -1; int hasone = 0; + if (cd == NULL) { + lua_pushstring(L, ""); + lua_pushnumber(L, ERROR_FINALIZED); + return 2; + } + outbuf = (char*) malloc(obsize * sizeof(char)); if (outbuf == NULL) { lua_pushstring(L, ""); @@ -182,8 +187,13 @@ static int Liconvlist(lua_State *L) { static int Liconv_close(lua_State *L) { iconv_t cd = get_iconv_t(L, 1); - if (iconv_close(cd) == 0) + if (cd != NULL && iconv_close(cd) == 0) { + /* Mark the pointer as freed, preventing interpreter crashes + if the user forces __gc to be called twice. */ + void **ptr = lua_touserdata(L, 1); + *ptr = NULL; lua_pushboolean(L, 1); /* ok */ + } else lua_pushnil(L); /* error */ return 1; @@ -207,6 +217,7 @@ int luaopen_iconv(lua_State *L) { TBL_SET_INT_CONST(L, ERROR_NO_MEMORY); TBL_SET_INT_CONST(L, ERROR_INVALID); TBL_SET_INT_CONST(L, ERROR_INCOMPLETE); + TBL_SET_INT_CONST(L, ERROR_FINALIZED); TBL_SET_INT_CONST(L, ERROR_UNKNOWN); lua_pushliteral(L, "VERSION"); diff --git a/test_iconv.lua b/test_iconv.lua index 972778f..a70b0d0 100644 --- a/test_iconv.lua +++ b/test_iconv.lua @@ -97,3 +97,14 @@ check_one(termcs, "utf8", utf8) check_one(termcs, "utf16", utf16) check_one(termcs, "EBCDIC-CP-ES", ebcdic) + +-- The library must never crash the interpreter, even if the user tweaks +-- with the garbage collector methods. +local cd = iconv.new("iso-8859-1", "utf-8") +local _, e = cd:iconv("atenção") +assert(e == nil, "Unexpected conversion error") +local gc = getmetatable(cd).__gc +gc(cd) +local _, e = cd:iconv("atenção") +assert(e == iconv.ERROR_FINALIZED, "Failed to detect double-freed objects") +gc(cd)