LibJS: Prevent stack overflow if Proxy handler's __proto__ is the Proxy

Fixes #9322.
This commit is contained in:
Linus Groh 2021-09-05 20:21:58 +01:00
parent 9998a2c91e
commit 941ff0cf60
Notes: sideshowbarker 2024-07-18 04:39:55 +09:00
2 changed files with 27 additions and 0 deletions

View file

@ -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())

View file

@ -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");
});