Title: [250860] trunk
Revision
250860
Author
commit-qu...@webkit.org
Date
2019-10-08 14:23:14 -0700 (Tue, 08 Oct 2019)

Log Message

JSON.parse incorrectly handles array proxies
https://bugs.webkit.org/show_bug.cgi?id=199292

Patch by Alexey Shvayka <shvaikal...@gmail.com> on 2019-10-08
Reviewed by Saam Barati.

JSTests:

* microbenchmarks/json-parse-array-reviver-same-value.js: Added.
* microbenchmarks/json-parse-array-reviver.js: Added.
* microbenchmarks/json-parse-object-reviver-same-value.js: Added.
* microbenchmarks/json-parse-object-reviver.js: Added.
* stress/json-parse-reviver-array-proxy.js: Added.
* stress/json-parse-reviver-revoked-proxy.js: Added.
* test262/expectations.yaml: Mark 6 test cases as passing.

Source/_javascript_Core:

1. Use isArray to correctly detect proxied arrays.
2. Make "length" lookup observable to array proxies and handle exceptions.

* runtime/JSONObject.cpp:
(JSC::Walker::walk):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (250859 => 250860)


--- trunk/JSTests/ChangeLog	2019-10-08 21:09:17 UTC (rev 250859)
+++ trunk/JSTests/ChangeLog	2019-10-08 21:23:14 UTC (rev 250860)
@@ -1,3 +1,18 @@
+2019-10-08  Alexey Shvayka  <shvaikal...@gmail.com>
+
+        JSON.parse incorrectly handles array proxies
+        https://bugs.webkit.org/show_bug.cgi?id=199292
+
+        Reviewed by Saam Barati.
+
+        * microbenchmarks/json-parse-array-reviver-same-value.js: Added.
+        * microbenchmarks/json-parse-array-reviver.js: Added.
+        * microbenchmarks/json-parse-object-reviver-same-value.js: Added.
+        * microbenchmarks/json-parse-object-reviver.js: Added.
+        * stress/json-parse-reviver-array-proxy.js: Added.
+        * stress/json-parse-reviver-revoked-proxy.js: Added.
+        * test262/expectations.yaml: Mark 6 test cases as passing.
+
 2019-10-08  Ross Kirsling  <ross.kirsl...@sony.com>
 
         Update test262 (2019.10.08).

Added: trunk/JSTests/microbenchmarks/json-parse-array-reviver-same-value.js (0 => 250860)


--- trunk/JSTests/microbenchmarks/json-parse-array-reviver-same-value.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/json-parse-array-reviver-same-value.js	2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,3 @@
+const json = JSON.stringify([0,1,2,3,4,5,6,7,8,9]);
+for (let i = 0; i < 1e5; ++i)
+    JSON.parse(json, (_key, val) => val);

Added: trunk/JSTests/microbenchmarks/json-parse-array-reviver.js (0 => 250860)


--- trunk/JSTests/microbenchmarks/json-parse-array-reviver.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/json-parse-array-reviver.js	2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,3 @@
+const json = JSON.stringify([0,1,2,3,4,5,6,7,8,9]);
+for (let i = 0; i < 1e5; ++i)
+    JSON.parse(json, (_key, val) => val + 1);

Added: trunk/JSTests/microbenchmarks/json-parse-object-reviver-same-value.js (0 => 250860)


--- trunk/JSTests/microbenchmarks/json-parse-object-reviver-same-value.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/json-parse-object-reviver-same-value.js	2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,3 @@
+const json = JSON.stringify({a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7,i:8,j:9});
+for (let i = 0; i < 1e5; ++i)
+    JSON.parse(json, (_key, val) => val);

Added: trunk/JSTests/microbenchmarks/json-parse-object-reviver.js (0 => 250860)


--- trunk/JSTests/microbenchmarks/json-parse-object-reviver.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/json-parse-object-reviver.js	2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,3 @@
+const json = JSON.stringify({a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7,i:8,j:9});
+for (let i = 0; i < 1e5; ++i)
+    JSON.parse(json, (_key, val) => val + 1);

