Skip to content

Commit 1b0f943

Browse files
committed
Bug: new metatable in weak table can fool the GC
All-weak tables are not being revisited after being visited during propagation; if it gets a new metatable after that, the new metatable may not be marked.
1 parent 6e22fed commit 1b0f943

File tree

2 files changed

+16
-2
lines changed

2 files changed

+16
-2
lines changed

lgc.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -553,8 +553,12 @@ static lu_mem traversetable (global_State *g, Table *h) {
553553
traverseweakvalue(g, h);
554554
else if (!weakvalue) /* strong values? */
555555
traverseephemeron(g, h, 0);
556-
else /* all weak */
557-
linkgclist(h, g->allweak); /* nothing to traverse now */
556+
else { /* all weak */
557+
if (g->gcstate == GCSpropagate)
558+
linkgclist(h, g->grayagain); /* must visit again its metatable */
559+
else
560+
linkgclist(h, g->allweak); /* must clear collected entries */
561+
}
558562
}
559563
else /* not weak */
560564
traversestrongtable(g, h);

testes/gc.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,16 @@ collectgarbage()
301301
assert(next(a) == string.rep('$', 11))
302302

303303

304+
if T then -- bug since 5.3: all-weak tables are not being revisited
305+
T.gcstate("propagate")
306+
local t = setmetatable({}, {__mode = "kv"})
307+
T.gcstate("atomic") -- 't' was visited
308+
setmetatable(t, {__mode = "kv"})
309+
T.gcstate("pause") -- its new metatable is not being visited
310+
assert(getmetatable(t).__mode == "kv")
311+
end
312+
313+
304314
-- 'bug' in 5.1
305315
a = {}
306316
local t = {x = 10}

0 commit comments

Comments
 (0)