Browse Source

Bug: stack overflow with nesting of coroutine.close

Roberto Ierusalimschy 2 years ago
parent
commit
1e64c1391f
6 changed files with 38 additions and 6 deletions
  1. 2 2
      lcorolib.c
  2. 2 1
      lstate.c
  3. 1 1
      ltests.c
  4. 1 1
      lua.h
  5. 6 1
      manual/manual.of
  6. 26 0
      testes/cstack.lua

+ 2 - 2
lcorolib.c

@@ -76,7 +76,7 @@ static int luaB_auxwrap (lua_State *L) {
   if (l_unlikely(r < 0)) {  /* error? */
   if (l_unlikely(r < 0)) {  /* error? */
     int stat = lua_status(co);
     int stat = lua_status(co);
     if (stat != LUA_OK && stat != LUA_YIELD) {  /* error in the coroutine? */
     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_assert(stat != LUA_OK);
       lua_xmove(co, L, 1);  /* move error message to the caller */
       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);
   int status = auxstatus(L, co);
   switch (status) {
   switch (status) {
     case COS_DEAD: case COS_YIELD: {
     case COS_DEAD: case COS_YIELD: {
-      status = lua_resetthread(co);
+      status = lua_resetthread(co, L);
       if (status == LUA_OK) {
       if (status == LUA_OK) {
         lua_pushboolean(L, 1);
         lua_pushboolean(L, 1);
         return 1;
         return 1;

+ 2 - 1
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;
   int status;
   lua_lock(L);
   lua_lock(L);
+  L->nCcalls = (from) ? getCcalls(from) : 0;
   status = luaE_resetthread(L, L->status);
   status = luaE_resetthread(L, L->status);
   lua_unlock(L);
   lua_unlock(L);
   return status;
   return status;

+ 1 - 1
ltests.c

@@ -1533,7 +1533,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
       lua_newthread(L1);
       lua_newthread(L1);
     }
     }
     else if EQ("resetthread") {
     else if EQ("resetthread") {
-      lua_pushinteger(L1, lua_resetthread(L1));
+      lua_pushinteger(L1, lua_resetthread(L1, L));
     }
     }
     else if EQ("newuserdata") {
     else if EQ("newuserdata") {
       lua_newuserdata(L1, getnum);
       lua_newuserdata(L1, getnum);

+ 1 - 1
lua.h

@@ -153,7 +153,7 @@ extern const char lua_ident[];
 LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
 LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
 LUA_API void       (lua_close) (lua_State *L);
 LUA_API void       (lua_close) (lua_State *L);
 LUA_API lua_State *(lua_newthread) (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);
 LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
 
 

+ 6 - 1
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,?,-}
 @apii{0,?,-}
 
 
 Resets a thread, cleaning its call stack and closing all pending
 Resets a thread, cleaning its call stack and closing all pending
@@ -4173,6 +4173,11 @@ or an error status otherwise.
 In case of error,
 In case of error,
 leaves the error object on the top of the stack.
 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,
 @APIEntry{int lua_resume (lua_State *L, lua_State *from, int nargs,

+ 26 - 0
testes/cstack.lua

@@ -84,6 +84,32 @@ do   -- bug in 5.4.0
 end
 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 <close> = 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
 do
   print("nesting of resuming yielded coroutines")
   print("nesting of resuming yielded coroutines")
   local count = 0
   local count = 0