lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


context:
1. load, pcall, and xpcall are protected calls; they do not throw
2. only xpcall allows a user defined errfunc (error msg handler)
3. load can take a "reader function" which must return a string
4. load will not throw, even if the reader function "misbehaves"

There are two types of reader function misbehavior
1. the reader function returns anything that is neither a string nor nil
2. the reader function throws an exception

bug:
load will use xpcall's user defined error handler when its reader function misbehaves. pcall does not do this

repro
xpcall(
  function()
    load(function()
      return true
    end)
  end,
  function()
    print("insider my user defined error handler", debug.traceback(msg))
  end
)
print("normal exit")

expected output:
normal exit

actual output:
insider my user defined error handler	stack traceback:
	./test.lua:8: in function <./test.lua:7>
	[C]: in function 'load'
	./test.lua:3: in function <./test.lua:2>
	[C]: in function 'xpcall'
	./test.lua:1: in main chunk
	[C]: in ?
normal exit

The reader function can cause the same behavior if it throws an exception.

Compare to pcall:
pcall does not exhibit this same behavior. You can throw an exception in pcall, and xpcall's errfunc is NOT used

e.g.
xpcall(
  function()
    pcall(function()
      error()
    end)
  end,
  function()
    print("insider my user defined error handler", debug.traceback(msg))
  end
)
print("normal exit")

output:
normal exit


Proposed Solution:
Set the errfunc to 0 before calling the unprotected. This is exactly what pcall does
pcall: https://github.com/lua/lua/blob/master/lbaselib.c#L455
The fourth param is the errfunc, here it is explicitly set to 0

for load, we could:

diff --git a/ldo.c b/ldo.c
index 64fe2915..16a222f7 100644
--- a/ldo.c
+++ b/ldo.c
@@ -810,7 +810,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
   p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
   p.dyd.label.arr = NULL; p.dyd.label.size = 0;
   luaZ_initbuffer(L, &p.buff);
-  status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
+  status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), 0);
   luaZ_freebuffer(L, &p.buff);
   luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
   luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);