Validate typed array set and constructors on resizable buffers

This commit is contained in:
Basilisk-Dev 2026-05-15 21:15:01 -04:00 • committed by wuggy
commit d8e52b4fce
5 changed files with 77 additions and 11 deletions

View file

@ -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);
}

View file

@ -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) {

View file

@ -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();

View file

@ -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<TypedArrayObject>()) {
if (arg0->as<TypedArrayObject>().length() > target->length() - offset) {
Rooted<TypedArrayObject*> source(cx, &arg0->as<TypedArrayObject>());
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<SomeTypedArray*> target, HandleObject source,
uint32_t offset = 0)
{
MOZ_ASSERT(source->is<TypedArrayObject>(), "use setFromNonTypedArray");
static bool
setFromTypedArray(JSContext* cx, Handle<SomeTypedArray*> target, HandleObject source,
uint32_t offset = 0)
{
MOZ_ASSERT(source->is<TypedArrayObject>(), "use setFromNonTypedArray");
bool isShared = target->isSharedMemory() || source->as<TypedArrayObject>().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<TypedArrayObject*> sourceArray(cx, &source->as<TypedArrayObject>());
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:

View file

@ -1337,6 +1337,10 @@ TypedArrayObjectTemplate<T>::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();