diff --git a/lib/VM/Operations.cpp b/lib/VM/Operations.cpp index 06931c4e7b1..33b0a060902 100644 --- a/lib/VM/Operations.cpp +++ b/lib/VM/Operations.cpp @@ -1436,6 +1436,15 @@ ordinaryHasInstance(Runtime *runtime, Handle<> constructor, Handle<> object) { "function's '.prototype' is not an object in 'instanceof'"); } + // 6.1.7.3 Invariants of the Essential Internal Methods notes that + // detection of infinite prototype chains is not enforceable as an + // invariant if exotic objects exist in the chain. Most of the + // time, ScopedNativeDepthTracker will detect this. Here, we need to + // check that we're not repeating forever. Since ordinary object + // chains are verified at the time the parent is set, we count Proxy + // objects. Thus, any length chain of ordinary objects is ok. + constexpr unsigned int kMaxProxyCount = 1024; + unsigned int proxyCount = 0; MutableHandle head{runtime, vmcast(object.get())}; GCScopeMarkerRAII gcScope{runtime}; // 6. Repeat @@ -1454,6 +1463,13 @@ ordinaryHasInstance(Runtime *runtime, Handle<> constructor, Handle<> object) { if (parentRes->get() == ctorPrototype.get()) { return true; } + if (head->isProxyObject()) { + ++proxyCount; + if (proxyCount > kMaxProxyCount) { + return runtime->raiseRangeError( + "Maximum prototype chain length exceeded"); + } + } head = parentRes->get(); gcScope.flush(); } diff --git a/test/hermes/instanceof.js b/test/hermes/instanceof.js index b23134b003c..4a0fc801947 100644 --- a/test/hermes/instanceof.js +++ b/test/hermes/instanceof.js @@ -54,3 +54,15 @@ print( (new ChildObj()) instanceof BoundBase); //CHECK-NEXT: true print( (new ChildObj()) instanceof BoundChild); //CHECK-NEXT: true + +var a = new Proxy({}, {}); +var b = Object.create(a); +var c = Object.create(b); +a.__proto__ = c; +try { + b instanceof Date; +} catch (e) { + print("caught", e.name, e.message); +} +//CHECK-NEXT: caught RangeError Maximum prototype chain length exceeded +