diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-12-22 09:00:52 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-12-22 09:00:52 -0300 |
commit | 597a53bbc681089d85b082b46c2e2428dec43b86 (patch) | |
tree | b7dc3fc3f21b66bd8e9422e16b0fdde6d1ec6e01 | |
parent | 86ec152433baf8daf39f03a59c6842cbe33a179d (diff) | |
download | lua-github-597a53bbc681089d85b082b46c2e2428dec43b86.tar.gz |
Bug: finalizer calling exit can corrupt finalization order
'os.exit' can call lua_close again, separating new finalizers
created after all previous finalizers were already separated.
-rw-r--r-- | lgc.c | 10 | ||||
-rw-r--r-- | lgc.h | 1 | ||||
-rw-r--r-- | testes/main.lua | 28 |
3 files changed, 34 insertions, 5 deletions
@@ -907,7 +907,7 @@ static void GCTM (lua_State *L) { int status; lu_byte oldah = L->allowhook; int oldgcstp = g->gcstp; - g->gcstp = GCSTPGC; /* avoid GC steps */ + g->gcstp |= GCSTPGC; /* avoid GC steps */ L->allowhook = 0; /* stop debug hooks during GC metamethod */ setobj2s(L, L->top++, tm); /* push finalizer... */ setobj2s(L, L->top++, &v); /* ... and its argument */ @@ -1011,7 +1011,8 @@ static void correctpointers (global_State *g, GCObject *o) { void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { global_State *g = G(L); if (tofinalize(o) || /* obj. is already marked... */ - gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ + gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */ + (g->gcstp & GCSTPCLS)) /* or closing state? */ return; /* nothing to be done */ else { /* move 'o' to 'finobj' list */ GCObject **p; @@ -1502,14 +1503,13 @@ static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { */ void luaC_freeallobjects (lua_State *L) { global_State *g = G(L); - g->gcstp = GCSTPGC; + g->gcstp = GCSTPCLS; /* no extra finalizers after here */ luaC_changemode(L, KGC_INC); separatetobefnz(g, 1); /* separate all objects with finalizers */ lua_assert(g->finobj == NULL); - g->gcstp = 0; callallpendingfinalizers(L); deletelist(L, g->allgc, obj2gco(g->mainthread)); - deletelist(L, g->finobj, NULL); + lua_assert(g->finobj == NULL); /* no new finalizers */ deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ lua_assert(g->strt.nuse == 0); } @@ -154,6 +154,7 @@ */ #define GCSTPUSR 1 /* bit true when GC stopped by user */ #define GCSTPGC 2 /* bit true when GC stopped by itself */ +#define GCSTPCLS 4 /* bit true when closing Lua state */ #define gcrunning(g) ((g)->gcstp == 0) diff --git a/testes/main.lua b/testes/main.lua index 52c77954..9def6386 100644 --- a/testes/main.lua +++ b/testes/main.lua @@ -261,6 +261,34 @@ u2 = setmetatable({}, {__gc = function () error("ZYX") end}) RUN('lua -W %s 2> %s', prog, out) checkprogout("ZYX)\nXYZ)\n") +-- bug since 5.2: finalizer called when closing a state could +-- subvert finalization order +prepfile[[ +-- should be called last +print("creating 1") +setmetatable({}, {__gc = function () print(1) end}) + +print("creating 2") +setmetatable({}, {__gc = function () + print("2") + print("creating 3") + -- this finalizer should not be called, as object will be + -- created after 'lua_close' has been called + setmetatable({}, {__gc = function () print(3) end}) + print(collectgarbage()) -- cannot call collector here + os.exit(0, true) +end}) +]] +RUN('lua -W %s > %s', prog, out) +checkout[[ +creating 1 +creating 2 +2 +creating 3 +nil +1 +]] + -- test many arguments prepfile[[print(({...})[30])]] |