浏览代码

Bug: message handler can be overwritten

A __close metamethod can overwrite a message handler in the stack
when closing a thread or a state.
Roberto Ierusalimschy 4 月之前
父节点
当前提交
3fe7be956f
共有 2 个文件被更改,包括 22 次插入0 次删除
  1. 3 0
      lstate.c
  2. 19 0
      testes/coroutine.lua

+ 3 - 0
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);

+ 19 - 0
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 <close> = 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