From cd520b3cbb8c1d40a52ebce99a8301b9617e4b28 Mon Sep 17 00:00:00 2001 From: robobun Date: Mon, 4 May 2026 14:24:06 +0000 Subject: [PATCH] Fix crash in Bun.inspect when Proxy getPrototypeOf trap throws When walking the prototype chain during property enumeration, JSObject::getPrototype() can invoke a Proxy getPrototypeOf trap which may throw. In that case it returns an empty JSValue, and calling .getObject() on it dereferences a null cell pointer. Check for the empty value before calling getObject(), matching the existing handling in the fast-path branch of the same function. Apply the same fix to napi_get_all_property_names which has the identical pattern. --- src/jsc/bindings/bindings.cpp | 5 ++++- src/jsc/bindings/napi.cpp | 4 +++- test/js/bun/util/inspect.test.js | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/jsc/bindings/bindings.cpp b/src/jsc/bindings/bindings.cpp index 22ac38063376..f4c7d880aefe 100644 --- a/src/jsc/bindings/bindings.cpp +++ b/src/jsc/bindings/bindings.cpp @@ -5444,7 +5444,10 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC: break; if (iterating == globalObject) break; - iterating = iterating->getPrototype(globalObject).getObject(); + JSValue proto = iterating->getPrototype(globalObject); + // Ignore exceptions from Proxy "getPrototype" trap. + CLEAR_IF_EXCEPTION(scope); + iterating = proto ? proto.getObject() : nullptr; } } diff --git a/src/jsc/bindings/napi.cpp b/src/jsc/bindings/napi.cpp index 54c289e47f51..085f2dcc351a 100644 --- a/src/jsc/bindings/napi.cpp +++ b/src/jsc/bindings/napi.cpp @@ -1834,7 +1834,9 @@ extern "C" napi_status napi_get_all_property_names( // Climb up the prototype chain to find inherited properties JSObject* current_object = object; while (!current_object->getOwnPropertyDescriptor(globalObject, key.toPropertyKey(globalObject), desc)) { - JSObject* proto = current_object->getPrototype(globalObject).getObject(); + JSValue protoValue = current_object->getPrototype(globalObject); + NAPI_RETURN_IF_EXCEPTION(env); + JSObject* proto = protoValue.getObject(); if (!proto) { break; } diff --git a/test/js/bun/util/inspect.test.js b/test/js/bun/util/inspect.test.js index 32a70af30183..8a6ffe5f4511 100644 --- a/test/js/bun/util/inspect.test.js +++ b/test/js/bun/util/inspect.test.js @@ -451,6 +451,32 @@ const fixture = [ }, }, ), + () => { + const proto = new Proxy( + {}, + { + getPrototypeOf() { + throw new Error("nope"); + }, + }, + ); + const obj = Object.create(proto); + obj[0] = 1; + return obj; + }, + () => { + const proto = new Proxy( + {}, + { + getPrototypeOf() { + throw new Error("nope"); + }, + }, + ); + const obj = Object.create(proto); + Object.defineProperty(obj, "x", { get: () => 1, enumerable: true, configurable: true }); + return obj; + }, ]; describe("crash testing", () => {