Added: trunk/JSTests/stress/json-parse-reviver-array-proxy.js (0 => 250860)


--- trunk/JSTests/stress/json-parse-reviver-array-proxy.js	                        (rev 0)
+++ trunk/JSTests/stress/json-parse-reviver-array-proxy.js	2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+const json = '{"a": 1, "b": 2}';
+
+for (let i = 1; i < 10000; i++) {
+    let keys = [];
+    let proxy = new Proxy([2, 3], {
+        get: function(target, key) {
+            keys.push(key);
+            return target[key];
+        },
+        ownKeys: function() {
+            throw new Error('[[OwnPropertyKeys]] should not be called');
+        },
+    });
+
+    let result = JSON.parse(json, function(key, value) {
+        if (key === 'a')
+            this.b = proxy;
+        return value;
+    });
+
+    shouldBe(keys.toString(), 'length,0,1');
+    shouldBe(JSON.stringify(result), '{"a":1,"b":[2,3]}');
+}

Added: trunk/JSTests/stress/json-parse-reviver-revoked-proxy.js (0 => 250860)


--- trunk/JSTests/stress/json-parse-reviver-revoked-proxy.js	                        (rev 0)
+++ trunk/JSTests/stress/json-parse-reviver-revoked-proxy.js	2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,48 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function revivedObjProxy(key, value) {
+    if (key === 'a') {
+        let {proxy, revoke} = Proxy.revocable({}, {});
+        revoke();
+        this.b = proxy;
+    }
+
+    return value;
+}
+
+function revivedArrProxy(key, value) {
+    if (key === '0') {
+        let {proxy, revoke} = Proxy.revocable([], {});
+        revoke();
+        this[1] = proxy;
+    }
+
+    return value;
+}
+
+const objJSON = '{"a": 1, "b": 2}';
+const arrJSON = '[3, 4]';
+const isArrayError = 'TypeError: Array.isArray cannot be called on a Proxy that has been revoked';
+
+for (let i = 1; i < 10000; i++) {
+    let error;
+    try {
+        JSON.parse(objJSON, revivedObjProxy);
+    } catch (e) {
+        error = e;
+    }
+    shouldBe(error.toString(), isArrayError);
+}
+
+for (let i = 1; i < 10000; i++) {
+    let error;
+    try {
+        JSON.parse(arrJSON, revivedArrProxy);
+    } catch (e) {
+        error = e;
+    }
+    shouldBe(error.toString(), isArrayError);
+}

Modified: trunk/JSTests/test262/expectations.yaml (250859 => 250860)


--- trunk/JSTests/test262/expectations.yaml	2019-10-08 21:09:17 UTC (rev 250859)
+++ trunk/JSTests/test262/expectations.yaml	2019-10-08 21:23:14 UTC (rev 250860)
@@ -1206,15 +1206,6 @@
 test/built-ins/GeneratorFunction/proto-from-ctor-realm.js:
   default: 'Test262Error: Expected SameValue(«[object GeneratorFunction]», «[object GeneratorFunction]») to be true'
   strict mode: 'Test262Error: Expected SameValue(«[object GeneratorFunction]», «[object GeneratorFunction]») to be true'
-test/built-ins/JSON/parse/revived-proxy.js:
-  default: 'Test262Error: proxy for array Expected SameValue(«true», «false») to be true'
-  strict mode: 'Test262Error: proxy for array Expected SameValue(«true», «false») to be true'
-test/built-ins/JSON/parse/reviver-array-length-coerce-err.js:
-  default: 'Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all'
-  strict mode: 'Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all'
-test/built-ins/JSON/parse/reviver-array-length-get-err.js:
-  default: 'Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all'
-  strict mode: 'Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all'
 test/built-ins/Map/proto-from-ctor-realm.js:
   default: 'Test262Error: Expected SameValue(«[object Map]», «[object Map]») to be true'
   strict mode: 'Test262Error: Expected SameValue(«[object Map]», «[object Map]») to be true'

