New metatable in an all-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:29:32 -03:00
parent 8cd7ae7da0
commit 9386e49a31
2 changed files with 15 additions and 2 deletions

7
lgc.c
View File

@@ -617,8 +617,11 @@ static l_mem traversetable (global_State *g, Table *h) {
case 2: /* weak keys */
traverseephemeron(g, h, 0);
break;
case 3: /* all weak */
linkgclist(h, g->allweak); /* nothing to traverse now */
case 3: /* all weak; nothing to traverse */
if (g->gcstate == GCSpropagate)
linkgclist(h, g->grayagain); /* must visit again its metatable */
else
linkgclist(h, g->allweak); /* must clear collected entries */
break;
}
return 1 + 2*sizenode(h) + h->asize;

View File

@@ -294,6 +294,16 @@ do -- invalid mode
end
if T then -- bug since 5.3: all-weak tables are not being revisited
T.gcstate("propagate")
local t = setmetatable({}, {__mode = "kv"})
T.gcstate("enteratomic") -- '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}