From 0d0f91c9dc039eeb410de57908d39e157027a9a7 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Sat, 22 Nov 2025 22:59:34 -0800 Subject: [PATCH 1/3] exception check --- src/bun.js/ConsoleObject.zig | 6 +++--- src/bun.js/bindings/JSValue.zig | 5 ++--- src/bun.js/bindings/bindings.cpp | 14 ++++++++++---- src/bun.js/bindings/headers.h | 1 - src/bun.js/bindings/napi.cpp | 4 +++- src/bun.js/modules/NodeUtilTypesModule.cpp | 1 + src/bun.js/test/ScopeFunctions.zig | 2 +- src/bun.js/test/pretty_format.zig | 2 +- 8 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/bun.js/ConsoleObject.zig b/src/bun.js/ConsoleObject.zig index 20e1de9898f..c2a28f04743 100644 --- a/src/bun.js/ConsoleObject.zig +++ b/src/bun.js/ConsoleObject.zig @@ -2037,7 +2037,7 @@ pub const Formatter = struct { try value.getClassName(globalThis, &name_str); if (!name_str.eqlComptime("Object")) { return name_str; - } else if (value.getPrototype(globalThis).eqlValue(JSValue.null)) { + } else if ((try value.getPrototype(globalThis)) == .null) { return ZigString.static("[Object: null prototype]").*; } return null; @@ -2318,7 +2318,7 @@ pub const Formatter = struct { try value.getClassName(this.globalThis, &printable); this.addForNewLine(printable.len); - const proto = value.getPrototype(this.globalThis); + const proto = try value.getPrototype(this.globalThis); var printable_proto = ZigString.init(&name_buf); try proto.getClassName(this.globalThis, &printable_proto); this.addForNewLine(printable_proto.len); @@ -2341,7 +2341,7 @@ pub const Formatter = struct { var printable = try value.getName(this.globalThis); defer printable.deref(); - const proto = value.getPrototype(this.globalThis); + const proto = try value.getPrototype(this.globalThis); const func_name = try proto.getName(this.globalThis); // "Function" | "AsyncFunction" | "GeneratorFunction" | "AsyncGeneratorFunction" defer func_name.deref(); diff --git a/src/bun.js/bindings/JSValue.zig b/src/bun.js/bindings/JSValue.zig index 75a891f6595..78dec100c1b 100644 --- a/src/bun.js/bindings/JSValue.zig +++ b/src/bun.js/bindings/JSValue.zig @@ -1275,9 +1275,8 @@ pub const JSValue = enum(i64) { } extern fn JSC__JSValue__unwrapBoxedPrimitive(*JSGlobalObject, JSValue) JSValue; - extern fn JSC__JSValue__getPrototype(this: JSValue, globalObject: *JSGlobalObject) JSValue; - pub fn getPrototype(this: JSValue, globalObject: *JSGlobalObject) JSValue { - return JSC__JSValue__getPrototype(this, globalObject); + pub fn getPrototype(this: JSValue, globalObject: *JSGlobalObject) JSError!JSValue { + return bun.cpp.JSC__JSValue__getPrototype(this, globalObject); } extern fn JSC__JSValue__eqlValue(this: JSValue, other: JSValue) bool; diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 945c31bda46..f4a42539e95 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -3691,7 +3691,7 @@ bool JSC__JSValue__eqlValue(JSC::EncodedJSValue JSValue0, JSC::EncodedJSValue JS { return JSC::JSValue::decode(JSValue0) == JSC::JSValue::decode(JSValue1); }; -JSC::EncodedJSValue JSC__JSValue__getPrototype(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1) +[[ZIG_EXPORT(zero_is_throw)]] JSC::EncodedJSValue JSC__JSValue__getPrototype(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1) { auto value = JSC::JSValue::decode(JSValue0); return JSC::JSValue::encode(value.getPrototype(arg1)); @@ -4970,6 +4970,8 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC: fast = canPerformFastPropertyEnumerationForIterationBun(structure); prototypeCount = 1; } + } else { + RETURN_IF_EXCEPTION(scope, void()); } } } @@ -5049,9 +5051,9 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC: goto restart; } } + } else { + RETURN_IF_EXCEPTION(scope, void()); } - // Ignore exceptions from Proxy "getPrototype" trap. - CLEAR_IF_EXCEPTION(scope); } return; } @@ -5164,7 +5166,11 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC: break; if (iterating == globalObject) break; - iterating = iterating->getPrototype(globalObject).getObject(); + + JSValue proto = iterating->getPrototype(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + + iterating = proto.getObject(); } } diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index f0f02cf835d..d267f9d2af3 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -229,7 +229,6 @@ CPP_DECL JSC::EncodedJSValue JSC__JSValue__getErrorsProperty(JSC::EncodedJSValue CPP_DECL JSC::EncodedJSValue JSC__JSValue__getIfPropertyExistsFromPath(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1, JSC::EncodedJSValue JSValue2); CPP_DECL double JSC__JSValue__getLengthIfPropertyExistsInternal(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1); CPP_DECL void JSC__JSValue__getNameProperty(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1, ZigString* arg2); -CPP_DECL JSC::EncodedJSValue JSC__JSValue__getPrototype(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1); CPP_DECL void JSC__JSValue__getSymbolDescription(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1, ZigString* arg2); CPP_DECL double JSC__JSValue__getUnixTimestamp(JSC::EncodedJSValue JSValue0); CPP_DECL bool JSC__JSValue__hasOwnProperty(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1, ZigString arg2); diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index 5166bf1d55e..e97c63eaed6 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -1831,7 +1831,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/src/bun.js/modules/NodeUtilTypesModule.cpp b/src/bun.js/modules/NodeUtilTypesModule.cpp index 5dc76db0a02..7ccf0de05fb 100644 --- a/src/bun.js/modules/NodeUtilTypesModule.cpp +++ b/src/bun.js/modules/NodeUtilTypesModule.cpp @@ -140,6 +140,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionIsError, } JSValue proto = object->getPrototype(globalObject); + RETURN_IF_EXCEPTION(scope, {}); if (proto.isCell() && (proto.inherits() || proto.asCell()->type() == ErrorInstanceType || proto.inherits())) return JSValue::encode(jsBoolean(true)); } diff --git a/src/bun.js/test/ScopeFunctions.zig b/src/bun.js/test/ScopeFunctions.zig index 715c334505b..e45539076ca 100644 --- a/src/bun.js/test/ScopeFunctions.zig +++ b/src/bun.js/test/ScopeFunctions.zig @@ -453,7 +453,7 @@ pub fn createUnbound(globalThis: *JSGlobalObject, mode: Mode, each: jsc.JSValue, pub fn bind(value: JSValue, globalThis: *JSGlobalObject, name: bun.String) bun.JSError!JSValue { const callFn = jsc.JSFunction.create(globalThis, name, callAsFunction, 1, .{}); const bound = try callFn.bind(globalThis, value, &name, 1, &.{}); - try bound.setPrototypeDirect(value.getPrototype(globalThis), globalThis); + try bound.setPrototypeDirect(try value.getPrototype(globalThis), globalThis); return bound; } diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig index 5b0860cc131..eb61054e2d0 100644 --- a/src/bun.js/test/pretty_format.zig +++ b/src/bun.js/test/pretty_format.zig @@ -725,7 +725,7 @@ pub const JestPrettyFormat = struct { if (name_str.len > 0 and !name_str.eqlComptime("Object")) { writer.print("{f} ", .{name_str}); } else { - try value.getPrototype(globalThis).getNameProperty(globalThis, &name_str); + try (try value.getPrototype(globalThis)).getNameProperty(globalThis, &name_str); if (name_str.len > 0 and !name_str.eqlComptime("Object")) { writer.print("{f} ", .{name_str}); } From bbff1a6b366aa92f998b9568e47395898c5bcec6 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Sat, 22 Nov 2025 23:11:21 -0800 Subject: [PATCH 2/3] add minimized test --- test/js/bun/util/inspect.test.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/js/bun/util/inspect.test.js b/test/js/bun/util/inspect.test.js index 02b3be838c0..35d5130534d 100644 --- a/test/js/bun/util/inspect.test.js +++ b/test/js/bun/util/inspect.test.js @@ -82,6 +82,30 @@ it("getter/setters", () => { expect(Bun.inspect(obj)).toBe("{\n" + " foo: [Getter/Setter]," + "\n" + "}"); }); +it("stack overflow exception checks", () => { + function probe(value) { + let originalPrototype, newPrototype; + let handler = { + set(target, key, value, receiver) { + return Reflect.set(target, key, value, receiver); + }, + }; + originalPrototype = Object.getPrototypeOf(value); + newPrototype = new Proxy(originalPrototype, handler); + Object.setPrototypeOf(value, newPrototype); + } + class Foo { + get bar() { + Bun.inspect(this); + } + } + const foo = new Foo(); + probe(foo); + expect(() => { + foo.bar(Foo, foo); + }).toThrow("Maximum call stack size exceeded"); +}); + it("Timeout", () => { const id = setTimeout(() => {}, 0); expect(Bun.inspect(id)).toBe(`Timeout (#${+id})`); From 64f6d526f3042c2fb7c6f5e53f9d946adc34bd33 Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Wed, 26 Nov 2025 22:30:49 +0000 Subject: [PATCH 3/3] fix: add exception check in JSC__JSValue__getPrototype MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The getPrototype function was missing a proper exception check using DECLARE_THROW_SCOPE and RETURN_IF_EXCEPTION. This fixes exception validation when BUN_JSC_validateExceptionChecks is enabled. Also fixed the exception check pattern in forEachPropertyImpl to check for exceptions immediately after getPrototype returns, rather than only in the else branch (when getPrototype returns null/falsy). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/bun.js/bindings/bindings.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index f4a42539e95..6f7dd0a1f47 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -3691,10 +3691,14 @@ bool JSC__JSValue__eqlValue(JSC::EncodedJSValue JSValue0, JSC::EncodedJSValue JS { return JSC::JSValue::decode(JSValue0) == JSC::JSValue::decode(JSValue1); }; -[[ZIG_EXPORT(zero_is_throw)]] JSC::EncodedJSValue JSC__JSValue__getPrototype(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1) +[[ZIG_EXPORT(zero_is_throw)]] JSC::EncodedJSValue JSC__JSValue__getPrototype(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* globalObject) { + auto& vm = JSC::getVM(globalObject); + auto scope = DECLARE_THROW_SCOPE(vm); auto value = JSC::JSValue::decode(JSValue0); - return JSC::JSValue::encode(value.getPrototype(arg1)); + JSValue result = value.getPrototype(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + return JSC::JSValue::encode(result); } bool JSC__JSValue__isException(JSC::EncodedJSValue JSValue0, JSC::VM* arg1) { @@ -4964,14 +4968,14 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC: if (structure->outOfLineSize() == 0 && structure->inlineSize() == 0) { fast = false; - if (JSValue proto = object->getPrototype(globalObject)) { + JSValue proto = object->getPrototype(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + if (proto) { if ((structure = proto.structureOrNull())) { prototypeObject = proto; fast = canPerformFastPropertyEnumerationForIterationBun(structure); prototypeCount = 1; } - } else { - RETURN_IF_EXCEPTION(scope, void()); } } } @@ -5043,7 +5047,9 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC: if (anyHits) { if (prototypeCount++ < 5) { - if (JSValue proto = prototypeObject.getPrototype(globalObject)) { + JSValue proto = prototypeObject.getPrototype(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + if (proto) { if (!(proto == globalObject->objectPrototype() || proto == globalObject->functionPrototype() || (proto.inherits() && jsCast(proto)->target() != globalObject))) { if ((structure = proto.structureOrNull())) { prototypeObject = proto; @@ -5051,8 +5057,6 @@ static void JSC__JSValue__forEachPropertyImpl(JSC::EncodedJSValue JSValue0, JSC: goto restart; } } - } else { - RETURN_IF_EXCEPTION(scope, void()); } } return;