Modified: trunk/Source/_javascript_Core/ChangeLog (250859 => 250860)


--- trunk/Source/_javascript_Core/ChangeLog	2019-10-08 21:09:17 UTC (rev 250859)
+++ trunk/Source/_javascript_Core/ChangeLog	2019-10-08 21:23:14 UTC (rev 250860)
@@ -1,3 +1,16 @@
+2019-10-08  Alexey Shvayka  <shvaikal...@gmail.com>
+
+        JSON.parse incorrectly handles array proxies
+        https://bugs.webkit.org/show_bug.cgi?id=199292
+
+        Reviewed by Saam Barati.
+
+        1. Use isArray to correctly detect proxied arrays.
+        2. Make "length" lookup observable to array proxies and handle exceptions.
+
+        * runtime/JSONObject.cpp:
+        (JSC::Walker::walk):
+
 2019-10-08  Adrian Perez de Castro  <ape...@igalia.com>
 
         [GTK][WPE] Fix non-unified builds after r250486

Modified: trunk/Source/_javascript_Core/runtime/JSONObject.cpp (250859 => 250860)


--- trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2019-10-08 21:09:17 UTC (rev 250859)
+++ trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2019-10-08 21:23:14 UTC (rev 250860)
@@ -32,6 +32,7 @@
 #include "Error.h"
 #include "ExceptionHelpers.h"
 #include "JSArray.h"
+#include "JSArrayInlines.h"
 #include "JSGlobalObject.h"
 #include "LiteralParser.h"
 #include "Lookup.h"
@@ -663,19 +664,21 @@
             arrayStartState:
             case ArrayStartState: {
                 ASSERT(inValue.isObject());
-                ASSERT(asObject(inValue)->inherits<JSArray>(vm));
+                ASSERT(isJSArray(inValue) || inValue.inherits<ProxyObject>(vm));
                 if (markedStack.size() > maximumFilterRecursion)
                     return throwStackOverflowError(m_exec, scope);
 
-                JSArray* array = asArray(inValue);
+                JSObject* array = asObject(inValue);
                 markedStack.appendWithCrashOnOverflow(array);
-                arrayLengthStack.append(array->length());
+                unsigned length = toLength(m_exec, array);
+                RETURN_IF_EXCEPTION(scope, { });
+                arrayLengthStack.append(length);
                 indexStack.append(0);
             }
             arrayStartVisitMember:
             FALLTHROUGH;
             case ArrayStartVisitMember: {
-                JSArray* array = jsCast<JSArray*>(markedStack.last());
+                JSObject* array = asObject(markedStack.last());
                 uint32_t index = indexStack.last();
                 unsigned arrayLength = arrayLengthStack.last();
                 if (index == arrayLength) {
@@ -704,7 +707,7 @@
                 FALLTHROUGH;
             }
             case ArrayEndVisitMember: {
-                JSArray* array = jsCast<JSArray*>(markedStack.last());
+                JSObject* array = asObject(markedStack.last());
                 JSValue filteredValue = callReviver(array, jsString(vm, String::number(indexStack.last())), outValue);
                 RETURN_IF_EXCEPTION(scope, { });
                 if (filteredValue.isUndefined())
@@ -718,7 +721,7 @@
             objectStartState:
             case ObjectStartState: {
                 ASSERT(inValue.isObject());
-                ASSERT(!asObject(inValue)->inherits<JSArray>(vm));
+                ASSERT(!isJSArray(inValue));
                 if (markedStack.size() > maximumFilterRecursion)
                     return throwStackOverflowError(m_exec, scope);
 
@@ -778,8 +781,9 @@
                     outValue = inValue;
                     break;
                 }
-                JSObject* object = asObject(inValue);
-                if (object->inherits<JSArray>(vm))
+                bool valueIsArray = isArray(m_exec, inValue);
+                RETURN_IF_EXCEPTION(scope, { });
+                if (valueIsArray)
                     goto arrayStartState;
                 goto objectStartState;
         }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to