From 3cb76bb20e4d4caed347bd9a0496fd3effbe996b Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Fri, 15 May 2026 20:55:57 -0400 Subject: [PATCH] Validate typed array methods on resizable buffers --- js/src/builtin/TypedArray.js | 15 +++++++ js/src/js.msg | 1 + .../non262/ArrayBuffer/resizable-views.js | 41 +++++++++++++++++++ js/src/vm/SelfHosting.cpp | 34 +++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index 16603d9b86..1c054ed759 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -41,10 +41,21 @@ function TypedArrayContentTypeIsBigIntMethod() { return IsBigInt64TypedArray(this) || IsBigUint64TypedArray(this); } +function ThrowIfTypedArrayOutOfBounds(tarray) { + if (TypedArrayIsOutOfBounds(tarray)) + ThrowTypeError(JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS); +} + +function ThrowIfPossiblyWrappedTypedArrayOutOfBounds(tarray) { + if (PossiblyWrappedTypedArrayIsOutOfBounds(tarray)) + ThrowTypeError(JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS); +} + function GetAttachedArrayBuffer(tarray) { var buffer = ViewedArrayBufferIfReified(tarray); if (IsDetachedBuffer(buffer)) ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); + ThrowIfTypedArrayOutOfBounds(tarray); return buffer; } @@ -67,6 +78,7 @@ function IsTypedArrayEnsuringArrayBuffer(arg) { if (IsObject(arg) && IsPossiblyWrappedTypedArray(arg)) { if (PossiblyWrappedTypedArrayHasDetachedBuffer(arg)) ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); + ThrowIfPossiblyWrappedTypedArrayOutOfBounds(arg); return false; } @@ -89,6 +101,7 @@ function ValidateTypedArray(obj, error) { if (IsPossiblyWrappedTypedArray(obj)) { if (PossiblyWrappedTypedArrayHasDetachedBuffer(obj)) ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); + ThrowIfPossiblyWrappedTypedArrayOutOfBounds(obj); return false; } } @@ -1472,6 +1485,8 @@ function TypedArraySubarray(begin, end) { "TypedArraySubarray"); } + GetAttachedArrayBuffer(obj); + // Steps 4-6. var buffer = TypedArrayBuffer(obj); var srcLength = TypedArrayLength(obj); diff --git a/js/src/js.msg b/js/src/js.msg index 517e34c38a..d8547ae4eb 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -555,6 +555,7 @@ MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 0, JSEXN_TYPEERR, "invalid arguments") MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be >= 0") MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED, 0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer") MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS, 0, JSEXN_RANGEERR, "attempting to construct out-of-bounds TypedArray on ArrayBuffer") +MSG_DEF(JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS, 0, JSEXN_TYPEERR, "attempting to access out-of-bounds TypedArray") MSG_DEF(JSMSG_DATA_VIEW_OUT_OF_BOUNDS, 0, JSEXN_TYPEERR, "attempting to access out-of-bounds DataView") MSG_DEF(JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT, 1, JSEXN_TYPEERR, "cannot directly {0} builtin %TypedArray%") MSG_DEF(JSMSG_NON_TYPED_ARRAY_RETURNED, 0, JSEXN_TYPEERR, "constructor didn't return TypedArray object") diff --git a/js/src/tests/non262/ArrayBuffer/resizable-views.js b/js/src/tests/non262/ArrayBuffer/resizable-views.js index 590b9ce691..81e6328b67 100644 --- a/js/src/tests/non262/ArrayBuffer/resizable-views.js +++ b/js/src/tests/non262/ArrayBuffer/resizable-views.js @@ -59,6 +59,47 @@ rab.resize(6); assertEq(fixedDv.byteOffset, 4); assertEq(fixedDv.byteLength, 2); +var methodRab = new ArrayBuffer(4, { maxByteLength: 8 }); +var methodFixed = new Uint8Array(methodRab, 2, 2); +methodRab.resize(2); +[ + () => methodFixed.at(0), + () => methodFixed.copyWithin(0, 0), + () => methodFixed.entries(), + () => methodFixed.every(x => true), + () => methodFixed.fill(1), + () => methodFixed.filter(x => true), + () => methodFixed.find(x => true), + () => methodFixed.findIndex(x => true), + () => methodFixed.findLast(x => true), + () => methodFixed.findLastIndex(x => true), + () => methodFixed.forEach(x => x), + () => methodFixed.includes(0), + () => methodFixed.indexOf(0), + () => methodFixed.join(","), + () => methodFixed.keys(), + () => methodFixed.lastIndexOf(0), + () => methodFixed.map(x => x), + () => methodFixed.reduce((a, b) => a + b, 0), + () => methodFixed.reduceRight((a, b) => a + b, 0), + () => methodFixed.reverse(), + () => methodFixed.set([1], 0), + () => methodFixed.slice(), + () => methodFixed.some(x => true), + () => methodFixed.sort(), + () => methodFixed.subarray(), + () => methodFixed.toLocaleString(), + () => methodFixed.toReversed(), + () => methodFixed.toSorted(), + () => methodFixed.toString(), + () => methodFixed.values(), + () => methodFixed.with(0, 1), + () => methodFixed[Symbol.iterator](), +].forEach(fn => assertThrowsInstanceOf(fn, TypeError)); + +methodRab.resize(4); +assertEq(methodFixed.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 804e231a3f..926739e500 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1310,6 +1310,36 @@ intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer(JSContext* cx, unsigned arg return true; } +static bool +intrinsic_TypedArrayIsOutOfBounds(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + RootedObject obj(cx, &args[0].toObject()); + MOZ_ASSERT(obj->is()); + args.rval().setBoolean(obj->as().isOutOfBounds()); + return true; +} + +static bool +intrinsic_PossiblyWrappedTypedArrayIsOutOfBounds(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + JSObject* obj = CheckedUnwrap(&args[0].toObject()); + if (!obj) { + JS_ReportErrorASCII(cx, "Permission denied to access object"); + return false; + } + + MOZ_ASSERT(obj->is()); + args.rval().setBoolean(obj->as().isOutOfBounds()); + return true; +} + static bool intrinsic_MoveTypedArrayElements(JSContext* cx, unsigned argc, Value* vp) { @@ -2489,6 +2519,10 @@ static const JSFunctionSpec intrinsic_functions[] = { 1, 0, IntrinsicPossiblyWrappedTypedArrayLength), JS_FN("PossiblyWrappedTypedArrayHasDetachedBuffer", intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer, 1, 0), + JS_FN("TypedArrayIsOutOfBounds", + intrinsic_TypedArrayIsOutOfBounds, 1, 0), + JS_FN("PossiblyWrappedTypedArrayIsOutOfBounds", + intrinsic_PossiblyWrappedTypedArrayIsOutOfBounds, 1, 0), JS_FN("MoveTypedArrayElements", intrinsic_MoveTypedArrayElements, 4,0), JS_FN("SetFromTypedArrayApproach",intrinsic_SetFromTypedArrayApproach, 4, 0),