From 1e64c1391f9a14115b5cc82066dbf545ae73ee27 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 25 Oct 2022 16:44:06 -0300 Subject: Bug: stack overflow with nesting of coroutine.close --- lcorolib.c | 4 ++-- lstate.c | 3 ++- ltests.c | 2 +- lua.h | 2 +- manual/manual.of | 7 ++++++- testes/cstack.lua | 26 ++++++++++++++++++++++++++ 6 files changed, 38 insertions(+), 6 deletions(-) diff --git a/lcorolib.c b/lcorolib.c index 785a1e81..40b880b1 100644 --- a/lcorolib.c +++ b/lcorolib.c @@ -76,7 +76,7 @@ static int luaB_auxwrap (lua_State *L) { if (l_unlikely(r < 0)) { /* error? */ int stat = lua_status(co); if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ - stat = lua_resetthread(co); /* close its tbc variables */ + stat = lua_resetthread(co, L); /* close its tbc variables */ lua_assert(stat != LUA_OK); lua_xmove(co, L, 1); /* move error message to the caller */ } @@ -172,7 +172,7 @@ static int luaB_close (lua_State *L) { int status = auxstatus(L, co); switch (status) { case COS_DEAD: case COS_YIELD: { - status = lua_resetthread(co); + status = lua_resetthread(co, L); if (status == LUA_OK) { lua_pushboolean(L, 1); return 1; diff --git a/lstate.c b/lstate.c index 1ffe1a0f..4b5c1000 100644 --- a/lstate.c +++ b/lstate.c @@ -343,9 +343,10 @@ int luaE_resetthread (lua_State *L, int status) { } -LUA_API int lua_resetthread (lua_State *L) { +LUA_API int lua_resetthread (lua_State *L, lua_State *from) { int status; lua_lock(L); + L->nCcalls = (from) ? getCcalls(from) : 0; status = luaE_resetthread(L, L->status); lua_unlock(L); return status; diff --git a/ltests.c b/ltests.c index 9a887f98..734a96da 100644 --- a/ltests.c +++ b/ltests.c @@ -1533,7 +1533,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { lua_newthread(L1); } else if EQ("resetthread") { - lua_pushinteger(L1, lua_resetthread(L1)); + lua_pushinteger(L1, lua_resetthread(L1, L)); } else if EQ("newuserdata") { lua_newuserdata(L1, getnum); diff --git a/lua.h b/lua.h index 219784cc..bfba4d1e 100644 --- a/lua.h +++ b/lua.h @@ -153,7 +153,7 @@ extern const char lua_ident[]; LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); LUA_API void (lua_close) (lua_State *L); LUA_API lua_State *(lua_newthread) (lua_State *L); -LUA_API int (lua_resetthread) (lua_State *L); +LUA_API int (lua_resetthread) (lua_State *L, lua_State *from); LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); diff --git a/manual/manual.of b/manual/manual.of index 10c16bd1..6d19e251 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -4160,7 +4160,7 @@ and then pops the top element. } -@APIEntry{int lua_resetthread (lua_State *L);| +@APIEntry{int lua_resetthread (lua_State *L, lua_State *from);| @apii{0,?,-} Resets a thread, cleaning its call stack and closing all pending @@ -4173,6 +4173,11 @@ or an error status otherwise. In case of error, leaves the error object on the top of the stack. +The parameter @id{from} represents the coroutine that is resetting @id{L}. +If there is no such coroutine, +this parameter can be @id{NULL}. +(This parameter was introduced in @N{release 5.4.5}.) + } @APIEntry{int lua_resume (lua_State *L, lua_State *from, int nargs, diff --git a/testes/cstack.lua b/testes/cstack.lua index ca76c872..97afe9fd 100644 --- a/testes/cstack.lua +++ b/testes/cstack.lua @@ -84,6 +84,32 @@ do -- bug in 5.4.0 end +do -- bug since 5.4.0 + local count = 0 + print("chain of 'coroutine.close'") + -- create N coroutines forming a list so that each one, when closed, + -- closes the previous one. (With a large enough N, previous Lua + -- versions crash in this test.) + local coro = false + for i = 1, 1000 do + local previous = coro + coro = coroutine.create(function() + local cc = setmetatable({}, {__close=function() + count = count + 1 + if previous then + assert(coroutine.close(previous)) + end + end}) + coroutine.yield() -- leaves 'cc' pending to be closed + end) + assert(coroutine.resume(coro)) -- start it and run until it yields + end + local st, msg = coroutine.close(coro) + assert(not st and string.find(msg, "C stack overflow")) + print("final count: ", count) +end + + do print("nesting of resuming yielded coroutines") local count = 0 -- cgit v1.2.1