From d8e52b4fcee1cde99ef7a9ea1ff0c20738ec62e0 Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Fri, 15 May 2026 21:15:01 -0400 Subject: [PATCH] Validate typed array set and constructors on resizable buffers --- js/src/builtin/TypedArray.js | 8 ++- .../non262/ArrayBuffer/resizable-views.js | 9 +++ js/src/vm/SelfHosting.cpp | 4 ++ js/src/vm/TypedArrayCommon.h | 63 ++++++++++++++++--- js/src/vm/TypedArrayObject.cpp | 4 ++ 5 files changed, 77 insertions(+), 11 deletions(-) diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index 97a4c2d8f7..b5a3b2a011 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -1047,12 +1047,18 @@ function TypedArraySet(overloaded, offset = 0) { // Steps 9-10. var targetBuffer = GetAttachedArrayBuffer(target); + ThrowIfTypedArrayOutOfBounds(target); + // Step 11. var targetLength = TypedArrayLength(target); // Steps 12 et seq. - if (IsPossiblyWrappedTypedArray(overloaded)) + if (IsPossiblyWrappedTypedArray(overloaded)) { + if (PossiblyWrappedTypedArrayHasDetachedBuffer(overloaded)) + ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); + ThrowIfPossiblyWrappedTypedArrayOutOfBounds(overloaded); return SetFromTypedArray(target, overloaded, targetOffset, targetLength); + } return SetFromNonTypedArray(target, overloaded, targetOffset, targetLength, targetBuffer); } diff --git a/js/src/tests/non262/ArrayBuffer/resizable-views.js b/js/src/tests/non262/ArrayBuffer/resizable-views.js index 81e6328b67..e8d810b165 100644 --- a/js/src/tests/non262/ArrayBuffer/resizable-views.js +++ b/js/src/tests/non262/ArrayBuffer/resizable-views.js @@ -100,6 +100,15 @@ methodRab.resize(2); methodRab.resize(4); assertEq(methodFixed.length, 2); +var sourceRab = new ArrayBuffer(4, { maxByteLength: 8 }); +var oobSource = new Uint8Array(sourceRab, 2, 2); +sourceRab.resize(2); +assertThrowsInstanceOf(() => new Uint8Array(oobSource), TypeError); +assertThrowsInstanceOf(() => new Uint16Array(oobSource), TypeError); +assertThrowsInstanceOf(() => new Uint8Array(4).set(oobSource), TypeError); +sourceRab.resize(4); +assertEq(new Uint8Array(oobSource).length, 2); + var ctorRab = new ArrayBuffer(8, { maxByteLength: 8 }); var ShrinkingNewTarget = new Proxy(function() {}, { get(target, prop, receiver) { diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index d95d0b004f..24b23f19ff 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1462,6 +1462,10 @@ intrinsic_SetFromTypedArrayApproach(JSContext* cx, unsigned argc, Value* vp) JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); return false; } + if (unsafeTypedArrayCrossCompartment->isOutOfBounds()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS); + return false; + } // Steps 21, 23. uint32_t unsafeSrcLengthCrossCompartment = unsafeTypedArrayCrossCompartment->length(); diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index fab7210417..056f8b52be 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -1065,16 +1065,40 @@ class TypedArrayMethods if (!ToInt32(cx, args[1], &offset)) return false; - if (offset < 0 || uint32_t(offset) > target->length()) { - // the given offset is bogus + if (offset < 0) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); return false; } } + if (target->hasDetachedBuffer()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + if (target->isOutOfBounds()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS); + return false; + } + + uint32_t targetLength = target->length(); + if (uint32_t(offset) > targetLength) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); + return false; + } + RootedObject arg0(cx, &args[0].toObject()); if (arg0->is()) { - if (arg0->as().length() > target->length() - offset) { + Rooted source(cx, &arg0->as()); + if (source->hasDetachedBuffer()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + if (source->isOutOfBounds()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS); + return false; + } + + if (source->length() > targetLength - offset) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); return false; } @@ -1086,7 +1110,7 @@ class TypedArrayMethods if (!GetLengthProperty(cx, arg0, &len)) return false; - if (uint32_t(offset) > target->length() || len > target->length() - offset) { + if (len > targetLength - offset) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); return false; } @@ -1099,13 +1123,32 @@ class TypedArrayMethods return true; } - static bool - setFromTypedArray(JSContext* cx, Handle target, HandleObject source, - uint32_t offset = 0) - { - MOZ_ASSERT(source->is(), "use setFromNonTypedArray"); + static bool + setFromTypedArray(JSContext* cx, Handle target, HandleObject source, + uint32_t offset = 0) + { + MOZ_ASSERT(source->is(), "use setFromNonTypedArray"); - bool isShared = target->isSharedMemory() || source->as().isSharedMemory(); + if (target->hasDetachedBuffer()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + if (target->isOutOfBounds()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS); + return false; + } + + Rooted sourceArray(cx, &source->as()); + if (sourceArray->hasDetachedBuffer()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + if (sourceArray->isOutOfBounds()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS); + return false; + } + + bool isShared = target->isSharedMemory() || sourceArray->isSharedMemory(); switch (target->type()) { case Scalar::Int8: diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index cf89eba3bb..22d21c8550 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -1337,6 +1337,10 @@ TypedArrayObjectTemplate::fromTypedArray(JSContext* cx, HandleObject other, b JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); return nullptr; } + if (srcArray->isOutOfBounds()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS); + return nullptr; + } // Step 9. uint32_t elementLength = srcArray->length();