mirror of
https://github.com/bellard/quickjs.git
synced 2026-03-31 12:18:01 +00:00
Fix use-after-free in Atomics operations with resizable ArrayBuffers
The Atomics operations (add, sub, and, or, xor, exchange, compareExchange,
store) capture a pointer to the buffer element before calling JS_ToUint32()
or JS_ToBigInt64() on the value arguments. These conversion functions can
execute arbitrary JavaScript via valueOf() callbacks, which may resize the
underlying ArrayBuffer.
After the value conversion, only a detached check was performed, but not a
bounds re-validation. If the buffer was resized (not detached), the stale
pointer would still be used, leading to a heap-use-after-free.
The fix re-validates the atomic access by calling js_atomics_get_ptr() again
after the value conversions. This follows the RevalidateAtomicAccess()
pattern already present in js_atomics_get_ptr() itself.
Proof of Concept:
let ab = new ArrayBuffer(1024, { maxByteLength: 2048 });
let int32Array = new Int32Array(ab);
let malicious = { valueOf: () => { ab.resize(8); return 1; } };
Atomics.add(int32Array, 200, malicious); // heap-use-after-free
This commit is contained in:
parent
f1139494d1
commit
4aa7c632ac
23
quickjs.c
23
quickjs.c
@ -58775,8 +58775,11 @@ static JSValue js_atomics_op(JSContext *ctx,
|
||||
rep_val = v32;
|
||||
}
|
||||
}
|
||||
if (abuf->detached)
|
||||
return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
|
||||
/* the buffer may have been resized or detached during the
|
||||
value conversion via valueOf(), so the pointer must be re-fetched */
|
||||
if (js_atomics_get_ptr(ctx, &ptr, &abuf, &size_log2, &class_id,
|
||||
argv[0], argv[1], 0))
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
switch(op | (size_log2 << 3)) {
|
||||
@ -58901,8 +58904,12 @@ static JSValue js_atomics_store(JSContext *ctx,
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (abuf->detached)
|
||||
return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
|
||||
/* buffer may have been resized or detached during value conversion */
|
||||
if (js_atomics_get_ptr(ctx, &ptr, &abuf, &size_log2, NULL,
|
||||
argv[0], argv[1], 0)) {
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
atomic_store((_Atomic(uint64_t) *)ptr, v64);
|
||||
} else {
|
||||
uint32_t v;
|
||||
@ -58914,8 +58921,12 @@ static JSValue js_atomics_store(JSContext *ctx,
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (abuf->detached)
|
||||
return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
|
||||
/* buffer may have been resized or detached during value conversion */
|
||||
if (js_atomics_get_ptr(ctx, &ptr, &abuf, &size_log2, NULL,
|
||||
argv[0], argv[1], 0)) {
|
||||
JS_FreeValue(ctx, ret);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
switch(size_log2) {
|
||||
case 0:
|
||||
atomic_store((_Atomic(uint8_t) *)ptr, v);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user