diff --git a/js/src/js.msg b/js/src/js.msg index 7a60a6119..97ccb6feb 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -559,6 +559,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_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") MSG_DEF(JSMSG_SHORT_TYPED_ARRAY_RETURNED, 2, JSEXN_TYPEERR, "expected TypedArray of at least length {0}, but constructor returned TypedArray of length {1}") diff --git a/js/src/tests/non262/ArrayBuffer/resizable-views.js b/js/src/tests/non262/ArrayBuffer/resizable-views.js index 40cb0260b..590b9ce69 100644 --- a/js/src/tests/non262/ArrayBuffer/resizable-views.js +++ b/js/src/tests/non262/ArrayBuffer/resizable-views.js @@ -41,9 +41,9 @@ dv.setUint8(0, 44); assertEq(tracking[4], 44); rab.resize(3); -assertEq(dv.byteOffset, 0); -assertEq(dv.byteLength, 0); -assertThrowsInstanceOf(() => dv.getUint8(0), RangeError); +assertThrowsInstanceOf(() => dv.byteOffset, TypeError); +assertThrowsInstanceOf(() => dv.byteLength, TypeError); +assertThrowsInstanceOf(() => dv.getUint8(0), TypeError); rab.resize(6); assertEq(dv.byteOffset, 4); @@ -52,12 +52,40 @@ assertEq(dv.getUint8(0), 0); var fixedDv = new DataView(rab, 4, 2); rab.resize(5); -assertEq(fixedDv.byteOffset, 0); -assertEq(fixedDv.byteLength, 0); +assertThrowsInstanceOf(() => fixedDv.byteOffset, TypeError); +assertThrowsInstanceOf(() => fixedDv.byteLength, TypeError); +assertThrowsInstanceOf(() => fixedDv.getUint8(0), TypeError); rab.resize(6); assertEq(fixedDv.byteOffset, 4); assertEq(fixedDv.byteLength, 2); +var ctorRab = new ArrayBuffer(8, { maxByteLength: 8 }); +var ShrinkingNewTarget = new Proxy(function() {}, { + get(target, prop, receiver) { + if (prop === "prototype") { + ctorRab.resize(2); + return DataView.prototype; + } + return Reflect.get(target, prop, receiver); + } +}); +assertThrowsInstanceOf(() => Reflect.construct(DataView, [ctorRab, 4], ShrinkingNewTarget), + RangeError); + +var fixedCtorRab = new ArrayBuffer(8, { maxByteLength: 8 }); +var FixedShrinkingNewTarget = new Proxy(function() {}, { + get(target, prop, receiver) { + if (prop === "prototype") { + fixedCtorRab.resize(5); + return DataView.prototype; + } + return Reflect.get(target, prop, receiver); + } +}); +assertThrowsInstanceOf(() => Reflect.construct(DataView, [fixedCtorRab, 4, 2], + FixedShrinkingNewTarget), + RangeError); + var gsab = new SharedArrayBuffer(4, { maxByteLength: 16 }); var sharedTracking = new Uint8Array(gsab); assertEq(sharedTracking.length, 4); diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index d3af69120..dd26b4cd4 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -1885,7 +1885,18 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, MOZ_ASSERT(byteOffset <= INT32_MAX); MOZ_ASSERT(byteLength <= INT32_MAX); - MOZ_ASSERT(byteOffset + byteLength < UINT32_MAX); + + uint32_t bufferByteLength = arrayBuffer->byteLength(); + if (byteOffset > bufferByteLength || + (!lengthTracking && byteLength > bufferByteLength - byteOffset)) + { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, + "1"); + return nullptr; + } + + if (lengthTracking) + byteLength = bufferByteLength - byteOffset; RootedObject proto(cx, protoArg); RootedObject obj(cx); @@ -1909,9 +1920,6 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, } } - // Caller should have established these preconditions, and no - // (non-self-hosted) JS code has had an opportunity to run so nothing can - // have invalidated them. MOZ_ASSERT(byteOffset <= arrayBuffer->byteLength()); MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength()); @@ -2171,6 +2179,11 @@ template DataViewObject::getDataPointer(JSContext* cx, Handle obj, uint64_t offset, bool* isSharedMemory) { + if (obj->isOutOfBounds()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATA_VIEW_OUT_OF_BOUNDS); + return SharedMem::unshared(nullptr); + } + const size_t TypeSize = sizeof(NativeType); if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, @@ -3208,7 +3221,13 @@ template bool DataViewObject::getterImpl(JSContext* cx, const CallArgs& args) { - args.rval().set(ValueGetter(&args.thisv().toObject().as())); + Rooted view(cx, &args.thisv().toObject().as()); + if (ValueGetter != bufferValue && view->isOutOfBounds()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATA_VIEW_OUT_OF_BOUNDS); + return false; + } + + args.rval().set(ValueGetter(view)); return true; }