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.
This commit is contained in:
Roberto Ierusalimschy
2025-06-16 16:33:02 -03:00
parent 6e22fedb74
commit 1b0f943da7
2 changed files with 16 additions and 2 deletions

8
lgc.c
View File

@@ -553,8 +553,12 @@ static lu_mem traversetable (global_State *g, Table *h) {
traverseweakvalue(g, h);
else if (!weakvalue) /* strong values? */
traverseephemeron(g, h, 0);
else /* all weak */
linkgclist(h, g->allweak); /* nothing to traverse now */
else { /* all weak */
if (g->gcstate == GCSpropagate)
linkgclist(h, g->grayagain); /* must visit again its metatable */
else
linkgclist(h, g->allweak); /* must clear collected entries */
}
}
else /* not weak */
traversestrongtable(g, h);

View File

@@ -301,6 +301,16 @@ collectgarbage()
assert(next(a) == string.rep('$', 11))
if T then -- bug since 5.3: all-weak tables are not being revisited
T.gcstate("propagate")
local t = setmetatable({}, {__mode = "kv"})
T.gcstate("atomic") -- 't' was visited
setmetatable(t, {__mode = "kv"})
T.gcstate("pause") -- its new metatable is not being visited
assert(getmetatable(t).__mode == "kv")
end
-- 'bug' in 5.1
a = {}
local t = {x = 10}