Re: HARDMEMTESTS: apparent stale stack pointer written to L->top by checkGC (heap OOB read in traversethread)

"'TopchetoEU' via lua-l" <[email protected]>
Newsgroups gmane.comp.lang.lua.general
Message-ID <[email protected]>
On Thursday, 27 August 2026 03:15:21 EEST Thota Lavanya wrote:
> You bring the perfect mix of insight and fun.
> 
> 
> On Wed, Aug 19, 2026 at 12:39 PM kid <[email protected]> wrote:
> 
> 
> Hello,
> 
> I would like to report a problem I ran into while testing the current master
> (7579fc9d7ed90240487251dfb69168f8e64e9294) built with -DHARDMEMTESTS:
> AddressSanitizer reports a heap out-of-bounds read in traversethread. From
> my reading of the code, it looks as if checkGC(L, ra + 1) wrote a stale
> stack pointer to L->top after the GC step moved the stack, but I may well
> be missing something about the intended invariants here, so I would
> appreciate your view. I found it while fuzzing with an OSS-Fuzz-style
> harness, but it also reproduces with the plain interpreter.
> 
> Reproduction
> 
> Save as repro.lua:
> 
> local function f(n)
>     if n <= 0 then return 0 end 
>     return f(n - 1) + 0 
> end 
> f(100) 
> local function g() return 1 end
> 
> 
> 
> Build and run:
> 
> git clone https://github.com/lua/lua && cd lua
> make CC=clang \
> MYCFLAGS="-std=c99 -O1 -g -DLUA_USE_LINUX -DHARDMEMTESTS -fsanitize=address
> -fno-omit-frame-pointer" \ MYLDFLAGS="-fsanitize=address"
> ./lua repro.lua
> 
> 
> Result:
> 
> ==7071==ERROR: AddressSanitizer: heap-buffer-overflow on address
> 0x518000000ff8 at pc 0x598b2bf1a477 bp 0x7ffc26980780 sp 0x7ffc26980778
> READ of size 1 at 0x518000000ff8 thread T0
>     #0 0x598b2bf1a476  (lua+0x143476) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => propagatemark lgc.c:705
>     #1 0x598b2bf1646e  (lua+0x13f46e) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => atomic lgc.c:745
>     #2 0x598b2bf130b7  (lua+0x13c0b7) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => entergen lgc.c:1431
>     #3 0x598b2bf158bb  (lua+0x13e8bb) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => fullgen lgc.c:1460
>     #4 0x598b2bf4a369  (lua+0x173369) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaV_execute lvm.c:1939
>     #5 0x598b2bf09edd  (lua+0x132edd) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_callnoyield ldo.c:768
>     #6 0x598b2bf05b55  (lua+0x12eb55) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_rawrunprotected ldo.c:166
>     #7 0x598b2bf0b355  (lua+0x134355) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_pcall ldo.c:1090
>     #8 0x598b2befb9a8  (lua+0x1249a8) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => lua_pcallk lapi.c:1097
>     #9 0x598b2bef02ca  (lua+0x1192ca) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => docall lua.c:168
>     #10 0x598b2beef59b  (lua+0x11859b) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => pmain lua.c:272
>     #11 0x598b2bf097b7  (lua+0x1327b7) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_precall ldo.c:657
>     #12 0x598b2bf09eaf  (lua+0x132eaf) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_callnoyield ldo.c:766
>     #13 0x598b2bf05b55  (lua+0x12eb55) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_rawrunprotected ldo.c:166
>     #14 0x598b2bf0b355  (lua+0x134355) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_pcall ldo.c:1090
>     #15 0x598b2befb9a8  (lua+0x1249a8) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => lua_pcallk lapi.c:1097
>     #16 0x598b2beeea5d  (lua+0x117a5d) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => main lua.c:788
>     #17 0x7dfe5e02a1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId:
> 328820b908de8ea1ef79afa8995e302e819163d7) #18 0x7dfe5e02a28a
>  (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId:
> 328820b908de8ea1ef79afa8995e302e819163d7) #19 0x598b2be15604  (lua+0x3e604)
> (BuildId: cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => _start ??:?
> 
> 0x518000000ff8 is located 8 bytes after 880-byte region
> [0x518000000c80,0x518000000ff0) allocated by thread T0 here:
>     #0 0x598b2beb0870  (lua+0xd9870) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => ___interceptor_realloc ??:?
>     #1 0x598b2bf1beee  (lua+0x144eee) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaM_realloc_ lmem.c:180
>     #2 0x598b2bf062de  (lua+0x12f2de) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_reallocstack ldo.c:339
>     #3 0x598b2bf069ee  (lua+0x12f9ee) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_shrinkstack ldo.c:426
>     #4 0x598b2bf19764  (lua+0x142764) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => propagatemark lgc.c:710
>     #5 0x598b2bf166f7  (lua+0x13f6f7) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => atomic lgc.c:745
>     #6 0x598b2bf14978  (lua+0x13d978) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaC_step lgc.c:1348
>     #7 0x598b2bf4a2c8  (lua+0x1732c8) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaV_execute lvm.c:1939
>     #8 0x598b2bf09edd  (lua+0x132edd) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_callnoyield ldo.c:768
>     #9 0x598b2bf05b55  (lua+0x12eb55) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_rawrunprotected ldo.c:166
>     #10 0x598b2bf0b355  (lua+0x134355) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_pcall ldo.c:1090
>     #11 0x598b2befb9a8  (lua+0x1249a8) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => lua_pcallk lapi.c:1097
>     #12 0x598b2bef02ca  (lua+0x1192ca) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => docall lua.c:168
>     #13 0x598b2beef59b  (lua+0x11859b) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => pmain lua.c:272
>     #14 0x598b2bf097b7  (lua+0x1327b7) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_precall ldo.c:657
>     #15 0x598b2bf09eaf  (lua+0x132eaf) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_callnoyield ldo.c:766
>     #16 0x598b2bf05b55  (lua+0x12eb55) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_rawrunprotected ldo.c:166
>     #17 0x598b2bf0b355  (lua+0x134355) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => luaD_pcall ldo.c:1090
>     #18 0x598b2befb9a8  (lua+0x1249a8) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => lua_pcallk lapi.c:1097
>     #19 0x598b2beeea5d  (lua+0x117a5d) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => main lua.c:788
>     #20 0x7dfe5e02a1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId:
> 328820b908de8ea1ef79afa8995e302e819163d7) #21 0x7dfe5e02a28a
>  (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId:
> 328820b908de8ea1ef79afa8995e302e819163d7) #22 0x598b2be15604  (lua+0x3e604)
> (BuildId: cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => _start ??:?
> 
> SUMMARY: AddressSanitizer: heap-buffer-overflow (lua+0x143476) (BuildId:
> cf47c548dcdedf37f5c5990c60b2f6da369f4d68) => propagatemark lgc.c:705
> Shadow bytes around the buggy address:
>   0x518000000d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x518000000d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x518000000e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x518000000e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x518000000f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> =>0x518000000f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fa[fa]
>   0x518000001000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>   0x518000001080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>   0x518000001100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>   0x518000001180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>   0x518000001200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
> Shadow byte legend (one shadow byte represents 8 application bytes):
>   Addressable:           00
>   Partially addressable: 01 02 03 04 05 06 07
>   Heap left redzone:       fa
>   Freed heap region:       fd
>   Stack left redzone:      f1
>   Stack mid redzone:       f2
>   Stack right redzone:     f3
>   Stack after return:      f5
>   Stack use after scope:   f8
>   Global redzone:          f9
>   Global init order:       f6
>   Poisoned by user:        f7
>   Container overflow:      fc
>   Array cookie:            ac
>   Intra object redzone:    bb
>   ASan internal:           fe
>   Left alloca redzone:     ca
>   Right alloca redzone:    cb
> ==7071==ABORTING
> 
> That is, the collection that over-reads the stack happens in
> the luaC_fullgc forced by HARDMEMTESTS, and it reads from a stack that was
> just shrunk/reallocated by the luaC_step of the very same checkGC.
> 
> Explanation
> 
> OP_CLOSURE (lvm.c:1935) and OP_NEWTABLE (lvm.c:1423) both end
> with checkGC(L, ra + 1), which expands to luaC_condGC(L, (savepc(ci),
> L->top.p = ra+1), updatetrap(ci)).
> 
> With HARDMEMTESTS on, luaC_condGC evaluates pre twice (lgc.h:229-235): once
> before luaC_step (when the debt is due) and once again before the
> forced luaC_fullgc (condchangemem).
> 
> In the repro, f(100) leaves the stack much larger than necessary (stacksize
> > 3*inuse). At the OP_CLOSURE for g:
> 
> first pre: L->top.p = ra + 1 (valid); luaC_step runs, its atomic phase
> calls traversethread -> luaD_shrinkstack, which reallocates the
> stack. correctstack remaps L->top correctly and sets ci->u.l.trap = 1, but
> the C registers base/ra of the running luaV_execute stay stale until the
> next vmfetch; second pre (from condchangemem): re-evaluates L->top.p = ra +
> 1 with the stale ra, overwriting the correctly remapped value with a
> dangling pointer (I confirmed this write with a gdb watchpoint: L->top goes
> from the remapped, in-bounds address to an address in the freed old stack,
> PC at lvm.c:1939); luaC_fullgc then marks [th->stack, th->top) out of
> bounds (lgc.c:704-705).
> 
> Without HARDMEMTESTS, condchangemem compiles to nothing, pre runs only once
> (before the step, with a valid ra), and the trap/vmfetch mechanism
> repairs base before it is used again — so normal builds cannot reach this.
> It is also rare with HARDMEMTESTS: the step of that particular checkGC must
> actually shrink the stack, i.e. the stack must be oversized relative
> to inuse at that point. Besides deep recursion, a call with many arguments
> also works; this variant needs no recursion at all (crashes at OP_NEWTABLE,
> lvm.c:1431):
> 
> local function sink(...) return 0 end 
> local t = {} 
> for i = 1, 20000 do t[i] = i end 
> sink(table.unpack(t)) 
> local x = {}
> 
> 
> 
> Note that OP_CONCAT's checkGC(L, L->top.p) (lvm.c:1637) is not affected,
> because re-reading L->top.p after the remap is safe.
> 
> --
> You received this message because you are subscribed to the Google Groups
> "lua-l" group. To unsubscribe from this group and stop receiving emails
> from it, send an email to [email protected]. To view this
> discussion visit
> https://groups.google.com/d/msgid/lua-l/ec502a73-1dc1-4912-8ba1-959d467d0dc
> bn%40googlegroups.com.
> 
> 
> --
> You received this message because you are subscribed to the Google Groups
> "lua-l" group. To unsubscribe from this group and stop receiving emails
> from it, send an email to [email protected]. To view this
> discussion visit
> https://groups.google.com/d/msgid/lua-l/CAP_y_jCzYJiXgR0eZ-yWqLZ%3D%3DGd167
> d7YsdOcQvWuTCPu6p%2BxA%40mail.gmail.com.

Could you pretty please stop drowning the list with AI slop? If you are going 
to report a bug that AI found, at least have the courtesy to write it 
yourself, the rest of us are humans, as far as I'm aware...




-- 
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion visit https://groups.google.com/d/msgid/lua-l/7InMjuv4TbmIMjNuloi8WQ%40topcheto.eu.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.