lua-users home
lua-l archive

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

Hi, I found an interesting SEGV crash on Lua interpreter.


Lua .5.4.4, commit hash ad3942adba574c9d008c99ce2785a5af19d146bf

Ubuntu 20.04.3 LTS

glibc 2.31




local function v(a, b, c, ...)

  return os.exit(0, true)



local function a()

  return h()



local e <close> = setmetatable({}, {__close = a})






How to reproduce:



./lua crash.lua




ASAN log:



==25870==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x560c2ac6bbc1 bp 0x606000000e60 sp 0x7ffd4f445030 T0)

==25870==The signal is caused by a READ memory access.

==25870==Hint: address points to the zero page.

    #0 0x560c2ac6bbc0 in funcnamefromcode (/home/lua/lua+0x22bc0)

    #1 0x560c2ac6d672 in lua_getinfo (/home/lua/lua+0x24672)

    #2 0x560c2acadbb1 in luaL_traceback (/home/lua/lua+0x64bb1)

    #3 0x560c2ac61c4b in msghandler (/home/lua/lua+0x18c4b)

    #4 0x560c2ac725c7 in luaD_precall (/home/lua/lua+0x295c7)

    #5 0x560c2ac731fc in luaD_callnoyield (/home/lua/lua+0x2a1fc)

    #6 0x560c2ac6e3fd in luaG_errormsg (/home/lua/lua+0x253fd)

    #7 0x560c2ac6e5f1 in luaG_runerror (/home/lua/lua+0x255f1)

    #8 0x560c2ac6e7fe in luaG_callerror (/home/lua/lua+0x257fe)

    #9 0x560c2ac70f4f in luaD_tryfuncTM (/home/lua/lua+0x27f4f)

    #10 0x560c2ac719c1 in luaD_pretailcall (/home/lua/lua+0x289c1)

    #11 0x560c2ac9e167 in luaV_execute (/home/lua/lua+0x55167)

    #12 0x560c2ac73237 in luaD_callnoyield (/home/lua/lua+0x2a237)

    #13 0x560c2ac76aec in luaF_close (/home/lua/lua+0x2daec)

    #14 0x560c2ac6fa37 in luaD_rawrunprotected (/home/lua/lua+0x26a37)

    #15 0x560c2ac73bb7 in luaD_closeprotected (/home/lua/lua+0x2abb7)

    #16 0x560c2ac8f623 in close_state (/home/lua/lua+0x46623)

    #17 0x560c2acc26f7 in os_exit (/home/lua/lua+0x796f7)

    #18 0x560c2ac71b5b in luaD_pretailcall (/home/lua/lua+0x28b5b)

    #19 0x560c2ac9e167 in luaV_execute (/home/lua/lua+0x55167)

    #20 0x560c2ac73237 in luaD_callnoyield (/home/lua/lua+0x2a237)

    #21 0x560c2ac6fa37 in luaD_rawrunprotected (/home/lua/lua+0x26a37)

    #22 0x560c2ac73d93 in luaD_pcall (/home/lua/lua+0x2ad93)

    #23 0x560c2ac68ba3 in lua_pcallk (/home/lua/lua+0x1fba3)

    #24 0x560c2ac61e9e in docall (/home/lua/lua+0x18e9e)

    #25 0x560c2ac6316d in pmain (/home/lua/lua+0x1a16d)

    #26 0x560c2ac725c7 in luaD_precall (/home/lua/lua+0x295c7)

    #27 0x560c2ac731fc in luaD_callnoyield (/home/lua/lua+0x2a1fc)

    #28 0x560c2ac6fa37 in luaD_rawrunprotected (/home/lua/lua+0x26a37)

    #29 0x560c2ac73d93 in luaD_pcall (/home/lua/lua+0x2ad93)

    #30 0x560c2ac68ba3 in lua_pcallk (/home/lua/lua+0x1fba3)

    #31 0x560c2ac613ae in main (/home/lua/lua+0x183ae)

    #32 0x7f2a1883d0b2 in __libc_start_main (/lib/x86_64-linux-gnu/

    #33 0x560c2ac61a6d in _start (/home/lua/lua+0x18a6d)


AddressSanitizer can not provide additional info.

SUMMARY: AddressSanitizer: SEGV (/home/lua/lua+0x22bc0) in funcnamefromcode




Root cause:



The main reason is that L->ci->previous->func value is located above L->top in the luaD_seterrorobj function.


While the os.exit function call the __close metamethod of tbc(to-be-closed), the upper callinfo frame calls the lower callinfo frame. At this time, if the function is executed normally. there is no problem, but when an error occurs (argument error or nil call, etc.), the corresponding crash occurs.


To explain in more detail, a crash occurs in retrieving the name of the function that called the function in which the error occurred during the error handling process. In the luaD_seterrorobj function, L->top is set to the tbc+2 value obtained in the luaF_close function. At this time, if you look at the value, the address of the L->ci->previous->func value used to find the function name is the same as or above L->top.


In this case, L->top is used (increase or decrement) during the error handling process, and the value of L->ci->previous->func is overwritten with another value. As a result, it will refer to a non-function value in the function funcnamefromcode.

Additionally, the function’s argument must contain “…” for the crash to occur. Perhaps it has something to do with ‘ci->func += actual + 1’ in the luaT_adjustvarargs function.




How to patch(suggestion):


In fact, the patch mentioned here is not recommended because it does not eliminate the cause and it is not known how it will affect the error handling function.

However, I have checked that the crash does not occur by applying the patch, so I would like to use it as a reference only. It is recommended to patch the luaT_adjustvarargs function to eliminate the cause.




diff --git a/ldebug.c b/ldebug.c

index dc5f78c6..9b917b24 100644

--- a/ldebug.c

+++ b/ldebug.c

@@ -96,6 +96,8 @@ int luaG_getfuncline (const Proto *f, int pc) {



 static int getcurrentline (CallInfo *ci) {

+  if(ci->func_corrupted)

+    return -1;

   return luaG_getfuncline(ci_func(ci)->p, currentpc(ci));



@@ -324,8 +326,11 @@ static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {

     return "metamethod";  /* report it as such */


   /* calling function is a known Lua function? */

-  else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous))

+  else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous)){

+    if(ci->previous->func_corrupted)

+      return "func_corrupted";

     return funcnamefromcode(L, ci->previous, name);

+  }

   else return NULL;  /* no way to find a name */



diff --git a/ldo.c b/ldo.c

index f282a773..a565b6ea 100644

--- a/ldo.c

+++ b/ldo.c

@@ -109,6 +109,8 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {



   L->top = oldtop + 1;

+  if(L->ci->previous->func >= L->top)

+    L->ci->previous->func_corrupted = 1;




@@ -483,6 +485,7 @@ l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret,

   ci->nresults = nret;

   ci->callstatus = mask;

   ci->top = top;

+  ci->func_corrupted = 0 ;

   return ci;



diff --git a/lstate.h b/lstate.h

index 44cf939c..0f2af604 100644

--- a/lstate.h

+++ b/lstate.h

@@ -196,6 +196,7 @@ typedef struct CallInfo {

   } u2;

   short nresults;  /* expected number of results from this function */

   unsigned short callstatus;

+  unsigned char func_corrupted;

 } CallInfo;



Thank you for reading.


Found by: WooSun Kang (team Nil Armstrong)