소스 검색

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

Fixes #9322.
Linus Groh 3 년 전
부모
커밋
941ff0cf60
2개의 변경된 파일27개의 추가작업 그리고 0개의 파일을 삭제
  1. 18 0
      Userland/Libraries/LibJS/Runtime/ProxyObject.cpp
  2. 9 0
      Userland/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js

+ 18 - 0
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())

+ 9 - 0
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");
+});