From 22974326ca0d4f893849ce722cc1d65b3e228f42 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 13 Mar 2025 15:30:52 -0300 Subject: Use after free in 'luaV_finishset' If a metatable is a weak table, its __newindex field could be collected by an emergency collection while being used in 'luaV_finishset'. (This bug has similarities with bug 5.3.2-1, fixed in commit a272fa66.) --- lapi.c | 5 +++++ lvm.c | 8 ++++++++ testes/events.lua | 13 ++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lapi.c b/lapi.c index a5e94507..eab12cac 100644 --- a/lapi.c +++ b/lapi.c @@ -681,6 +681,11 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) { } +/* +** The following function assumes that the registry cannot be a weak +** table, so that en mergency collection while using the global table +** cannot collect it. +*/ static void getGlobalTable (lua_State *L, TValue *gt) { Table *registry = hvalue(&G(L)->l_registry); lu_byte tag = luaH_getint(registry, LUA_RIDX_GLOBALS, gt); diff --git a/lvm.c b/lvm.c index f0e73f9b..af048d81 100644 --- a/lvm.c +++ b/lvm.c @@ -325,6 +325,11 @@ lu_byte luaV_finishget (lua_State *L, const TValue *t, TValue *key, /* ** Finish a table assignment 't[key] = val'. +** About anchoring the table before the call to 'luaH_finishset': +** This call may trigger an emergency collection. When loop>0, +** the table being acessed is a field in some metatable. If this +** metatable is weak and the table is not anchored, this collection +** could collect that table while it is being updated. */ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, TValue *val, int hres) { @@ -335,7 +340,10 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, Table *h = hvalue(t); /* save 't' table */ tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ if (tm == NULL) { /* no metamethod? */ + sethvalue2s(L, L->top.p, h); /* anchor 't' */ + L->top.p++; /* assume EXTRA_STACK */ luaH_finishset(L, h, key, val, hres); /* set new value */ + L->top.p--; invalidateTMcache(h); luaC_barrierback(L, obj2gco(h), val); return; diff --git a/testes/events.lua b/testes/events.lua index 2500fbd5..7e434b1f 100644 --- a/testes/events.lua +++ b/testes/events.lua @@ -379,6 +379,17 @@ x = 0 .."a".."b"..c..d.."e".."f".."g" assert(x.val == "0abcdefg") +do + -- bug since 5.4.1 (test needs T) + local mt = setmetatable({__newindex={}}, {__mode='v'}) + local t = setmetatable({}, mt) + + if T then T.allocfailnext() end + + -- seg. fault + for i=1, 10 do t[i] = 1 end +end + -- concat metamethod x numbers (bug in 5.1.1) c = {} local x @@ -481,7 +492,7 @@ assert(not pcall(function (a,b) return a[b] end, a, 10)) assert(not pcall(function (a,b,c) a[b] = c end, a, 10, true)) -- bug in 5.1 -T, K, V = nil +local T, K, V = nil grandparent = {} grandparent.__newindex = function(t,k,v) T=t; K=k; V=v end -- cgit v1.2.3-55-g6feb