From 4e555fae22f2e92bfa57388f74b3bdf02f6067ec Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Mon, 7 Jun 2021 22:56:16 +0100 Subject: [PATCH] LibJS: Add missing cyclic prototype check to Object.setPrototypeOf() --- Userland/Libraries/LibJS/Runtime/Object.cpp | 24 +++++++++++++++---- .../builtins/Object/Object.setPrototypeOf.js | 10 ++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Object.cpp b/Userland/Libraries/LibJS/Runtime/Object.cpp index 4667eed1d2a..9af14a02eab 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.cpp +++ b/Userland/Libraries/LibJS/Runtime/Object.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -110,17 +111,32 @@ const Object* Object::prototype() const return shape().prototype(); } +// 10.1.2.1 OrdinarySetPrototypeOf, https://tc39.es/ecma262/#sec-ordinarysetprototypeof bool Object::set_prototype(Object* new_prototype) { if (prototype() == new_prototype) return true; if (!m_is_extensible) return false; - if (shape().is_unique()) { - shape().set_prototype_without_transition(new_prototype); - return true; + auto* prototype = new_prototype; + while (prototype) { + if (prototype == this) + return false; + // NOTE: This is a best-effort implementation of the following step: + // "If p.[[GetPrototypeOf]] is not the ordinary object internal method defined in 10.1.1, + // set done to true." + // We don't have a good way of detecting whether certain virtual Object methods have been + // overridden by a given object, but as ProxyObject is the only one doing that, this check + // does the trick. + if (is(prototype)) + break; + prototype = prototype->prototype(); } - m_shape = m_shape->create_prototype_transition(new_prototype); + auto& shape = this->shape(); + if (shape.is_unique()) + shape.set_prototype_without_transition(new_prototype); + else + m_shape = shape.create_prototype_transition(new_prototype); return true; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Object/Object.setPrototypeOf.js b/Userland/Libraries/LibJS/Tests/builtins/Object/Object.setPrototypeOf.js index 69a7b24ab3e..1b0b2888e73 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Object/Object.setPrototypeOf.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Object/Object.setPrototypeOf.js @@ -40,4 +40,14 @@ describe("errors", () => { expect(Object.setPrototypeOf(o, p)).toBe(o); }); + + test("cyclic prototype chain", () => { + let o = {}; + let p = {}; + Object.setPrototypeOf(o, p); + + expect(() => { + Object.setPrototypeOf(p, o); + }).toThrowWithMessage(TypeError, "Object's [[SetPrototypeOf]] method returned false"); + }); });