summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2021-09-05 20:21:58 +0100
committerLinus Groh <mail@linusgroh.de>2021-09-05 22:17:09 +0100
commit941ff0cf601b220c8045209c15e95c658f8c2782 (patch)
treec18d11139da9ec1f2ce931b182c032fd83f5b49d
parent9998a2c91e5e6b09bcec12929bc29a170b276c10 (diff)
downloadserenity-941ff0cf601b220c8045209c15e95c658f8c2782.zip
LibJS: Prevent stack overflow if Proxy handler's __proto__ is the Proxy
Fixes #9322.
-rw-r--r--Userland/Libraries/LibJS/Runtime/ProxyObject.cpp18
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js9
2 files changed, 27 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp b/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp
index 803ba3dd74..f8db9de87d 100644
--- a/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp
+++ b/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp
@@ -577,6 +577,24 @@ Value ProxyObject::internal_get(PropertyName const& property_name, Value receive
// 4. Assert: Type(handler) is Object.
// 5. Let target be O.[[ProxyTarget]].
+ // NOTE: We need to protect ourselves from a Proxy with the handler's prototype set to the
+ // Proxy itself, which would by default bounce between these functions indefinitely and lead to
+ // a stack overflow when the Proxy's (p) or Proxy handler's (h) Object::get() is called and the
+ // handler doesn't have a `get` trap:
+ //
+ // 1. p -> ProxyObject::internal_get() <- you are here
+ // 2. h -> Value::get_method()
+ // 3. h -> Value::get()
+ // 4. h -> Object::internal_get()
+ // 5. h -> Object::internal_get_prototype_of() (result is p)
+ // 6. goto 1
+ //
+ // In JS code: `h = {}; p = new Proxy({}, h); h.__proto__ = p; p.foo // or h.foo`
+ if (vm.did_reach_stack_space_limit()) {
+ vm.throw_exception<Error>(global_object, ErrorType::CallStackSizeExceeded);
+ return {};
+ }
+
// 6. Let trap be ? GetMethod(handler, "get").
auto trap = Value(&m_handler).get_method(global_object, vm.names.get);
if (vm.exception())
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js b/Userland/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js
index 580254c3bc..acc251b1c7 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js
@@ -107,3 +107,12 @@ describe("[[Get]] invariants", () => {
);
});
});
+
+test("Proxy handler that has the Proxy itself as its prototype", () => {
+ const handler = {};
+ const proxy = new Proxy({}, handler);
+ handler.__proto__ = proxy;
+ expect(() => {
+ proxy.foo;
+ }).toThrowWithMessage(Error, "Call stack size limit exceeded");
+});