mirror of
https://github.com/RGBCube/serenity
synced 2025-07-03 05:32:13 +00:00
LibJS: Guard against stack overflow in ProxyObject set_property()
For similar reason as in the previous commit.
This commit is contained in:
parent
52a5a42147
commit
5d2e915623
2 changed files with 34 additions and 1 deletions
|
@ -470,7 +470,7 @@ ThrowCompletionOr<Value> ProxyObject::internal_get(PropertyKey const& property_k
|
||||||
// 3. Assert: Type(handler) is Object.
|
// 3. Assert: Type(handler) is Object.
|
||||||
// 4. Let target be O.[[ProxyTarget]].
|
// 4. Let target be O.[[ProxyTarget]].
|
||||||
|
|
||||||
// NOTE: We need to protect ourselves from a Proxy with the handler's prototype set to the
|
// NOTE: We need to protect ourselves from a Proxy with its (or handler's) prototype set to the
|
||||||
// Proxy itself, which would by default bounce between these functions indefinitely and lead to
|
// 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
|
// 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:
|
// handler doesn't have a `get` trap:
|
||||||
|
@ -539,6 +539,22 @@ ThrowCompletionOr<bool> ProxyObject::internal_set(PropertyKey const& property_ke
|
||||||
// 3. Assert: Type(handler) is Object.
|
// 3. Assert: Type(handler) is Object.
|
||||||
// 4. Let target be O.[[ProxyTarget]].
|
// 4. Let target be O.[[ProxyTarget]].
|
||||||
|
|
||||||
|
// NOTE: We need to protect ourselves from a Proxy with its 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 `has` trap:
|
||||||
|
//
|
||||||
|
// 1. p -> ProxyObject::internal_set() <- you are here
|
||||||
|
// 2. target -> Object::internal_set()
|
||||||
|
// 3. target -> Object::ordinary_set_with_own_descriptor()
|
||||||
|
// 4. target.[[Prototype]] -> Object::internal_set()
|
||||||
|
// 5. target.[[Prototype]] -> Object::ordinary_set_with_own_descriptor()
|
||||||
|
// 6. target.[[Prototype]].[[Prototype]] (which is ProxyObject) -> Object::internal_set()
|
||||||
|
//
|
||||||
|
// In JS code: `const proxy = new Proxy({}, {}); proxy.__proto__ = Object.create(proxy); proxy["foo"] = "bar";`
|
||||||
|
if (vm.did_reach_stack_space_limit())
|
||||||
|
return vm.throw_completion<InternalError>(ErrorType::CallStackSizeExceeded);
|
||||||
|
|
||||||
// 5. Let trap be ? GetMethod(handler, "set").
|
// 5. Let trap be ? GetMethod(handler, "set").
|
||||||
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.set));
|
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.set));
|
||||||
|
|
||||||
|
|
|
@ -112,3 +112,20 @@ describe("[[Set]] 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"] = "bar";
|
||||||
|
}).toThrowWithMessage(InternalError, "Call stack size limit exceeded");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Proxy that has the Proxy itself as its prototype", () => {
|
||||||
|
const proxy = new Proxy({}, {});
|
||||||
|
proxy.__proto__ = Object.create(proxy);
|
||||||
|
expect(() => {
|
||||||
|
proxy["foo"] = "bar";
|
||||||
|
}).toThrowWithMessage(InternalError, "Call stack size limit exceeded");
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue