Skip to content

Commit b996f8f

Browse files
committed
Bug: Issues with write barrier for __newindex
In 'luaV_finishset', there is an update on a table that is a field on another table. If the first table is the same as the one with the field (e.g., after 't.__newindex = t'), the update can change the value on that field (e.g., there may be a collision and the field is moved, or the field being updated is '__newindex' itself). After that, the barrier is called with the table stored in that field, which is not the correct table anymore.
1 parent bc4bbce commit b996f8f

2 files changed

Lines changed: 24 additions & 6 deletions

File tree

lvm.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -360,13 +360,19 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
360360
luaT_callTM(L, tm, t, key, val);
361361
return;
362362
}
363-
t = tm; /* else repeat assignment over 'tm' */
364-
luaV_fastset(t, key, val, hres, luaH_pset);
365-
if (hres == HOK) {
366-
luaV_finishfastset(L, t, val);
367-
return; /* done */
363+
t = tm; /* else must repeat assignment over 'tm' */
364+
/* do the equivalent to 'luaV_fastset', but saving 'h' */
365+
if (!ttistable(t))
366+
hres = HNOTATABLE;
367+
else {
368+
Table *h = hvalue(t); /* next call can change the value at 't' */
369+
hres = luaH_pset(h, key, val);
370+
if (hres == HOK) {
371+
luaC_barrierback(L, obj2gco(h), val); /* luaV_finishfastset */
372+
return; /* done */
373+
}
368374
}
369-
/* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */
375+
/* else 'return luaV_finishset(L, t, key, val, hres)' (loop) */
370376
}
371377
luaG_runerror(L, "'__newindex' chain too long; possible loop");
372378
}

testes/events.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,18 @@ do
390390
for i=1, 10 do t[i] = 1 end
391391
end
392392

393+
394+
do -- bug since 5.4
395+
local parent = {}
396+
parent.__newindex = parent
397+
collectgarbage()
398+
local child = setmetatable({}, parent)
399+
child.__newindex = {x = "hello"}
400+
collectgarbage("step")
401+
assert(parent.__newindex.x == "hello")
402+
end
403+
404+
393405
-- concat metamethod x numbers (bug in 5.1.1)
394406
c = {}
395407
local x

0 commit comments

Comments
 (0)