From 71a4fde0d60280e1a5e3045a502683d7e630f26a Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Wed, 22 Jul 2026 08:38:50 +0000 Subject: [PATCH 1/3] Error.appendStackTrace: don't abort when stackTraceLimit is unset ErrorInstance::captureStackTrace calls globalObject->stackTraceLimit().value() unconditionally, which aborts when the optional is empty. That state is reachable from JS by assigning a non-number to Error.stackTraceLimit (or deleting it), after which errors carry no native stack trace. Error.appendStackTrace then falls into the captureStackTrace path for the destination and the process dies with SIGABRT. Guard the capture on stackTraceLimit() having a value, and guard the append on the destination actually having a vector so skipping the capture does not turn into a null deref. --- src/jsc/bindings/FormatStackTraceForJS.cpp | 4 ++-- test/js/node/v8/capture-stack-trace.test.js | 24 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/jsc/bindings/FormatStackTraceForJS.cpp b/src/jsc/bindings/FormatStackTraceForJS.cpp index d622679f31aa..6c2deb376f1e 100644 --- a/src/jsc/bindings/FormatStackTraceForJS.cpp +++ b/src/jsc/bindings/FormatStackTraceForJS.cpp @@ -668,11 +668,11 @@ JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncAppendStackTrace, (JSC::JSGlobalObj return {}; } - if (!destination->stackTrace()) { + if (!destination->stackTrace() && globalObject->stackTraceLimit()) { destination->captureStackTrace(vm, globalObject, 1); } - if (source->stackTrace()) { + if (source->stackTrace() && destination->stackTrace()) { destination->stackTrace()->appendVector(*source->stackTrace()); source->stackTrace()->clear(); } diff --git a/test/js/node/v8/capture-stack-trace.test.js b/test/js/node/v8/capture-stack-trace.test.js index 6cd46a1ad90a..5788fc29f6f6 100644 --- a/test/js/node/v8/capture-stack-trace.test.js +++ b/test/js/node/v8/capture-stack-trace.test.js @@ -1121,3 +1121,27 @@ test("lazy error-info materialization does not store an empty stack value when t }); expect(exitCode).toBe(0); }); + +test("Error.appendStackTrace does not abort when stackTraceLimit is not a number", async () => { + const src = ` + Error.stackTraceLimit = undefined; + const a = new Error(); + const b = new Error(); + Error.appendStackTrace(a, b); + + const c = new Error(); + delete Error.stackTraceLimit; + const d = new Error(); + Error.appendStackTrace(c, d); + + process.stdout.write("ok"); + `; + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", src], + env: bunEnv, + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect({ stdout, stderr, signalCode: proc.signalCode }).toEqual({ stdout: "ok", stderr: "", signalCode: null }); + expect(exitCode).toBe(0); +}); From 7b64c8917185f31c30fa95aa933e7ab217438f4a Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Wed, 22 Jul 2026 08:51:57 +0000 Subject: [PATCH 2/3] Cover the null-destination append path in the stackTraceLimit test Create the second source error while the limit is set so it carries frames, then unset the limit before creating the destination. That reaches the appendVector call with a null destination vector, which is what the second guard protects against. --- test/js/node/v8/capture-stack-trace.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/js/node/v8/capture-stack-trace.test.js b/test/js/node/v8/capture-stack-trace.test.js index 5788fc29f6f6..c986a4fe48e3 100644 --- a/test/js/node/v8/capture-stack-trace.test.js +++ b/test/js/node/v8/capture-stack-trace.test.js @@ -1129,6 +1129,7 @@ test("Error.appendStackTrace does not abort when stackTraceLimit is not a number const b = new Error(); Error.appendStackTrace(a, b); + Error.stackTraceLimit = 10; const c = new Error(); delete Error.stackTraceLimit; const d = new Error(); From c9787deb8fffc61847f489e6fbafd9b6a2488eb4 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Wed, 22 Jul 2026 12:07:16 +0000 Subject: [PATCH 3/3] ci: retrigger