Compare commits

...

2 Commits

Author SHA1 Message Date
autofix-ci[bot]
ea63a4593d [autofix.ci] apply automated fixes 2026-02-20 22:07:02 +00:00
Claude
a58a332222 Fix null pointer crash in Bun.$ lazy property callback after stack overflow
PropertyCallback handlers like constructBunShell return empty JSValue {}
on exception via RETURN_IF_EXCEPTION(scope, {}). An empty JSValue (all
bits zero) passes isCell() check but asCell() returns nullptr, causing a
null pointer dereference in putDirect -> isGetterSetterSlow when
reifyStaticProperty unconditionally stores the callback result.

Return jsUndefined() instead of {} on error paths so the value is a
valid non-cell JSValue. The pending exception still propagates correctly.

https://claude.ai/code/session_01KjaS2DyQweyZeuB2ELKnTv
2026-02-20 22:05:11 +00:00
2 changed files with 34 additions and 6 deletions

View File

@@ -317,7 +317,7 @@ static JSValue defaultBunSQLObject(VM& vm, JSObject* bunObject)
#if BUN_DEBUG
if (scope.exception()) globalObject->reportUncaughtExceptionAtEventLoop(globalObject, scope.exception());
#endif
RETURN_IF_EXCEPTION(scope, {});
RETURN_IF_EXCEPTION(scope, jsUndefined());
RELEASE_AND_RETURN(scope, sqlValue.getObject()->get(globalObject, vm.propertyNames->defaultKeyword));
}
@@ -329,7 +329,7 @@ static JSValue constructBunSQLObject(VM& vm, JSObject* bunObject)
#if BUN_DEBUG
if (scope.exception()) globalObject->reportUncaughtExceptionAtEventLoop(globalObject, scope.exception());
#endif
RETURN_IF_EXCEPTION(scope, {});
RETURN_IF_EXCEPTION(scope, jsUndefined());
auto clientData = WebCore::clientData(vm);
RELEASE_AND_RETURN(scope, sqlValue.getObject()->get(globalObject, clientData->builtinNames().SQLPublicName()));
}
@@ -364,20 +364,20 @@ static JSValue constructBunShell(VM& vm, JSObject* bunObject)
args.append(createShellInterpreterFunction);
args.append(createParsedShellScript);
JSC::JSValue shell = JSC::call(globalObject, createShellFn, args, "BunShell"_s);
RETURN_IF_EXCEPTION(scope, {});
RETURN_IF_EXCEPTION(scope, jsUndefined());
if (!shell.isObject()) [[unlikely]] {
throwTypeError(globalObject, scope, "Internal error: BunShell constructor did not return an object"_s);
return {};
return jsUndefined();
}
auto* bunShell = shell.getObject();
auto ShellError = bunShell->get(globalObject, JSC::Identifier::fromString(vm, "ShellError"_s));
RETURN_IF_EXCEPTION(scope, {});
RETURN_IF_EXCEPTION(scope, jsUndefined());
if (!ShellError.isObject()) [[unlikely]] {
throwTypeError(globalObject, scope, "Internal error: BunShell.ShellError is not an object"_s);
return {};
return jsUndefined();
}
bunShell->putDirectNativeFunction(vm, globalObject, Identifier::fromString(vm, "braces"_s), 1, Generated::BunObject::jsBraces, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | 0);

View File

@@ -0,0 +1,28 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe } from "harness";
test("accessing Bun.$ after stack overflow does not crash", async () => {
await using proc = Bun.spawn({
cmd: [
bunExe(),
"-e",
`
function F8() {
if (!new.target) { throw 'must be called with new'; }
const v14 = this?.constructor;
try { new v14(); } catch (e) {}
Bun.$;
}
new F8();
console.log("OK");
`,
],
env: bunEnv,
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout).toBe("OK\n");
expect(exitCode).toBe(0);
});