diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2022-10-25 16:44:06 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2022-10-25 16:44:06 -0300 |
commit | 1e64c1391f9a14115b5cc82066dbf545ae73ee27 (patch) | |
tree | 4aa3b6c2854c920ed825bf9fe46d275826e5ab6e | |
parent | b85816b9a884cbe4cfd139a8e11ffc28ecead576 (diff) | |
download | lua-1e64c1391f9a14115b5cc82066dbf545ae73ee27.tar.gz lua-1e64c1391f9a14115b5cc82066dbf545ae73ee27.tar.bz2 lua-1e64c1391f9a14115b5cc82066dbf545ae73ee27.zip |
Bug: stack overflow with nesting of coroutine.close
-rw-r--r-- | lcorolib.c | 4 | ||||
-rw-r--r-- | lstate.c | 3 | ||||
-rw-r--r-- | ltests.c | 2 | ||||
-rw-r--r-- | lua.h | 2 | ||||
-rw-r--r-- | manual/manual.of | 7 | ||||
-rw-r--r-- | testes/cstack.lua | 26 |
6 files changed, 38 insertions, 6 deletions
@@ -76,7 +76,7 @@ static int luaB_auxwrap (lua_State *L) { | |||
76 | if (l_unlikely(r < 0)) { /* error? */ | 76 | if (l_unlikely(r < 0)) { /* error? */ |
77 | int stat = lua_status(co); | 77 | int stat = lua_status(co); |
78 | if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ | 78 | 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 */ |
80 | lua_assert(stat != LUA_OK); | 80 | lua_assert(stat != LUA_OK); |
81 | lua_xmove(co, L, 1); /* move error message to the caller */ | 81 | lua_xmove(co, L, 1); /* move error message to the caller */ |
82 | } | 82 | } |
@@ -172,7 +172,7 @@ static int luaB_close (lua_State *L) { | |||
172 | int status = auxstatus(L, co); | 172 | int status = auxstatus(L, co); |
173 | switch (status) { | 173 | switch (status) { |
174 | case COS_DEAD: case COS_YIELD: { | 174 | case COS_DEAD: case COS_YIELD: { |
175 | status = lua_resetthread(co); | 175 | status = lua_resetthread(co, L); |
176 | if (status == LUA_OK) { | 176 | if (status == LUA_OK) { |
177 | lua_pushboolean(L, 1); | 177 | lua_pushboolean(L, 1); |
178 | return 1; | 178 | return 1; |
@@ -343,9 +343,10 @@ int luaE_resetthread (lua_State *L, int status) { | |||
343 | } | 343 | } |
344 | 344 | ||
345 | 345 | ||
346 | LUA_API int lua_resetthread (lua_State *L) { | 346 | LUA_API int lua_resetthread (lua_State *L, lua_State *from) { |
347 | int status; | 347 | int status; |
348 | lua_lock(L); | 348 | lua_lock(L); |
349 | L->nCcalls = (from) ? getCcalls(from) : 0; | ||
349 | status = luaE_resetthread(L, L->status); | 350 | status = luaE_resetthread(L, L->status); |
350 | lua_unlock(L); | 351 | lua_unlock(L); |
351 | return status; | 352 | return status; |
@@ -1533,7 +1533,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { | |||
1533 | lua_newthread(L1); | 1533 | lua_newthread(L1); |
1534 | } | 1534 | } |
1535 | else if EQ("resetthread") { | 1535 | else if EQ("resetthread") { |
1536 | lua_pushinteger(L1, lua_resetthread(L1)); | 1536 | lua_pushinteger(L1, lua_resetthread(L1, L)); |
1537 | } | 1537 | } |
1538 | else if EQ("newuserdata") { | 1538 | else if EQ("newuserdata") { |
1539 | lua_newuserdata(L1, getnum); | 1539 | lua_newuserdata(L1, getnum); |
@@ -153,7 +153,7 @@ extern const char lua_ident[]; | |||
153 | LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); | 153 | LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); |
154 | LUA_API void (lua_close) (lua_State *L); | 154 | LUA_API void (lua_close) (lua_State *L); |
155 | LUA_API lua_State *(lua_newthread) (lua_State *L); | 155 | 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); |
157 | 157 | ||
158 | LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); | 158 | LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); |
159 | 159 | ||
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. | |||
4160 | 4160 | ||
4161 | } | 4161 | } |
4162 | 4162 | ||
4163 | @APIEntry{int lua_resetthread (lua_State *L);| | 4163 | @APIEntry{int lua_resetthread (lua_State *L, lua_State *from);| |
4164 | @apii{0,?,-} | 4164 | @apii{0,?,-} |
4165 | 4165 | ||
4166 | Resets a thread, cleaning its call stack and closing all pending | 4166 | Resets a thread, cleaning its call stack and closing all pending |
@@ -4173,6 +4173,11 @@ or an error status otherwise. | |||
4173 | In case of error, | 4173 | In case of error, |
4174 | leaves the error object on the top of the stack. | 4174 | leaves the error object on the top of the stack. |
4175 | 4175 | ||
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 | |||
4176 | } | 4181 | } |
4177 | 4182 | ||
4178 | @APIEntry{int lua_resume (lua_State *L, lua_State *from, int nargs, | 4183 | @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 | |||
84 | end | 84 | end |
85 | 85 | ||
86 | 86 | ||
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 | |||
87 | do | 113 | do |
88 | print("nesting of resuming yielded coroutines") | 114 | print("nesting of resuming yielded coroutines") |
89 | local count = 0 | 115 | local count = 0 |