Skip to content

spawn: don't reinstall fault-signal handlers after sync-spawn signal forwarding - #30776

Open
robobun wants to merge 10 commits into
mainfrom
farm/39e70373/sync-spawn-preserve-jsc-fault-handlers
Open

spawn: don't reinstall fault-signal handlers after sync-spawn signal forwarding#30776
robobun wants to merge 10 commits into
mainfrom
farm/39e70373/sync-spawn-preserve-jsc-fault-handlers

Conversation

@robobun

@robobun robobun commented May 15, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

SignalForwarding::drop — the RAII cleanup around the CLI sync-spawn path used by bun run <pkg-script>, bun test --changed's git probes, bunx, bun create, bun pm version, … — called crash_handler::reset_on_posix() after Bun__unregisterSignalsForForwarding().

The signal-forwarding set (FOR_EACH_SIGNAL in c-bindings.cpp: SIGINT/SIGTERM/SIGHUP/SIGQUIT/SIGABRT/…) never included SIGSEGV/SIGBUS/SIGILL/SIGFPE, and the unregister call already restores every signal it touched from a saved snapshot. So the reset was a no-op for its stated purpose — but it reinstalled Bun's crash handler on the four fault signals with oldact=NULL, overwriting WTF's jscSignalHandler that WTF::SignalHandlers::finalize() had layered on top at JSC init.

Why does it matter?

jscSignalHandler is what makes signal-based VMTraps work. When JSC needs to interrupt DFG/FTL code asynchronously — stop-the-world GC (NeedStopTheWorld), Worker.terminate() (NeedTermination), watchdog timeout — the VMTraps::SignalSender thread patches a halt instruction (hlt on x86, dc zva, xzr on arm64) over each invalidation point in the running CodeBlock. The mutator hits it, the kernel delivers SIGSEGV, and jscSignalHandler dispatches to the VMTraps lambda, which looks the PC up in DFG::pcCodeBlockMap, jettisons the CodeBlock (patching the halt back to a jump to the OSR-exit thunk), and returns Handled. The mutator OSR-exits and reaches VMTraps::handleTraps().

With Bun's crash handler installed in its place, the halt delivers SIGSEGV straight to the crash reporter, and the process dies with a spurious "Segmentation fault in JIT code" whose stack shows a half-jettisoned CodeBlock. Users have been working around this with BUN_JSC_usePollingTraps=1.

