Visitar URL original
Bug: stack overflow with nesting of coroutine.close · lua/lua@1e64c13 · GitHub
Skip to content

Commit 1e64c13

Browse files
committed
Bug: stack overflow with nesting of coroutine.close
1 parent b85816b commit 1e64c13

6 files changed

Lines changed: 38 additions & 6 deletions

File tree

‎lcorolib.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ static int luaB_auxwrap (lua_State *L) {
7676
if (l_unlikely(r < 0)) { /* error? */
7777
int stat = lua_status(co);
7878
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
79-
stat = lua_resetthread(co); /* close its tbc variables */
79+
stat = lua_resetthread(co, L); /* close its tbc variables */
8080
lua_assert(stat != LUA_OK);
8181
lua_xmove(co, L, 1); /* move error message to the caller */
8282
}
@@ -172,7 +172,7 @@ static int luaB_close (lua_State *L) {
172172
int status = auxstatus(L, co);
173173
switch (status) {
174174
case COS_DEAD: case COS_YIELD: {
175-
status = lua_resetthread(co);
175+
status = lua_resetthread(co, L);
176176
if (status == LUA_OK) {
177177
lua_pushboolean(L, 1);
178178
return 1;

‎lstate.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,10 @@ int luaE_resetthread (lua_State *L, int status) {
343343
}
344344

345345

346-
LUA_API int lua_resetthread (lua_State *L) {
346+
LUA_API int lua_resetthread (lua_State *L, lua_State *from) {
347347
int status;
348348
lua_lock(L);
349+
L->nCcalls = (from) ? getCcalls(from) : 0;
349350
status = luaE_resetthread(L, L->status);
350351
lua_unlock(L);
351352
return status;

‎ltests.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1533,7 +1533,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
15331533
lua_newthread(L1);
15341534
}
15351535
else if EQ("resetthread") {
1536-
lua_pushinteger(L1, lua_resetthread(L1));
1536+
lua_pushinteger(L1, lua_resetthread(L1, L));
15371537
}
15381538
else if EQ("newuserdata") {
15391539
lua_newuserdata(L1, getnum);

‎lua.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ extern const char lua_ident[];
153153
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
154154
LUA_API void (lua_close) (lua_State *L);
155155
LUA_API lua_State *(lua_newthread) (lua_State *L);
156-
LUA_API int (lua_resetthread) (lua_State *L);
156+
LUA_API int (lua_resetthread) (lua_State *L, lua_State *from);
157157

158158
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
159159

‎manual/manual.of‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4160,7 +4160,7 @@ and then pops the top element.
41604160

41614161
}
41624162

4163-
@APIEntry{int lua_resetthread (lua_State *L);|
4163+
@APIEntry{int lua_resetthread (lua_State *L, lua_State *from);|
41644164
@apii{0,?,-}
41654165

41664166
Resets a thread, cleaning its call stack and closing all pending
@@ -4173,6 +4173,11 @@ or an error status otherwise.
41734173
In case of error,
41744174
leaves the error object on the top of the stack.
41754175

4176+
The parameter @id{from} represents the coroutine that is resetting @id{L}.
4177+
If there is no such coroutine,
4178+
this parameter can be @id{NULL}.
4179+
(This parameter was introduced in @N{release 5.4.5}.)
4180+
41764181
}
41774182

41784183
@APIEntry{int lua_resume (lua_State *L, lua_State *from, int nargs,

‎testes/cstack.lua‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,32 @@ do -- bug in 5.4.0
8484
end
8585

8686

87+
do -- bug since 5.4.0
88+
local count = 0
89+
print("chain of 'coroutine.close'")
90+
-- create N coroutines forming a list so that each one, when closed,
91+
-- closes the previous one. (With a large enough N, previous Lua
92+
-- versions crash in this test.)
93+
local coro = false
94+
for i = 1, 1000 do
95+
local previous = coro
96+
coro = coroutine.create(function()
97+
local cc <close> = setmetatable({}, {__close=function()
98+
count = count + 1
99+
if previous then
100+
assert(coroutine.close(previous))
101+
end
102+
end})
103+
coroutine.yield() -- leaves 'cc' pending to be closed
104+
end)
105+
assert(coroutine.resume(coro)) -- start it and run until it yields
106+
end
107+
local st, msg = coroutine.close(coro)
108+
assert(not st and string.find(msg, "C stack overflow"))
109+
print("final count: ", count)
110+
end
111+
112+
87113
do
88114
print("nesting of resuming yielded coroutines")
89115
local count = 0

0 commit comments

Comments
 (0)