Skip to content

UAF in ArrayBuffer #1208

@yagilpatternlabs

Description

@yagilpatternlabs

Hey, the following code triggers the address sanitizer:

// Create ArrayBuffer and TypedArray
const ab = new ArrayBuffer(64);
const ta = new Uint8Array(ab);
ta.fill(0x41);

console.log("Before detach: ta[0] =", ta[0]);

// Detach the ArrayBuffer (frees the underlying memory)
ab.transfer();
console.log("After transfer");

// Use-After-Free: accessing freed memory
console.log("After detach:  ta[0] =", ta[0]);

To reproduce, save the code as poc.js and compile and run with:

cmake -B build -S . -DQJS_ENABLE_ASAN=ON
cmake --build build --parallel 12
build/qjs poc.js

The expected output should be like:

Before detach: ta[0] = 65
After transfer
=================================================================
==99762==ERROR: AddressSanitizer: heap-use-after-free on address 0x6060000021e0 at pc 0x0001022abecc bp 0x00016dbb9640 sp 0x00016dbb9638
READ of size 1 at 0x6060000021e0 thread T0
    #0 0x0001022abec8 in js_get_fast_array_element+0xdb8 (qjs:arm64+0x100067ec8)
    #1 0x000102325b20 in JS_GetPropertyValue+0x60 (qjs:arm64+0x1000e1b20)
    #2 0x0001022c12d4 in JS_CallInternal+0x737c (qjs:arm64+0x10007d2d4)
    #3 0x000102378c30 in js_async_function_resume+0x18c (qjs:arm64+0x100134c30)
    #4 0x0001022f5eac in js_async_function_call+0x27c (qjs:arm64+0x1000b1eac)
    #5 0x000102344248 in js_execute_sync_module+0xc4 (qjs:arm64+0x100100248)
    #6 0x000102343894 in js_inner_module_evaluation+0x700 (qjs:arm64+0x1000ff894)
    #7 0x0001022ddee8 in JS_EvalFunctionInternal+0x308 (qjs:arm64+0x100099ee8)
    #8 0x000102246fe4 in eval_buf+0xf4 (qjs:arm64+0x100002fe4)
    #9 0x00010224716c in eval_file+0x118 (qjs:arm64+0x10000316c)
    #10 0x0001022462e0 in main+0x1184 (qjs:arm64+0x1000022e0)
    #11 0x00018fefab94 in start+0x17b8 (dyld:arm64e+0xfffffffffff3ab94)

0x6060000021e0 is located 0 bytes inside of 64-byte region [0x6060000021e0,0x606000002220)
freed by thread T0 here:
    #0 0x000102e45480 in free+0x7c (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x3d480)
    #1 0x000102308c80 in js_array_buffer_finalizer+0x1b4 (qjs:arm64+0x1000c4c80)
    #2 0x000102311f48 in free_gc_object+0x8a0 (qjs:arm64+0x1000cdf48)
    #3 0x00010229a0e4 in js_free_value_rt+0x270 (qjs:arm64+0x1000560e4)
    #4 0x0001022ce424 in JS_CallInternal+0x144cc (qjs:arm64+0x10008a424)
    #5 0x000102378c30 in js_async_function_resume+0x18c (qjs:arm64+0x100134c30)
    #6 0x0001022f5eac in js_async_function_call+0x27c (qjs:arm64+0x1000b1eac)
    #7 0x000102344248 in js_execute_sync_module+0xc4 (qjs:arm64+0x100100248)
    #8 0x000102343894 in js_inner_module_evaluation+0x700 (qjs:arm64+0x1000ff894)
    #9 0x0001022ddee8 in JS_EvalFunctionInternal+0x308 (qjs:arm64+0x100099ee8)
    #10 0x000102246fe4 in eval_buf+0xf4 (qjs:arm64+0x100002fe4)
    #11 0x00010224716c in eval_file+0x118 (qjs:arm64+0x10000316c)
    #12 0x0001022462e0 in main+0x1184 (qjs:arm64+0x1000022e0)
    #13 0x00018fefab94 in start+0x17b8 (dyld:arm64e+0xfffffffffff3ab94)

previously allocated by thread T0 here:
    #0 0x000102e4567c in calloc+0x80 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x3d67c)
    #1 0x00010227b4ac in js_mallocz+0xa0 (qjs:arm64+0x1000374ac)
    #2 0x00010230042c in js_array_buffer_constructor3+0x4b0 (qjs:arm64+0x1000bc42c)
    #3 0x000102419fe0 in js_array_buffer_constructor0+0x36c (qjs:arm64+0x1001d5fe0)
    #4 0x00010227cb78 in js_call_c_function+0x260 (qjs:arm64+0x100038b78)
    #5 0x0001022da518 in JS_CallConstructorInternal+0x33c (qjs:arm64+0x100096518)
    #6 0x0001022baf14 in JS_CallInternal+0xfbc (qjs:arm64+0x100076f14)
    #7 0x000102378c30 in js_async_function_resume+0x18c (qjs:arm64+0x100134c30)
    #8 0x0001022f5eac in js_async_function_call+0x27c (qjs:arm64+0x1000b1eac)
    #9 0x000102344248 in js_execute_sync_module+0xc4 (qjs:arm64+0x100100248)
    #10 0x000102343894 in js_inner_module_evaluation+0x700 (qjs:arm64+0x1000ff894)
    #11 0x0001022ddee8 in JS_EvalFunctionInternal+0x308 (qjs:arm64+0x100099ee8)
    #12 0x000102246fe4 in eval_buf+0xf4 (qjs:arm64+0x100002fe4)
    #13 0x00010224716c in eval_file+0x118 (qjs:arm64+0x10000316c)
    #14 0x0001022462e0 in main+0x1184 (qjs:arm64+0x1000022e0)
    #15 0x00018fefab94 in start+0x17b8 (dyld:arm64e+0xfffffffffff3ab94)

SUMMARY: AddressSanitizer: heap-use-after-free (qjs:arm64+0x100067ec8) in js_get_fast_array_element+0xdb8
Shadow bytes around the buggy address:
  0x606000001f00: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fd
  0x606000001f80: fa fa fa fa fd fd fd fd fd fd fd fd fa fa fa fa
  0x606000002000: fd fd fd fd fd fd fd fd fa fa fa fa fd fd fd fd
  0x606000002080: fd fd fd fd fa fa fa fa 00 00 00 00 00 00 00 00
  0x606000002100: fa fa fa fa 00 00 00 00 00 00 00 00 fa fa fa fa
=>0x606000002180: 00 00 00 00 00 00 00 fa fa fa fa fa[fd]fd fd fd
  0x606000002200: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fa
  0x606000002280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x606000002300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x606000002380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x606000002400: 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
==99762==ABORTING
[1]    99762 abort      build/qjs poc.js

Tested on MacOS, commit c85fd87

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