From 3fe7be956f23385aa1950dc31e2f25127ccfc0ea Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 17 Mar 2025 16:14:17 -0300 Subject: Bug: message handler can be overwritten A __close metamethod can overwrite a message handler in the stack when closing a thread or a state. --- lstate.c | 3 +++ testes/coroutine.lua | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lstate.c b/lstate.c index 89c8b6ad..f3f2ccfd 100644 --- a/lstate.c +++ b/lstate.c @@ -272,7 +272,9 @@ static void close_state (lua_State *L) { luaC_freeallobjects(L); /* just collect its objects */ else { /* closing a fully built state */ L->ci = &L->base_ci; /* unwind CallInfo list */ + L->errfunc = 0; /* stack unwind can "throw away" the error function */ luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ + L->top.p = L->stack.p + 1; /* empty the stack to run finalizers */ luaC_freeallobjects(L); /* collect all objects */ luai_userstateclose(L); } @@ -328,6 +330,7 @@ int luaE_resetthread (lua_State *L, int status) { if (status == LUA_YIELD) status = LUA_OK; L->status = LUA_OK; /* so it can run __close metamethods */ + L->errfunc = 0; /* stack unwind can "throw away" the error function */ status = luaD_closeprotected(L, 1, status); if (status != LUA_OK) /* errors? */ luaD_seterrorobj(L, status, L->stack.p + 1); diff --git a/testes/coroutine.lua b/testes/coroutine.lua index e566c86e..03e04451 100644 --- a/testes/coroutine.lua +++ b/testes/coroutine.lua @@ -493,6 +493,25 @@ assert(not pcall(a, a)) a = nil +do + -- bug in 5.4: thread can use message handler higher in the stack + -- than the variable being closed + local c = coroutine.create(function() + local clo = setmetatable({}, {__close=function() + local x = 134 -- will overwrite message handler + error(x) + end}) + -- yields coroutine but leaves a new message handler for it, + -- that would be used when closing the coroutine (except that it + -- will be overwritten) + xpcall(coroutine.yield, function() return "XXX" end) + end) + + assert(coroutine.resume(c)) -- start coroutine + local st, msg = coroutine.close(c) + assert(not st and msg == 134) +end + -- access to locals of erroneous coroutines local x = coroutine.create (function () local a = 10 -- cgit v1.2.3-55-g6feb