diff --git a/Libraries/LibJS/Runtime/AtomicsObject.cpp b/Libraries/LibJS/Runtime/AtomicsObject.cpp index 27574df959d..f2e8c2ef072 100644 --- a/Libraries/LibJS/Runtime/AtomicsObject.cpp +++ b/Libraries/LibJS/Runtime/AtomicsObject.cpp @@ -290,12 +290,6 @@ static ThrowCompletionOr atomic_compare_exchange_impl(VM& vm, TypedArrayB // 1. Let byteIndexInBuffer be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index). auto byte_index_in_buffer = TRY(validate_atomic_access_on_integer_typed_array(vm, typed_array, index)); - // 2. Let buffer be typedArray.[[ViewedArrayBuffer]]. - auto* buffer = typed_array.viewed_array_buffer(); - - // 3. Let block be buffer.[[ArrayBufferData]]. - auto& block = buffer->buffer(); - Value expected; Value replacement; @@ -319,6 +313,15 @@ static ThrowCompletionOr atomic_compare_exchange_impl(VM& vm, TypedArrayB // 6. Perform ? RevalidateAtomicAccess(typedArray, byteIndexInBuffer). TRY(revalidate_atomic_access(vm, typed_array, byte_index_in_buffer)); + // NOTE: We defer steps 2 and 3 to ensure we have revalidated the TA before accessing these internal slots. + // In our implementation, accessing [[ArrayBufferData]] on a detached buffer will fail assertions. + + // 2. Let buffer be typedArray.[[ViewedArrayBuffer]]. + auto* buffer = typed_array.viewed_array_buffer(); + + // 3. Let block be buffer.[[ArrayBufferData]]. + auto& block = buffer->buffer(); + // 7. Let elementType be TypedArrayElementType(typedArray). // 8. Let elementSize be TypedArrayElementSize(typedArray). diff --git a/Libraries/LibJS/Tests/builtins/Atomics/Atomics.compareExchange.js b/Libraries/LibJS/Tests/builtins/Atomics/Atomics.compareExchange.js index 60a0e724adf..ae99428a2de 100644 --- a/Libraries/LibJS/Tests/builtins/Atomics/Atomics.compareExchange.js +++ b/Libraries/LibJS/Tests/builtins/Atomics/Atomics.compareExchange.js @@ -21,6 +21,24 @@ test("error cases", () => { const array = new Int32Array(4); Atomics.compareExchange(array, 100, 0, 0); }).toThrow(RangeError); + + expect(() => { + const array = new Int32Array(4); + + function detachArrayWhileAccessingIndex(array) { + return { + valueOf() { + detachArrayBuffer(array.buffer); + return 0; + }, + }; + } + + Atomics.compareExchange(array, detachArrayWhileAccessingIndex(array), 0, 0); + }).toThrowWithMessage( + TypeError, + "TypedArray contains a property which references a value at an index not contained within its buffer's bounds" + ); }); test("basic functionality (non-BigInt)", () => {