[BUG] SUMMARY: AddressSanitizer: SEGV /home/lua-5.4.7/src/lgc.c:639 in traversethread

84 views
Skip to first unread message

Jizhou Chen

unread,
Jan 17, 2025, 4:26:35 AM1/17/25
to lua-l
Hi there,

Just to report that Lua crashes with SEGV with the following test case.

Version: 5.4.7 (ASAN enabled)

Test case (fuzzer-generated):

do
 
  local a = collectgarbage("setpause", 200)
  local b = collectgarbage("generational", 1)
  local t = {pcall(pcall,pcall,pcall,pcall,assert,pcall,pcall,error,"hi")}
  for i = 3, 5, 2 do
    local p = t[i]
    for i = 1.0, 10 do
  local r = debug.getregistry()
  local tt = {}
  tt.__gc = function (x)
      assert(debug.setlocal(2, #t, x) == "(vararg)")
      assert(thread_id < 1000)
     end
  local u = setmetatable({}, tt)
  ___Glob = {u}
end
  end
 
  collectgarbage("generational", a)
  debug.getuservalue(b, -2)
  collectgarbage()
end


_G["while"] = 234

Stack dump:
AddressSanitizer:DEADLYSIGNAL
=================================================================
==938726==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x55b74613e257 bp 0x61b000000150 sp 0x7fff031b5b00 T0)
==938726==The signal is caused by a READ memory access.
==938726==Hint: address points to the zero page.
    #0 0x55b74613e256 in traversethread /home/lua-5.4.7/src/lgc.c:639
    #1 0x55b74613e256 in propagatemark /home/lua-5.4.7/src/lgc.c:670
    #2 0x55b74613e4a0 in propagateall /home/lua-5.4.7/src/lgc.c:679
    #3 0x55b74613e996 in atomic /home/lua-5.4.7/src/lgc.c:1543
    #4 0x55b746140359 in youngcollection /home/lua-5.4.7/src/lgc.c:1261
    #5 0x55b746140359 in genstep /home/lua-5.4.7/src/lgc.c:1461
    #6 0x55b746140359 in luaC_step /home/lua-5.4.7/src/lgc.c:1696
    #7 0x55b74615da75 in luaV_execute /home/lua-5.4.7/src/lvm.c:1377
    #8 0x55b746136995 in ccall /home/lua-5.4.7/src/ldo.c:637
    #9 0x55b746136995 in luaD_callnoyield /home/lua-5.4.7/src/ldo.c:655
    #10 0x55b74612752e in f_call /home/lua-5.4.7/src/lapi.c:1038
    #11 0x55b746133205 in luaD_rawrunprotected /home/lua-5.4.7/src/ldo.c:144
    #12 0x55b7461374fd in luaD_pcall /home/lua-5.4.7/src/ldo.c:957
    #13 0x55b74612c28a in lua_pcallk /home/lua-5.4.7/src/lapi.c:1064
    #14 0x55b74612548e in docall /home/lua-5.4.7/src/lua.c:161
    #15 0x55b746126920 in handle_script /home/lua-5.4.7/src/lua.c:265
    #16 0x55b746126920 in pmain /home/lua-5.4.7/src/lua.c:654
    #17 0x55b746135ff5 in precallC /home/lua-5.4.7/src/ldo.c:529
    #18 0x55b746135ff5 in luaD_precall /home/lua-5.4.7/src/ldo.c:595
    #19 0x55b746136958 in ccall /home/lua-5.4.7/src/ldo.c:635
    #20 0x55b746136958 in luaD_callnoyield /home/lua-5.4.7/src/ldo.c:655
    #21 0x55b74612752e in f_call /home/lua-5.4.7/src/lapi.c:1038
    #22 0x55b746133205 in luaD_rawrunprotected /home/lua-5.4.7/src/ldo.c:144
    #23 0x55b7461374fd in luaD_pcall /home/lua-5.4.7/src/ldo.c:957
    #24 0x55b74612c28a in lua_pcallk /home/lua-5.4.7/src/lapi.c:1064
    #25 0x55b746126a70 in main /home/lua-5.4.7/src/lua.c:682
    #26 0x7f4b4b280082 in __libc_start_main ../csu/libc-start.c:308
    #27 0x55b746124fcd in _start (/home/lua-5.4.7/src/lua+0x17fcd)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/lua-5.4.7/src/lgc.c:639 in traversethread
==938726==ABORTING

Yao Zi

unread,
Jan 17, 2025, 6:36:38 AM1/17/25
to lu...@googlegroups.com
On Thu, Jan 16, 2025 at 06:44:30PM -0800, Jizhou Chen wrote:
> Hi there,
>
> Just to report that Lua crashes with SEGV with the following test case.
>
> *Version: *5.4.7 (ASAN enabled)
>
> *Test case (fuzzer-generated):*
>
> *do local a = collectgarbage("setpause", 200) local b =
> collectgarbage("generational", 1) local t =
> {pcall(pcall,pcall,pcall,pcall,assert,pcall,pcall,error,"hi")} for i = 3,
> 5, 2 do local p = t[i] for i = 1.0, 10 do local r =
> debug.getregistry() local tt = {} tt.__gc = function (x)
> assert(debug.setlocal(2, #t, x) == "(vararg)") assert(thread_id <
> 1000) end local u = setmetatable({}, tt) ___Glob = {u}end end
> collectgarbage("generational", a) debug.getuservalue(b, -2)
> collectgarbage()end_G["while"] = 234*
>
> *Stack dump:*
tl;dr: this isn't a fault of Lua interpreter. The usage of
debug.setlocal() is unsafe. One should remember that functions in the
debug library are dangerous and may break expected behavior.

You should exert care when using this library. Several of its
functions violate basic assumptions about Lua code (e.g., that
variables local to a function cannot be accessed from outside;
that userdata metatables cannot be changed by Lua code; that
Lua programs do not crash) and therefore can compromise otherwise
secure code.[1]

Format of your testcase seems broken. For the interested reader, here's
a reformated version,

do
local a = collectgarbage("setpause", 200)
local b = collectgarbage("generational", 1)
local t = {pcall(pcall,pcall,pcall,pcall,assert,pcall,
pcall,error,"hi")}
for i = 3, 5, 2 do
local p = t[i]
for i = 1.0, 10 do
local r = debug.getregistry()
local tt = {}
tt.__gc = function (x)
assert(debug.setlocal(2, #t, x) == "(vararg)")
assert(thread_id < 1000)
end
local u = setmetatable({}, tt)
___Glob = {u}
end
end
collectgarbage("generational", a)
debug.getuservalue(b, -2)
collectgarbage()
end
_G["while"] = 234

which immediately results in a SIGSEGV on my x86_64-ewe-linux-musl
machine.

The cataclysm occurs around the call to debug.setlocal(). With this in
mind, I rewrote a smaller case,

do
collectgarbage("generational", 1);
local a, b, c, d

for i = 1, 9 do
local tt = {};
tt.__gc = function (x)
debug.setlocal(2, 5, x);
end

x = {setmetatable({}, tt)}
end
end

If you build Lua with assertion enabled (src/llimits.h:103), a failure
will be observed,

Assertion failed: (((((&(ra)->val)))->tt_) == (((3) | ((0) << 4))))
(lvm.c: luaV_execute: 1790)
Aborted

and lvm.c:1790 handles for-loop opcode. This assertion checks whether
for-loop control variable is of an integer type.

When compiling a for loop, Lua interpreter allocates locals (registers)
to hold the evaluated results of the control varible, limit and step
expressions[2].

Looking back to the case: since the minor multiplier for generational
gc is small, after some new tables created in several iterations, a GC
step will be triggered: the __gc metamethod will be invoked, with the
main chunk as the caller. Then setlocal(2, 5, x) will rewrite the fifth
local in the main chunk: a, b, c, d are the first four and we're
actually rewriting the one containing control variable, replacing the
integer with a table. This really shouldn't happen when running normal
code.

Thanks,
Yao Zi

[1]: Lua 5.4 Manual, 6.10 The Debug Library
[2]: Lua 5.4 Manual, 3.3.5: The loop starts by evaluating once the three
control expressions.
Reply all
Reply to author
Forward
0 new messages