How

  • Drop the reset_on_posix() call from SignalForwarding::drop. jscSignalHandler already chains unhandled faults to the handler it displaced (Bun's crash handler), so crash reporting is unchanged.
  • Add a bun:internal-for-testing probe, crash_handler.getFaultSignalHandlers(), that returns the four current sa_sigaction addresses via libc::sigaction.
  • Add a test that captures the fault-signal handler partition in a fresh bun -e process and in a bun test --changed process (the one cross-platform path where the sync-spawn scope runs inside a live JSC VM and JS continues afterward) and asserts they match.
  • reset_on_posix() itself is now private to crash_handler; with the spawn call gone, init() in the same module is its only caller. bun_spawn no longer depends on bun_crash_handler.
  • bake-codegen.ts passes OVERLAY_CSS through JSON.stringify like the neighbouring side define, so the codegen step does not depend on the bootstrap bun having the define auto-quoting from JSON lexer: tokenize ?/*/(/) so define auto-quote can recover #30679 (it failed to build with an older bootstrap bun otherwise). The bundler test comment that pointed at the raw form is updated to match.

Testing note

Debug ASAN builds never exhibited the regression because reset_on_posix() early-returns under ENABLE_ASAN. The test asserts the invariant — fault handlers identical with and without a preceding sync spawn — which holds for both build types, so it guards the release behaviour even though bun bd alone cannot reproduce the original failure. Release CI will catch a regression.

A companion change (oven-sh/WebKit#233) reorders CommonData::invalidateLinkedCode() so the HLT is patched to a jump before its PC is removed from pcCodeBlockMap, closing a separate window where codeBlockForVMTrapPC() can return nullptr for a still-live HLT.


no test proof · iteration 15 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/bundler/bun-build-api.test.ts

@robobun

robobun commented May 15, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 2:17 PM PT - Aug 15th, 2026

@robobun, your commit 49e6487 has 1 failures in Build #98613 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 30776

That installs a local version of the PR into your bun-30776 executable, so you can run:

bun-30776 --bun

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Worried about impact? Review this PR in Change Stack to explore blast radius before you approve or request changes.

Review Change Stack

Walkthrough

This PR adds fault-signal handler probing to internal testing APIs, prevents signal-forwarding cleanup from resetting JSC fault handlers during sync-spawn, validates the fix with a POSIX regression test, and applies minor formatting and documentation tweaks.

Changes

JSC Signal Handler Preservation

Layer / File(s) Summary
Native fault-signal handler exposure
src/js/internal-for-testing.ts, src/runtime/api/crash_handler_jsc.rs
Adds getFaultSignalHandlers binding that queries installed sa_sigaction addresses for SIGSEGV, SIGBUS, SIGILL, and SIGFPE, returning hex-string pointer map on Unix or undefined on non-Unix.
Signal forwarding cleanup fix
src/spawn/process.rs, src/spawn/Cargo.toml, src/spawn/lib.rs
Removes bun_crash_handler::reset_on_posix() call from SignalForwarding drop so cleanup no longer restores crash-handler dispositions; updates documentation and removes the now-unused bun_crash_handler dependency.
Sync-spawn signal handler preservation test
test/js/bun/spawn/sync-spawn-preserves-jsc-signal-handlers.test.ts
POSIX-only regression test that captures baseline handlers via the internal probe, runs bun test --changed in a temp git repo, and asserts SIGSEGV/SIGBUS handler shape and non-zero SIGSEGV are preserved across sync-spawn.
Platform-conditional code reformatting
src/spawn/process.rs
Reformats platform-gating in the no-orphans wait logic to explicitly list supported platforms (linux, android, macos) and adds an explicit fallback branch; behavior is preserved.

Unrelated Minor Updates

Layer / File(s) Summary
CSS stringify and JSON lexer documentation
src/codegen/bake-codegen.ts, src/parsers/json_lexer.rs
Wraps minified CSS with JSON.stringify() for OVERLAY_CSS define substitution and clarifies a JSON lexer comment about error timing relative to auto-quote fallback.

Possibly related PRs

  • oven-sh/bun#31183: Modifies sync-spawn signal-forwarding behavior by removing SIGPWR from the Linux forwarded signal set, related to signal-forwarding changes.

Suggested reviewers

  • Jarred-Sumner
  • dylan-conway
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the primary change: preventing fault-signal handlers from being reinstalled after sync-spawn forwarding.
Description check ✅ Passed The description explains the change, impact, implementation, and verification approach, including the regression test and platform limitations.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/js/bun/spawn/sync-spawn-preserves-jsc-signal-handlers.test.ts`:
- Around line 46-87: Replace the unmanaged tmpdirSync usage by creating the
empty git config inside a harness-managed tempDir: remove tmpdirSync and the
standalone writeFileSync call that creates emptyGitConfig, call tempDir(...) to
create a temp directory with an "empty.gitconfig" entry (using tempDir(..., {
"empty.gitconfig": "" })) and set emptyGitConfig to that generated path; update
env to use that emptyGitConfig and keep BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING
and other env vars unchanged so cleanup is automatic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 59335f5a-b1d9-4ef1-8f28-bfce5e1e4d63

📥 Commits

Reviewing files that changed from the base of the PR and between 314d044 and f55d019.

📒 Files selected for processing (13)
  • src/crash_handler/lib.rs
  • src/errno/lib.rs
  • src/js/internal-for-testing.ts
  • src/perf/tracy.rs
  • src/runtime/api/crash_handler_jsc.rs
  • src/runtime/cli/Arguments.rs
  • src/runtime/cli/run_command.rs
  • src/runtime/cli/upgrade_command.rs
  • src/runtime/jsc_hooks.rs
  • src/runtime/webview/ChromeProcess.rs
  • src/spawn/process.rs
  • src/spawn_sys/spawn_process.rs
  • test/js/bun/spawn/sync-spawn-preserves-jsc-signal-handlers.test.ts

Comment thread test/js/bun/spawn/sync-spawn-preserves-jsc-signal-handlers.test.ts Outdated
Comment thread src/spawn/process.rs
Comment thread src/runtime/api/crash_handler_jsc.rs
Comment thread src/spawn/process.rs Outdated
Comment thread src/codegen/bake-codegen.ts
Comment thread src/spawn/process.rs
Comment thread src/codegen/bake-codegen.ts

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All earlier nits are addressed and I didn't find anything new, but the core change alters process-wide fault-signal dispositions that gate both JSC's VMTraps and Bun's crash reporter (and pairs with a WebKit-side change), so it's worth a human sign-off.

Extended reasoning...

Overview

The functional core is a one-line deletion: removing bun_crash_handler::reset_on_posix() from SignalForwarding::drop (src/spawn/process.rs:2929). Everything else is scaffolding around it — a new testing-only getFaultSignalHandlers() probe in crash_handler_jsc.rs (read-only sigaction(sig, NULL, &out)), a regression test that compares fault-handler partitions before/after a sync-spawn, dropping the now-unused bun_crash_handler dep from bun_spawn, an unrelated JSON.stringify wrap of OVERLAY_CSS in bake-codegen, and a large volume of pure rustfmt #[cfg(...)] reflow across ~10 files.

Security risks

None identified. The new unsafe block is a read-only libc::sigaction query gated behind bun:internal-for-testing. No new inputs are parsed and no privilege boundaries are crossed.

Level of scrutiny

High. The deleted line governs which handler receives SIGSEGV/SIGBUS/SIGILL/SIGFPE after every CLI sync-spawn. The PR's argument — that jscSignalHandler already chains unhandled faults to Bun's crash handler, so reinstalling it on top is both redundant and destructive to VMTraps — is well-reasoned and matches the WTF source, but the correctness depends on JSC's signal-chaining contract holding on every platform/build config (release vs ASAN, glibc vs musl, macOS). The PR description itself notes debug-ASAN builds never exhibited the regression and that release CI is the real guard. There's also a companion WebKit PR (#233) reordering CommonData::invalidateLinkedCode(); someone should confirm whether this change is safe to land independently of that.

Other factors

All six of my earlier inline comments (stale dep, capacity hint, tempDir convention, stale cross-references, .zig spec divergence) have been addressed or explicitly declined with reasoning, and the threads are resolved. The bundled bake-codegen.ts change is logically separate from the signal fix but is small and self-explanatory. The rustfmt reflow is noise-only. No CODEOWNERS cover the touched paths.

@robobun

robobun commented May 15, 2026

Copy link
Copy Markdown
Collaborator Author

CI status for maintainer review (head 49e6487, rebased onto current main)

Build #98613 (current head, finished): 178 of 179 jobs passed. The one red job is a single windows 2019 x64 test shard, failing on test/js/node/test/parallel/test-cluster-shared-leak.js (timeout). That test also timed out on the same lane in the previous build (#98552) and passed on retry there; it is a Windows-only node cluster test and every line this PR changes is #[cfg(unix)] code (plus a test that is skipIf(!isPosix)), so it cannot be affected by this diff. It has been reported separately as a main-side failure.

Every other failure in #98613 and #98552 was a retried flake that passed on retry or alone (bun-prune, isolated-relink, shell/mv, child_process_ipc_handle, terminal-platform-gaps, test-watch-mode-kill-signal-default, ctrl-c, build-debug-info-flags, inspect-error-leak, resolve, bun-patch, build-codegen-declared-outputs, bun-install-registry, malformed-integrity-base64), none of them touching this PR's code. The fleet-wide reds from earlier builds (#33214) are resolved and no longer appear.

The new test/js/bun/spawn/sync-spawn-preserves-jsc-signal-handlers.test.ts passes on every lane it runs on, including ASAN. The one allowed CI re-trigger for this PR was used earlier, so the Windows shard will stay red until a maintainer re-runs it or merges past it.

For the reviewer: the chaining invariant the bot review asks about is verifiable at vendor/WebKit/Source/WTF/wtf/threads/Signals.cpp:594-596 (JSC saves the displaced handler, i.e. Bun's crash reporter, into oldActions[]) and Signals.cpp:538-544 (unhandled faults are forwarded to it), so real-segfault crash reporting is unchanged by this PR.

Companion WebKit change oven-sh/WebKit#233 is green and mergeable, awaiting review.

@robobun
robobun force-pushed the farm/39e70373/sync-spawn-preserve-jsc-fault-handlers branch from dc83ab1 to 268a31e Compare May 22, 2026 05:51

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All earlier review feedback is addressed and I found nothing further, but the core change alters fault-signal (SIGSEGV/SIGBUS) disposition behaviour that JSC's VMTraps and Bun's crash reporter both depend on — worth a maintainer sign-off on the chaining argument and the companion WebKit change before merging.

Extended reasoning...

Overview

The functional core is a one-line deletion: bun_crash_handler::reset_on_posix() is removed from SignalForwarding::drop in src/spawn/process.rs. Supporting changes: a new getFaultSignalHandlers() testing probe in crash_handler_jsc.rs (read-only libc::sigaction query, gated behind bun:internal-for-testing), a comprehensive regression test, removal of the now-unused bun_crash_handler dep from bun_spawn, and some unrelated cleanup (JSON.stringify the OVERLAY_CSS define in bake-codegen.ts, two stale comment edits).

Security risks

No new attack surface. The new native binding only reads signal dispositions via sigaction(sig, NULL, &out) and is only reachable from bun:internal-for-testing. The deleted call was itself the thing that mutated process-global signal state; removing it strictly reduces side effects. No auth/crypto/permissions/input-handling code is touched.

Level of scrutiny

High. The change is small and the reasoning in the PR description and the new doc-comment is thorough and internally consistent, but its correctness rests on a non-local invariant: that WTF's jscSignalHandler chains unhandled SIGSEGV/SIGBUS/SIGILL/SIGFPE to the handler it displaced (Bun's crash reporter), so removing the explicit reinstall does not regress crash reporting. That holds per the JSC source the description cites, but it's exactly the kind of cross-repo assumption (and there is a companion oven-sh/WebKit#233) that a maintainer familiar with the JSC signal layer should confirm. A mistake here would either re-break VMTraps or silently lose crash reports — both hard to notice in CI since debug ASAN builds never exhibited the original bug.

Other factors

All five inline threads from earlier review rounds (mine and CodeRabbit's) are resolved with corresponding commits; the diff reflects every requested fix. The author explicitly declined to edit the frozen .zig spec sibling, with sound reasoning, and deferred the pre-existing SIGIOT/SIGPOLL aliasing bug to a follow-up — both reasonable. CI build #54775 reports all 72 lanes green including the new test; only the Buildkite aggregate status is in a confused state. The regression test is well-designed (compares handler equivalence classes across two processes to neutralise ASLR) and would catch a reintroduction.

@robobun
robobun force-pushed the farm/39e70373/sync-spawn-preserve-jsc-fault-handlers branch from 268a31e to 38b52c1 Compare June 6, 2026 01:40

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All my earlier nits are addressed and no new issues found — deferring final sign-off to a maintainer since this changes fault-signal disposition handling on the crash-reporter / JSC VMTraps boundary.

Extended reasoning...

Overview

The core functional change is a single deleted line: bun_crash_handler::reset_on_posix() is removed from SignalForwarding::drop in src/spawn/process.rs, so the CLI sync-spawn path no longer reinstalls Bun's crash handler on SIGSEGV/SIGBUS/SIGILL/SIGFPE after the signal-forwarding scope unwinds. Supporting changes: a new getFaultSignalHandlers() probe in crash_handler_jsc.rs (unsafe libc::sigaction query, testing-only), a thorough regression test driving bun test --changed to exercise the scope inside a live JSC VM, removal of the now-unused bun_crash_handler dep from bun_spawn, and a handful of unrelated comment/codegen cleanups (bake-codegen.ts OVERLAY_CSS stringify, stale cross-reference comments in json_lexer.rs / bun-build-api.test.ts).

Security risks

No injection, auth, or data-exposure surface. The change does affect process-wide signal dispositions and crash-reporting behaviour: the correctness argument is that WTF's jscSignalHandler already chains unhandled faults to Bun's crash handler (which it displaced at JSC init), so removing the reinstall preserves crash reporting while restoring signal-based VMTraps. That argument is well-documented in the PR body and the new doc-comment, but it hinges on JSC/WTF behaviour that lives outside this repo (and there's a companion WebKit PR #233). The new unsafe block in the testing probe is a read-only sigaction(sig, NULL, &out) query and is gated behind bun:internal-for-testing.

Level of scrutiny

High. Signal handling sits at the intersection of Bun's crash reporter, JSC's stop-the-world GC / Worker.terminate() / watchdog interruption, and ASAN — getting it wrong manifests as spurious "Segmentation fault in JIT code" crashes (the bug being fixed) or, in the other direction, lost crash reports. The fix itself is one line, but verifying that not reinstalling the handler is safe in every configuration (release vs debug-ASAN, with/without the WebKit companion change, across the various sync-spawn callers) is the kind of judgment call a maintainer who owns this area should make.

Other factors

All five of my earlier inline threads (unused dep, capacity hint, stale comments, .zig spec divergence, SIGIOT/SIGPOLL aliasing follow-up) are resolved; the author addressed each one or gave a reasoned decline. The bug-hunting system found nothing this round. CI is green on all 74 build/test lanes per the author's status comment, with two unrelated infra flakes. The PR also bundles a small unrelated bake-codegen.ts change (explicit JSON.stringify of OVERLAY_CSS) — benign, but another reason a human should glance at the full diff before merge.

@robobun
robobun force-pushed the farm/39e70373/sync-spawn-preserve-jsc-fault-handlers branch from b944f76 to 4aefdf6 Compare July 1, 2026 22:48

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No further issues found — all my earlier nits are addressed. The core one-line deletion looks correct given the reasoning, but since it changes process-wide fault-signal disposition (and its correctness hinges on jscSignalHandler chaining unhandled faults back to Bun's crash handler), it'd be good to have a maintainer familiar with the JSC signal layer / crash_handler confirm before merge.

Extended reasoning...

Overview

The PR removes a single bun_crash_handler::reset_on_posix() call from SignalForwarding::drop in src/spawn/process.rs, drops the now-unused bun_crash_handler dep from bun_spawn, adds a getFaultSignalHandlers() internal-for-testing probe in crash_handler_jsc.rs, and adds a regression test that compares fault-signal handler shape with and without a preceding CLI sync-spawn. It also bundles two small unrelated cleanups: JSON.stringify-wrapping OVERLAY_CSS in bake-codegen.ts and dropping a stale comment cross-reference in bun-build-api.test.ts.

Security risks

None identified. The change removes a sigaction reinstallation rather than adding one; the new probe is read-only (sigaction(sig, NULL, &out)) and gated behind bun:internal-for-testing. No untrusted-input parsing, auth, or crypto paths are touched.

Level of scrutiny

High. The functional diff is tiny, but it changes the process-wide disposition of SIGSEGV/SIGBUS/SIGILL/SIGFPE for every CLI path that goes through the sync-spawn signal-forwarding scope (bun run, bunx, bun test --changed, bun create, …). Correctness of both crash reporting and signal-based VMTraps after this change rests on the claim that WTF's jscSignalHandler chains unhandled faults to whatever handler it displaced at SignalHandlers::finalize() time — which is a JSC/WTF-internal invariant a maintainer should confirm. The PR also notes debug ASAN builds never exhibited the regression (reset_on_posix() early-returns under ASAN), so the test guards the invariant by shape-comparison rather than reproducing the original crash; release CI is the real guard.

Other factors

All prior inline review comments (mine and CodeRabbit's) are resolved. The author's CI-status comment indicates the lanes exercising this code pass and remaining reds are unrelated known issues. There's a companion WebKit change (oven-sh/WebKit#233) touching the same VMTraps area, which further suggests someone with JSC-signal context should look at both together. The bundled bake-codegen.ts change is orthogonal but low-risk. Given the subtlety of signal-handler layering in a JIT runtime, I'm deferring rather than approving.

@robobun

robobun commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

For whoever picks up the final review, the one thing the bot review asks to have confirmed (that crash reporting still works after this change) is verifiable in two places in the vendored WTF source:

  • Install + save: SignalHandlers::finalize() registers jscSignalHandler with sigaction(sig, &action, &handlers.oldActions[...]), capturing the handler it displaced, which at that point is Bun's handle_segfault_posix (installed by crash_handler::init() at startup, before JSC init). vendor/WebKit/Source/WTF/wtf/threads/Signals.cpp:594-596
  • Chain: when no registered handler claims the fault (not a VMTraps HLT, not a Wasm OOB), jscSignalHandler invokes that saved handler directly: if (!didHandle) { if (oldAction.sa_sigaction) { oldAction.sa_sigaction(sig, info, ucontext); return; } }. vendor/WebKit/Source/WTF/wtf/threads/Signals.cpp:538-544

So for a real segfault the delivery order is unchanged by this PR: jscSignalHandler -> (unhandled) -> Bun crash reporter. The only behavioral change is that after a CLI sync-spawn, a VMTraps HLT now reaches JSC's handler instead of being misrouted to the crash reporter.

The regression test exercises exactly that scope (bun test --changed sync-spawns git inside a live VM) and compares the fault-signal dispositions against a no-spawn baseline process.

robobun and others added 7 commits August 15, 2026 18:40
…forwarding

SignalForwarding::drop (the CLI sync-spawn wrapper used by bun run
<pkg-script>, bun test --changed's git probes, bunx, bun create, bun pm
version, …) called crash_handler::reset_on_posix() after
Bun__unregisterSignalsForForwarding(). The forwarding set never included
SIGSEGV/SIGBUS/SIGILL/SIGFPE and the unregister call already restores every
signal it touched from previous_actions[], so the reset was a no-op for its
stated purpose — but it reinstalled Bun's crash handler on the four
fault signals with oldact=NULL, overwriting WTF's jscSignalHandler that
WTF::SignalHandlers::finalize() had layered on top at JSC init.

jscSignalHandler is what makes signal-based VMTraps work: when JSC needs to
interrupt DFG/FTL code for a stop-the-world GC, Worker.terminate(), or the
watchdog, it patches a halt instruction over each invalidation point, the
mutator takes SIGSEGV, jscSignalHandler looks the PC up in
DFG::pcCodeBlockMap, jettisons the CodeBlock (patching the halt back to a
jump to the OSR-exit thunk), and returns Handled. With Bun's crash handler
in its place the halt delivers straight to the crash reporter instead, and
the process dies with a spurious segfault in JIT code. Users have been
working around this with BUN_JSC_usePollingTraps=1.

Drop the reset_on_posix() call; jscSignalHandler already chains unhandled
faults to the crash handler it displaced. Add a
crash_handler.getFaultSignalHandlers() test probe and a test that captures
the fault-signal dispositions in a fresh process and in a
bun test --changed process (which runs git via the sync-spawn scope before
loading tests) and asserts the handler partition is identical.

ASAN builds never exhibited the regression because reset_on_posix()
early-returns under ASAN; the test asserts the invariant for both.
…ir for gitconfig fixture

The minified overlay CSS begins with `*{box-sizing:...}`. Pre-#30679
codegen buns reject that in the JSON-lex pass before the `define`
auto-quote fallback can treat it as a string literal, so building from
source breaks whenever the bootstrap bun predates that commit. Quote it
explicitly like the `side` define already is.

Also: colocate the empty gitconfig fixture with the test's git repo
inside one harness-managed `tempDir` instead of a standalone
`tmpdirSync()`, per test conventions.
…handler js_bindings object from ENTRIES.len()
…uote comment

Same as 0d0cc60 for json_lexer.rs — bake-codegen.ts now explicitly
JSON.stringify()s OVERLAY_CSS, so the 'passes verbatim' clause is no
longer true. The describe.each() inputs below are the concrete example.
std::mem::zeroed is on the workspace disallowed-methods list; the
bun_core::ffi::zeroed helper is the sanctioned form for zero-init of
C POD structs (same pattern as crash_handler and Global).
… siblings

Main tightened every other host fn in this module to private; the
unreachable_pub lint flags the one remaining pub.
@robobun
robobun force-pushed the farm/39e70373/sync-spawn-preserve-jsc-fault-handlers branch from 4aefdf6 to b658f64 Compare August 15, 2026 18:45
Comment thread src/codegen/bake-codegen.ts Outdated
Comment thread src/runtime/api/crash_handler_jsc.rs Outdated
@robobun

robobun commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto main (da12867). One conflict, in crash_handler_jsc.rs: main made generate() pub(crate); kept main's signature, the body (new getFaultSignalHandlers entry, ENTRIES.len() sizing) merged cleanly. Also made js_get_fault_signal_handlers private to match the sibling host fns main tightened, so unreachable_pub stays quiet.

Re-verified after rebase: the new test passes under bun bd, cargo clippy -p bun_runtime is clean. Functional diff is unchanged.

Comment thread src/runtime/api/crash_handler_jsc.rs Outdated
Comment thread src/runtime/api/crash_handler_jsc.rs Outdated
Comment thread src/runtime/api/crash_handler_jsc.rs Outdated
Comment thread src/spawn/lib.rs
Comment thread src/spawn/process.rs Outdated
The full explanation lives in the PR, commit message, and the test file
header; the source comments only need to say what not to do and where
to read why.
Comment thread src/runtime/api/crash_handler_jsc.rs Outdated
Comment thread src/spawn/process.rs Outdated
The regression test is the guard against re-adding the reset; the
history is in the commit that removed it.
Comment thread src/spawn/process.rs

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All prior feedback is addressed (including the reset_on_posix visibility nit, fixed in 49e6487) and this run found no new issues. Deferring final sign-off to a maintainer given this changes process-wide fault-signal dispositions in the crash-handler/JSC-VMTraps layering — the correctness argument that crash reporting is preserved rests on WTF's jscSignalHandler chaining (Signals.cpp:538-544, 594-596), which is worth a human confirming.

What was reviewed:

  • SignalForwarding::drop deletion vs. Bun__unregisterSignalsForForwarding's own snapshot restore — the removed call was redundant for the forwarding set and only clobbered SIGSEGV/SIGBUS/SIGILL/SIGFPE.
  • The new getFaultSignalHandlers probe: read-only sigaction(sig, NULL, &out), gated to bun:internal-for-testing, no exception-scope gaps.
  • Regression test hermeticity (git env pinned via GIT_CONFIG_NOSYSTEM/local gitconfig; ASLR handled by comparing partition shape, not addresses).
  • bake-codegen.ts JSON.stringify(OVERLAY_CSS) change and the two stale cross-references it invalidated — both now updated.
Extended reasoning...

Overview

The functional change is a single deleted line in src/spawn/process.rs: SignalForwarding::drop no longer calls bun_crash_handler::reset_on_posix() after Bun__unregisterSignalsForForwarding(). The rest is cleanup (drop the now-unused bun_crash_handler dep from bun_spawn, make reset_on_posix module-private) plus test scaffolding: a new getFaultSignalHandlers() probe in crash_handler_jsc.rs exposed via bun:internal-for-testing, and a regression test that compares fault-signal dispositions before/after a bun test --changed sync-spawn. A tangential build-from-source fix in bake-codegen.ts (wrapping OVERLAY_CSS in JSON.stringify) rides along with two stale-comment updates.

Security risks

None identified. The new probe is a read-only sigaction(sig, NULL, &out) query, only reachable via bun:internal-for-testing (gated behind BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING). No user input reaches native code beyond that, and no signal dispositions are changed by the new code — the fix is purely a deletion.

Level of scrutiny

High. The one-line deletion is small, but its correctness depends on a non-local invariant: that WTF's jscSignalHandler (installed on SIGSEGV/SIGBUS at JSC init) chains unhandled faults to the handler it displaced — Bun's crash reporter. If that chaining ever changed, removing reset_on_posix() would silently break crash reporting after any CLI sync-spawn. The author has cited the exact WTF source lines (Signals.cpp:594-596 for save, 538-544 for chain), and the regression test guards the observable invariant, but this is process-wide signal-handler layering across the JSC/Bun boundary and there's a companion WebKit PR (#233) — a maintainer familiar with that stack should confirm.

Other factors

Three prior review rounds from me have all been addressed: the stale json_lexer.rs and bun-build-api.test.ts cross-references, the .zig porting-spec question (declined with a reasonable rationale — the doc-comment + test guard the divergence), and the reset_on_posix pub → private nit (fixed in 49e6487). The comment-cop bot's paragraph-comment complaints were all trimmed. CI on the last full build was green on lanes exercising this code; the reds were documented fleet-wide breakages unrelated to this diff. The test is well-constructed (hermetic git env, ASLR-tolerant shape comparison, asserts the baseline is non-SIG_DFL so it can't pass vacuously) though it can only guard release behaviour indirectly since debug-ASAN builds never exhibited the regression.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant