[JSC] JSObject::attemptToInterceptPutByIndexOnHole should use getPrototype instead...
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Jun 2019 18:27:46 +0000 (18:27 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Jun 2019 18:27:46 +0000 (18:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=198477
<rdar://problem/51299504>

Reviewed by Saam Barati.

Source/JavaScriptCore:

JSObject::attemptToInterceptPutByIndexOnHole uses getPrototypeDirect, but it should use getPrototype to
handle getPrototype methods in derived JSObject classes correctly.

* runtime/JSArrayInlines.h:
(JSC::JSArray::pushInline):
* runtime/JSObject.cpp:
(JSC::JSObject::putByIndex):
(JSC::JSObject::attemptToInterceptPutByIndexOnHoleForPrototype):
(JSC::JSObject::attemptToInterceptPutByIndexOnHole):
(JSC::JSObject::putByIndexBeyondVectorLength):

LayoutTests:

Ensure that JSWindow::getPrototype is used.

* http/tests/security/cross-frame-access-object-getPrototypeOf-in-put-expected.txt: Added.
* http/tests/security/cross-frame-access-object-getPrototypeOf-in-put.html: Added.
* http/tests/security/resources/cross-frame-iframe-for-object-getPrototypeOf-in-put-test.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@246040 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/http/tests/security/cross-frame-access-object-getPrototypeOf-in-put-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/cross-frame-access-object-getPrototypeOf-in-put.html [new file with mode: 0644]
LayoutTests/http/tests/security/resources/cross-frame-iframe-for-object-getPrototypeOf-in-put-test.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/JSArrayInlines.h
Source/JavaScriptCore/runtime/JSObject.cpp

index 63029f8..ea4ebab 100644 (file)
@@ -1,3 +1,17 @@
+2019-06-03  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] JSObject::attemptToInterceptPutByIndexOnHole should use getPrototype instead of getPrototypeDirect
+        https://bugs.webkit.org/show_bug.cgi?id=198477
+        <rdar://problem/51299504>
+
+        Reviewed by Saam Barati.
+
+        Ensure that JSWindow::getPrototype is used.
+
+        * http/tests/security/cross-frame-access-object-getPrototypeOf-in-put-expected.txt: Added.
+        * http/tests/security/cross-frame-access-object-getPrototypeOf-in-put.html: Added.
+        * http/tests/security/resources/cross-frame-iframe-for-object-getPrototypeOf-in-put-test.html: Added.
+
 2019-06-03  Devin Rousso  <drousso@apple.com>
 
         Flaky Test: inspector/canvas/recording.html
diff --git a/LayoutTests/http/tests/security/cross-frame-access-object-getPrototypeOf-in-put-expected.txt b/LayoutTests/http/tests/security/cross-frame-access-object-getPrototypeOf-in-put-expected.txt
new file mode 100644 (file)
index 0000000..6d5bca1
--- /dev/null
@@ -0,0 +1,6 @@
+This tests that you can't get the prototype of the window during [[Put]] operation.
+
+PASS: successfullyParsed should be 'true' and is.
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/security/cross-frame-access-object-getPrototypeOf-in-put.html b/LayoutTests/http/tests/security/cross-frame-access-object-getPrototypeOf-in-put.html
new file mode 100644 (file)
index 0000000..1dfe135
--- /dev/null
@@ -0,0 +1,42 @@
+<html>
+<head>
+    <script src="/js-test-resources/js-test-pre.js"></script>
+    <script src="resources/cross-frame-access.js"></script>
+    <script>
+        jsTestIsAsync = true;
+
+        // Set up listener for message from iframe
+        addEventListener('message', function(event) {
+            if (event.data == "finishedLoad")
+                doTest();
+        }, false);
+
+
+        doTest = function()
+        {
+            targetWindow = document.getElementById("target").contentWindow;
+            var array = [];
+            array.__proto__.__proto__ = targetWindow;
+            array[0] = 11.11;
+            array[2] = 22.22;
+            array[10101010] = {
+                toString() {
+                    testFailed("toString is called by 10101010 setter");
+                }
+            };
+            array["cocoa"] = {
+                toString() {
+                    testFailed("toString is called by cocoa setter");
+                }
+            };
+            finishJSTest();
+        }
+    </script>
+</head>
+<body>
+    <div>This tests that you can't get the prototype of the window during [[Put]] operation.</div>
+    <iframe id="target" src="http://localhost:8000/security/resources/cross-frame-iframe-for-object-getPrototypeOf-in-put-test.html"></iframe>
+    <pre id="console"></pre>
+    <script src="/js-test-resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/resources/cross-frame-iframe-for-object-getPrototypeOf-in-put-test.html b/LayoutTests/http/tests/security/resources/cross-frame-iframe-for-object-getPrototypeOf-in-put-test.html
new file mode 100644 (file)
index 0000000..1d6f821
--- /dev/null
@@ -0,0 +1,23 @@
+<html>
+<head>
+    <script>
+        onload = function()
+        {
+            Object.defineProperty(Object.prototype, 10101010, {
+                set: function (v) {
+                    return 'a' + v;
+                }
+            });
+            Object.defineProperty(Object.prototype, "cocoa", {
+                set: function (v) {
+                    return 'a' + v;
+                }
+            });
+            parent.postMessage("finishedLoad", "*");
+        }
+    </script>
+</head>
+<body>
+    Body
+</body>
+</html>
index 312079d..b6b428c 100644 (file)
@@ -1,3 +1,22 @@
+2019-06-03  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] JSObject::attemptToInterceptPutByIndexOnHole should use getPrototype instead of getPrototypeDirect
+        https://bugs.webkit.org/show_bug.cgi?id=198477
+        <rdar://problem/51299504>
+
+        Reviewed by Saam Barati.
+
+        JSObject::attemptToInterceptPutByIndexOnHole uses getPrototypeDirect, but it should use getPrototype to
+        handle getPrototype methods in derived JSObject classes correctly.
+
+        * runtime/JSArrayInlines.h:
+        (JSC::JSArray::pushInline):
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::putByIndex):
+        (JSC::JSObject::attemptToInterceptPutByIndexOnHoleForPrototype):
+        (JSC::JSObject::attemptToInterceptPutByIndexOnHole):
+        (JSC::JSObject::putByIndexBeyondVectorLength):
+
 2019-06-03  Don Olmstead  <don.olmstead@sony.com>
 
         [CMake] Add WebKit::JavaScriptCore target
index af718dc..dbe1a4e 100644 (file)
@@ -212,8 +212,10 @@ ALWAYS_INLINE void JSArray::pushInline(ExecState* exec, JSValue value)
     case ArrayWithSlowPutArrayStorage: {
         unsigned oldLength = length();
         bool putResult = false;
-        if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true, putResult)) {
-            if (!scope.exception() && oldLength < 0xFFFFFFFFu) {
+        bool result = attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true, putResult);
+        RETURN_IF_EXCEPTION(scope, void());
+        if (result) {
+            if (oldLength < 0xFFFFFFFFu) {
                 scope.release();
                 setLength(exec, oldLength + 1, true);
             }
index a0c0b20..1cb7ee0 100644 (file)
@@ -943,18 +943,24 @@ bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName,
         
         WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
         unsigned length = storage->length();
+
+        auto scope = DECLARE_THROW_SCOPE(vm);
         
         // Update length & m_numValuesInVector as necessary.
         if (propertyName >= length) {
             bool putResult = false;
-            if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
+            bool result = thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult);
+            RETURN_IF_EXCEPTION(scope, false);
+            if (result)
                 return putResult;
             length = propertyName + 1;
             storage->setLength(length);
             ++storage->m_numValuesInVector;
         } else if (!valueSlot) {
             bool putResult = false;
-            if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
+            bool result = thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult);
+            RETURN_IF_EXCEPTION(scope, false);
+            if (result)
                 return putResult;
             ++storage->m_numValuesInVector;
         }
@@ -2716,6 +2722,8 @@ void JSObject::deallocateSparseIndexMap()
 bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, JSValue thisValue, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
 {
     VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     for (JSObject* current = this; ;) {
         // This has the same behavior with respect to prototypes as JSObject::put(). It only
         // allows a prototype to intercept a put if (a) the prototype declares the property
@@ -2726,18 +2734,21 @@ bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, J
         if (storage && storage->m_sparseMap) {
             SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i);
             if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes() & (PropertyAttribute::Accessor | PropertyAttribute::ReadOnly))) {
+                scope.release();
                 putResult = iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow);
                 return true;
             }
         }
 
         if (current->type() == ProxyObjectType) {
+            scope.release();
             ProxyObject* proxy = jsCast<ProxyObject*>(current);
             putResult = proxy->putByIndexCommon(exec, thisValue, i, value, shouldThrow);
             return true;
         }
         
-        JSValue prototypeValue = current->getPrototypeDirect(vm);
+        JSValue prototypeValue = current->getPrototype(vm, exec);
+        RETURN_IF_EXCEPTION(scope, false);
         if (prototypeValue.isNull())
             return false;
         
@@ -2747,11 +2758,15 @@ bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, J
 
 bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
 {
-    JSValue prototypeValue = getPrototypeDirect(exec->vm());
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue prototypeValue = getPrototype(vm, exec);
+    RETURN_IF_EXCEPTION(scope, false);
     if (prototypeValue.isNull())
         return false;
     
-    return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow, putResult);
+    RELEASE_AND_RETURN(scope, asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow, putResult));
 }
 
 template<IndexingType indexingShape>
@@ -2937,10 +2952,16 @@ bool JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue
     case NonArrayWithSlowPutArrayStorage:
     case ArrayWithSlowPutArrayStorage: {
         // No own property present in the vector, but there might be in the sparse map!
+        auto scope = DECLARE_THROW_SCOPE(vm);
         SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
         bool putResult = false;
-        if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow, putResult))
-            return putResult;
+        if (!(map && map->contains(i))) {
+            bool result = attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow, putResult);
+            RETURN_IF_EXCEPTION(scope, false);
+            if (result)
+                return putResult;
+        }
+        scope.release();
         FALLTHROUGH;
     }