lua-users home
lua-l archive

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


I found an interesting crash on Lua interpreter.
(Ubuntu 20.04 LTS / glibc 2.3.1 / Lua 5.4.4 commit hash 0e5071b5fbcc244d9f8c4bae82e327ad59bccc3f)

Code below generates use after read crash with address-sanitizer applied.
1 co = coroutine.wrap(
2   function()
3     local pcall <close> = setmetatable(
4       {}, {__close = function() pcall(co) end}
5     )
6     pcall()
7   end
8 )
9 co()
==7572==ERROR: AddressSanitizer: heap-use-after-free on address 0x617000001230 at pc 0x56248f9fc291 bp 0x7ffeab31d500 sp 0x7ffeab31d4f0
READ of size 8 at 0x617000001230 thread T0
    #0 0x56248f9fc290 in luaD_seterrorobj /home/user/Project_Lua/lua-master/ldo.c:107
    #1 0x56248fa23ba1 in luaE_resetthread /home/user/Project_Lua/lua-master/lstate.c:335
    #2 0x56248fa23d6b in lua_resetthread /home/user/Project_Lua/lua-master/lstate.c:348
    #3 0x56248fa7be50 in luaB_auxwrap /home/user/Project_Lua/lua-master/lcorolib.c:79
    #4 0x56248f9ffabd in luaD_precall /home/user/Project_Lua/lua-master/ldo.c:542
    #5 0x56248fa48b94 in luaV_execute /home/user/Project_Lua/lua-master/lvm.c:1636
    #6 0x56248f9fffbc in ccall /home/user/Project_Lua/lua-master/ldo.c:582
    #7 0x56248fa00070 in luaD_callnoyield /home/user/Project_Lua/lua-master/ldo.c:600
    #8 0x56248f9f2ec8 in f_call /home/user/Project_Lua/lua-master/lapi.c:1039
    #9 0x56248f9fc866 in luaD_rawrunprotected /home/user/Project_Lua/lua-master/ldo.c:144
    #10 0x56248fa01a2b in luaD_pcall /home/user/Project_Lua/lua-master/ldo.c:897

Executing the example code, error occrus when calling pcall() in line 6. This is because pcall is redefined as tbc variable in line 3. During error handling process by Lua, Lua stack of the coroutine is reallocated through luaD_reallocstack(ldo.c:191) internally. After this, Lua restores old status of L->ci by "L->ci = old_ci" in luaD_pcall(ldo.c:899). However, old_ci points to freed Lua stack of the coroutine. Eventually, this causes L->top to also point  freed chunk through updating L->top value based on old_ci in moveresults function (ldo.c:415). Use after free read is detected whenever L->top is dereferenced, luaD_seterrorobj(ldo.c:107) in example code.

Similarly, code below generates use after write crash.
1 co = coroutine.wrap(
2   function()
3    local pcall <close> = setmetatable(
4      {}, {__close = function() return pcall(co) end}
5     )
6     pcall()
7   end
8 )
9 co()
==8901==ERROR: AddressSanitizer: heap-use-after-free on address 0x617000001250 at pc 0x558c40bdad82 bp 0x7ffddaacfc80 sp 0x7ffddaacfc70
WRITE of size 8 at 0x617000001250 thread T0
    #0 0x558c40bdad81 in moveresults /home/user/Project_Lua/lua-master/ldo.c:448
    #1 0x558c40bdafe0 in luaD_poscall /home/user/Project_Lua/lua-master/ldo.c:466
    #2 0x558c40bdbad6 in luaD_precall /home/user/Project_Lua/lua-master/ldo.c:545
    #3 0x558c40c24f70 in luaV_execute /home/user/Project_Lua/lua-master/lvm.c:1664
    #4 0x558c40bdbfbc in ccall /home/user/Project_Lua/lua-master/ldo.c:582
    #5 0x558c40bdc070 in luaD_callnoyield /home/user/Project_Lua/lua-master/ldo.c:600
    #6 0x558c40be0a8e in callclosemethod /home/user/Project_Lua/lua-master/lfunc.c:118
    #7 0x558c40be0c60 in prepcallclosemth /home/user/Project_Lua/lua-master/lfunc.c:153
    #8 0x558c40be140c in luaF_close /home/user/Project_Lua/lua-master/lfunc.c:234
    #9 0x558c40bdd608 in closepaux /home/user/Project_Lua/lua-master/ldo.c:860
    #10 0x558c40bd8866 in luaD_rawrunprotected /home/user/Project_Lua/lua-master/ldo.c:144

The only difference is that there is return value in line 4. After L->top value points to freed chunk, Return value is being copied to this area in moveresults function(ldo.c:448)

I think there needs to be some additional update logic between error handling and Lua stack reallocation. Thank you for reading my analysis.