Skip to content

Commit e4e335c

Browse files
committed
[JSC] Read Array newTarget.prototype before an invalid-length RangeError
https://bugs.webkit.org/show_bug.cgi?id=325494 Reviewed by Yusuke Suzuki. A single numeric argument is checked with ToUint32 before GetPrototypeFromConstructor. When that length is not a safe integer index, the RangeError is thrown without reading newTarget.prototype, so a throwing getter never runs. The length is already a number here, so the conversion itself has no side effect. Read prototype, and GetFunctionRealm when the result is not an object, before throwing. * JSTests/stress/array-constructor-length-after-prototype.js: Added. * Source/JavaScriptCore/runtime/ArrayConstructor.cpp: (JSC::constructArrayWithSizeQuirk): Canonical link: https://commits.webkit.org/322282@main
1 parent 1b1238d commit e4e335c

2 files changed

Lines changed: 49 additions & 0 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
function shouldThrow(func, errorName) {
2+
try {
3+
func();
4+
} catch (e) {
5+
if (e.name === errorName)
6+
return;
7+
throw new Error("threw " + e.name + ": " + e.message + ", expected " + errorName);
8+
}
9+
throw new Error("expected " + errorName);
10+
}
11+
12+
let newTarget = new Proxy(function() {}, {
13+
get() { throw new EvalError("prototype read"); }
14+
});
15+
16+
for (let length of [-1, 1.5, 2 ** 32, Infinity])
17+
shouldThrow(() => Reflect.construct(Array, [length], newTarget), "EvalError");
18+
19+
for (let args of [[], [3], ["a"], [1, 2]])
20+
shouldThrow(() => Reflect.construct(Array, args, newTarget), "EvalError");
21+
22+
shouldThrow(() => Array(-1), "RangeError");
23+
24+
let reads = 0;
25+
let prototypeTarget = new Proxy(function() {}, {
26+
get(target, key, receiver) {
27+
reads++;
28+
if (key === "prototype")
29+
return Array.prototype;
30+
return Reflect.get(target, key, receiver);
31+
}
32+
});
33+
shouldThrow(() => Reflect.construct(Array, [-1], prototypeTarget), "RangeError");
34+
if (reads < 1)
35+
throw new Error("prototype was not read");
36+
37+
class SubArray extends Array { }
38+
let array = Reflect.construct(Array, [2], SubArray);
39+
if (!(array instanceof SubArray) || array.length !== 2)
40+
throw new Error("valid length");

‎Source/JavaScriptCore/runtime/ArrayConstructor.cpp‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ JSArray* constructArrayWithSizeQuirk(JSGlobalObject* globalObject, ArrayAllocati
8787

8888
uint32_t n = length.toUInt32(globalObject);
8989
if (n != length.toNumber(globalObject)) {
90+
if (newTarget && newTarget != globalObject->arrayConstructor()) {
91+
JSObject* class="pl-c1">asObject(newTarget);
92+
JSValue prototype = target->get(globalObject, vm.propertyNames->prototype);
93+
RETURN_IF_EXCEPTION(scope, nullptr);
94+
if (!prototype.isObject()) {
95+
getFunctionRealm(globalObject, target);
96+
RETURN_IF_EXCEPTION(scope, nullptr);
97+
}
98+
}
9099
throwException(globalObject, scope, createRangeError(globalObject, ArrayInvalidLengthError));
91100
return nullptr;
92101
}

0 commit comments

Comments
 (0)