diff options
author | Linus Groh <mail@linusgroh.de> | 2021-09-05 20:21:58 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-09-05 22:17:09 +0100 |
commit | 941ff0cf601b220c8045209c15e95c658f8c2782 (patch) | |
tree | c18d11139da9ec1f2ce931b182c032fd83f5b49d | |
parent | 9998a2c91e5e6b09bcec12929bc29a170b276c10 (diff) | |
download | serenity-941ff0cf601b220c8045209c15e95c658f8c2782.zip |
LibJS: Prevent stack overflow if Proxy handler's __proto__ is the Proxy
Fixes #9322.
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/ProxyObject.cpp | 18 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js | 9 |
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"); +}); |