Title: [266257] trunk
Revision
266257
Author
[email protected]
Date
2020-08-27 14:32:55 -0700 (Thu, 27 Aug 2020)

Log Message

[JSC] setLength in Array#push could get very large length
https://bugs.webkit.org/show_bug.cgi?id=215897
<rdar://problem/67859149>

Reviewed by Keith Miller.

JSTests:

* stress/array-push-more-than-max-size.js: Added.
(shouldBe):
(shouldThrow):

Source/_javascript_Core:

Array#push can get length larger than UINT32_MAX. And in this case, we should throw a RangeError.
Before r266215, it was using putLength which throws an error. But it was replaced with setLength,
and JSC::setLength assumes that it never gets a length greater than UINT32_MAX by asserting. We
should fix it so that Array#push should thrown an error correctly.

* runtime/ArrayPrototype.cpp:
(JSC::setLength):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (266256 => 266257)


--- trunk/JSTests/ChangeLog	2020-08-27 21:31:38 UTC (rev 266256)
+++ trunk/JSTests/ChangeLog	2020-08-27 21:32:55 UTC (rev 266257)
@@ -1,3 +1,15 @@
+2020-08-27  Yusuke Suzuki  <[email protected]>
+
+        [JSC] setLength in Array#push could get very large length
+        https://bugs.webkit.org/show_bug.cgi?id=215897
+        <rdar://problem/67859149>
+
+        Reviewed by Keith Miller.
+
+        * stress/array-push-more-than-max-size.js: Added.
+        (shouldBe):
+        (shouldThrow):
+
 2020-08-27  Saam Barati  <[email protected]>
 
         GetByVal constant folding over a Double OutOfBoundsSaneChain array with no BytecodeUsesAsOther should constant fold to PNaN, not undefined

Added: trunk/JSTests/stress/array-push-more-than-max-size.js (0 => 266257)


--- trunk/JSTests/stress/array-push-more-than-max-size.js	                        (rev 0)
+++ trunk/JSTests/stress/array-push-more-than-max-size.js	2020-08-27 21:32:55 UTC (rev 266257)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+let a = Array(2**32-1);
+shouldThrow(() => {
+    a.push(0, 0);
+}, `RangeError: Invalid array length`);
+shouldBe(a.length, 2**32 - 1);
+shouldBe(a[2**32], 0);
+shouldBe(a[2**32 - 1], 0);
+shouldBe(a[2**32 - 2], undefined);

Modified: trunk/Source/_javascript_Core/ChangeLog (266256 => 266257)


--- trunk/Source/_javascript_Core/ChangeLog	2020-08-27 21:31:38 UTC (rev 266256)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-08-27 21:32:55 UTC (rev 266257)
@@ -1,3 +1,19 @@
+2020-08-27  Yusuke Suzuki  <[email protected]>
+
+        [JSC] setLength in Array#push could get very large length
+        https://bugs.webkit.org/show_bug.cgi?id=215897
+        <rdar://problem/67859149>
+
+        Reviewed by Keith Miller.
+
+        Array#push can get length larger than UINT32_MAX. And in this case, we should throw a RangeError.
+        Before r266215, it was using putLength which throws an error. But it was replaced with setLength,
+        and JSC::setLength assumes that it never gets a length greater than UINT32_MAX by asserting. We
+        should fix it so that Array#push should thrown an error correctly.
+
+        * runtime/ArrayPrototype.cpp:
+        (JSC::setLength):
+
 2020-08-27  Saam Barati  <[email protected]>
 
         GetByVal constant folding over a Double OutOfBoundsSaneChain array with no BytecodeUsesAsOther should constant fold to PNaN, not undefined

Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (266256 => 266257)


--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2020-08-27 21:31:38 UTC (rev 266256)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2020-08-27 21:32:55 UTC (rev 266257)
@@ -162,14 +162,20 @@
 
 static ALWAYS_INLINE void setLength(JSGlobalObject* globalObject, VM& vm, JSObject* obj, uint64_t value)
 {
+    auto scope = DECLARE_THROW_SCOPE(vm);
     static constexpr bool throwException = true;
     if (LIKELY(isJSArray(obj))) {
-        ASSERT(static_cast<uint32_t>(value) == value);
+        if (UNLIKELY(value > UINT32_MAX)) {
+            throwRangeError(globalObject, scope, "Invalid array length"_s);
+            return;
+        }
+        scope.release();
         jsCast<JSArray*>(obj)->setLength(globalObject, static_cast<uint32_t>(value), throwException);
-    } else {
-        PutPropertySlot slot(obj, throwException);
-        obj->methodTable(vm)->put(obj, globalObject, vm.propertyNames->length, jsNumber(value), slot);
+        return;
     }
+    scope.release();
+    PutPropertySlot slot(obj, throwException);
+    obj->methodTable(vm)->put(obj, globalObject, vm.propertyNames->length, jsNumber(value), slot);
 }
 
 namespace ArrayPrototypeInternal {
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to