Skip to content

heap-use-after-free in build_backtrace #1469

@krzyw1x

Description

@krzyw1x

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions