[JSC] getFunctionRealm should not use recursion
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 16 May 2020 01:23:40 +0000 (01:23 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 16 May 2020 01:23:40 +0000 (01:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=211965
<rdar://problem/63268287>

Reviewed by Saam Barati.

JSTests:

* stress/get-function-realm-not-doing-recursion.js: Added.
(canThrow):
(const.emptyFunction):

Source/JavaScriptCore:

This patch avoids using recursion in getFunctionRealm to avoid stack-overflow.

* runtime/InternalFunction.cpp:
(JSC::getFunctionRealm):

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

JSTests/ChangeLog
JSTests/stress/get-function-realm-not-doing-recursion.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/InternalFunction.cpp

index 18bc7c6..b4a5189 100644 (file)
@@ -1,3 +1,15 @@
+2020-05-15  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] getFunctionRealm should not use recursion
+        https://bugs.webkit.org/show_bug.cgi?id=211965
+        <rdar://problem/63268287>
+
+        Reviewed by Saam Barati.
+
+        * stress/get-function-realm-not-doing-recursion.js: Added.
+        (canThrow):
+        (const.emptyFunction):
+
 2020-05-15  Paulo Matos  <pmatos@igalia.com>
 
         Skip tests in ARM and MIPS post r261667
diff --git a/JSTests/stress/get-function-realm-not-doing-recursion.js b/JSTests/stress/get-function-realm-not-doing-recursion.js
new file mode 100644 (file)
index 0000000..a79ef2c
--- /dev/null
@@ -0,0 +1,31 @@
+//@ skip if $buildType != "debug"
+//@ runDefault("--useConcurrentJIT=0")
+
+function canThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+        print(error.message);
+    }
+    if (errorThrown && String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+    return false;
+}
+
+const emptyFunction = function() {};
+
+function makeLongProxyChain() {
+  let p = new Proxy(emptyFunction, {});
+  for (let i = 0; i < 200000; i++)
+    p = new Proxy(p, {});
+  return p;
+}
+
+let p = makeLongProxyChain();
+canThrow(() => {
+    Reflect.construct(Object, [], p);
+}, `RangeError: Maximum call stack size exceeded.`);
index a90deb6..fbf4b61 100644 (file)
@@ -1,3 +1,16 @@
+2020-05-15  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] getFunctionRealm should not use recursion
+        https://bugs.webkit.org/show_bug.cgi?id=211965
+        <rdar://problem/63268287>
+
+        Reviewed by Saam Barati.
+
+        This patch avoids using recursion in getFunctionRealm to avoid stack-overflow.
+
+        * runtime/InternalFunction.cpp:
+        (JSC::getFunctionRealm):
+
 2020-05-15  Keith Miller  <keith_miller@apple.com>
 
         Unreviewed, fix internal arm64e build.
index 016ca9a..e47c8a8 100644 (file)
@@ -157,20 +157,26 @@ JSGlobalObject* getFunctionRealm(VM& vm, JSObject* object)
 {
     ASSERT(object->isCallable(vm));
 
-    if (object->inherits<JSBoundFunction>(vm))
-        return getFunctionRealm(vm, jsCast<JSBoundFunction*>(object)->targetFunction());
-
-    if (object->type() == ProxyObjectType) {
-        auto* proxy = jsCast<ProxyObject*>(object);
-        // Per step 4.a, a TypeError should be thrown for revoked Proxy, yet we skip it since:
-        // a) It is barely observable anyway: "prototype" lookup in createSubclassStructure() will throw for revoked Proxy.
-        // b) Throwing getFunctionRealm() will restrict calling it inline as an argument of createSubclassStructure().
-        // c) There is ongoing discussion on removing it: https://github.com/tc39/ecma262/issues/1798.
-        if (!proxy->isRevoked())
-            return getFunctionRealm(vm, proxy->target());
-    }
+    while (true) {
+        if (object->inherits<JSBoundFunction>(vm)) {
+            object = jsCast<JSBoundFunction*>(object)->targetFunction();
+            continue;
+        }
 
-    return object->globalObject(vm);
+        if (object->type() == ProxyObjectType) {
+            auto* proxy = jsCast<ProxyObject*>(object);
+            // Per step 4.a, a TypeError should be thrown for revoked Proxy, yet we skip it since:
+            // a) It is barely observable anyway: "prototype" lookup in createSubclassStructure() will throw for revoked Proxy.
+            // b) Throwing getFunctionRealm() will restrict calling it inline as an argument of createSubclassStructure().
+            // c) There is ongoing discussion on removing it: https://github.com/tc39/ecma262/issues/1798.
+            if (!proxy->isRevoked()) {
+                object = proxy->target();
+                continue;
+            }
+        }
+
+        return object->globalObject(vm);
+    }
 }