Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
76a38f7
ffi: clear SA_ONSTACK on SIGPWR after dlopen
robobun May 21, 2026
efe6944
[autofix.ci] apply automated fixes
autofix-ci[bot] May 21, 2026
44ba51c
comment why Bun__repairJscGcSignalAfterDlopen exists
robobun May 21, 2026
05c79cc
drop the watchdog setTimeout — await-using + test timeout cover it
robobun May 21, 2026
0ef5a49
[autofix.ci] apply automated fixes
autofix-ci[bot] May 21, 2026
ce6f4c1
ship the real fix from WebKit#235, drop the band-aid
robobun May 21, 2026
c0c0e6c
Merge remote-tracking branch 'origin/main' into farm/ebd23fe3/fix-311…
robobun May 23, 2026
be86d91
ci: retrigger — WebKit #235 preview autobuild is now published
robobun May 23, 2026
14c08e5
Merge remote-tracking branch 'origin/main' into farm/ebd23fe3/fix-311…
robobun May 23, 2026
ee62500
Merge remote-tracking branch 'origin/main' into farm/ebd23fe3/fix-311…
robobun May 25, 2026
a42d8e1
Merge remote-tracking branch 'origin/main' into farm/ebd23fe3/fix-311…
robobun May 26, 2026
5700893
Merge remote-tracking branch 'origin/main' into farm/ebd23fe3/fix-311…
robobun Jun 2, 2026
008c293
Merge remote-tracking branch 'origin/main' into farm/ebd23fe3/fix-311…
robobun Jun 16, 2026
000610a
build: point WEBKIT_VERSION at rebased oven-sh/WebKit#235 preview (66…
robobun Jun 16, 2026
1f4deae
Merge remote-tracking branch 'origin/main' into farm/ebd23fe3/fix-311…
robobun Jun 17, 2026
152f661
Merge remote-tracking branch 'origin/main' into farm/ebd23fe3/fix-311…
robobun Jun 28, 2026
0968c00
build: align WEBKIT_VERSION to WebKit#235 697e8a79 (comment fix)
robobun Jun 28, 2026
7a95440
test(31158): filter benign ASAN stderr banner before asserting
robobun Jun 28, 2026
e8111d1
ci: retrigger (WebKit#235 preview tarball autobuild-preview-pr-235-69…
robobun Jun 28, 2026
8a95f9b
Merge remote-tracking branch 'origin/main' into farm/ebd23fe3/fix-311…
robobun Jul 1, 2026
63e2247
Merge remote-tracking branch 'origin/main' into farm/ebd23fe3/fix-311…
robobun Jul 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion scripts/build/deps/webkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
* for local mode. Override via `--webkit-version=<hash>` to test a branch.
* From https://github.com/oven-sh/WebKit releases.
*/
export const WEBKIT_VERSION = "3167a44fb92c268c83f09b232b38a9f3e7f9655a";
// Preview autobuild of oven-sh/WebKit#235 — ucontext-SP fix for JSC's
// signalHandlerSuspendResume so the GC thread-suspend signal works under
// SA_ONSTACK (Go cgo's initsig etc.) instead of spinning forever. Swap
// back to the next merged hash once #235 lands.
export const WEBKIT_VERSION = "autobuild-preview-pr-235-a355e3e0";

Check warning on line 10 in scripts/build/deps/webkit.ts

View check run for this annotation

Claude / Claude Code Review

prebuiltDestDir cache-key collision for autobuild-preview-* version string

nit: `prebuiltDestDir()` keys the cache extraction dir on `cfg.webkitVersion.slice(0, 16)`, and `"autobuild-preview-pr-235-a355e3e0".slice(0, 16) === "autobuild-previe"` — the PR number and commit hash are truncated away, so every `autobuild-preview-*` tag (including a future revision of #235) maps to the same shared-cache dir. The `.identity` stamp catches this within a single worktree, but with the machine-shared `$BUN_INSTALL/build-cache` across worktrees the colliding `-I` path can let ninja
Comment thread
claude[bot] marked this conversation as resolved.
Outdated

/**
* WebKit (JavaScriptCore) — the JS engine.
Expand Down
89 changes: 89 additions & 0 deletions test/regression/issue/31158.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Regression test for https://github.com/oven-sh/bun/issues/31158.
//
// When a shared library (e.g. Go's cgo runtime) force-sets `SA_ONSTACK` on
// JavaScriptCore's thread-suspend signal (SIGPWR on Linux in Bun's WebKit
// fork), every delivery of that signal on a thread that has an alternate
// signal stack (ASAN runtime, Bun's crash handler, libbacktrace) runs the
// handler on the alt stack. WTF's stack-check in `signalHandlerSuspendResume`
// used `currentStackPointer()` — the handler's own SP — so the sanity
// check failed every time, `Thread::suspend()` retried forever, and the
// event loop stopped advancing on the next WASM compile/install.
//
// Triggers the exact shape of the bug without needing Go installed in CI:
// force `SA_ONSTACK` onto SIGPWR via `bun:ffi`, then call `dlopen` once
// more so the repair fires, then compile+call a WASM function (which
// triggers `resetInstructionCacheOnAllThreads`). Without the fix,
// `setTimeout` after the WASM call never fires and the child hangs.
import { expect, test } from "bun:test";
import { bunEnv, bunExe, isGlibc } from "harness";

// The bug is only reachable on Linux: SIGPWR is only used as the suspend/
// resume signal on Linux (see `vendor/WebKit/Source/WTF/wtf/posix/
// ThreadingPOSIX.cpp` — USE(BUN_JSC_ADDITIONS) branch). The reproduction
// dlopens `libc.so.6` directly so it needs glibc; musl names its libc
// differently and static-musl builds of Bun don't even have a dynamic
// loader in the room.
test.skipIf(!isGlibc)(
"event loop survives SA_ONSTACK on SIGPWR + WASM (oven-sh/bun#31158)",
async () => {
const script = /* ts */ `
import { dlopen, FFIType, ptr } from "bun:ffi";

// 1. Open libc so we can flip SA_ONSTACK on SIGPWR the same way Go's
// cgo 'initsig' does on every inherited handler.
const libc = dlopen("libc.so.6", {
sigaction: { args: [FFIType.i32, FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
});

// x86_64 glibc layout: sa_handler (8), sa_mask (128), sa_flags (4), sa_restorer (8).
// We only touch sa_flags so this layout holds on aarch64 too (same offsets).
const SIGACTION_SIZE = 152;
const FLAGS_OFFSET = 8 + 128;
const SIGPWR = 30;
const SA_ONSTACK = 0x08000000;

const buf = new ArrayBuffer(SIGACTION_SIZE);
const view = new DataView(buf);

libc.symbols.sigaction(SIGPWR, null, ptr(buf));
view.setInt32(FLAGS_OFFSET, view.getInt32(FLAGS_OFFSET, true) | SA_ONSTACK, true);
libc.symbols.sigaction(SIGPWR, ptr(buf), null);

// 2. Trigger a fresh dlopen — the fix clears SA_ONSTACK here.
dlopen("libc.so.6", { getpid: { args: [], returns: FFIType.i32 } });

Check warning on line 53 in test/regression/issue/31158.test.ts

View check run for this annotation

Claude / Claude Code Review

Stale test comment/step references removed post-dlopen band-aid

These comments (and the file-header at lines 13-14, "call `dlopen` once more so the repair fires") describe the Bun-side `repair_jsc_gc_signal_after_dlopen()` band-aid that was deleted in ce6f4c16. The shipped fix is now the WTF ucontext-SP change (oven-sh/WebKit#235), which *tolerates* `SA_ONSTACK` rather than clearing it — nothing runs at this second `dlopen` anymore. Either drop step 2 (it's vestigial) or reword the comments to reflect the actual mechanism.
Comment thread
claude[bot] marked this conversation as resolved.
Outdated

// 3. Exercise the WASM path that calls resetInstructionCacheOnAllThreads.
// The bytes below are a minimal WASM module with one exported function
// that loops 10_000 times and returns — enough to trigger compilation.
const bytes = new Uint8Array([
0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 127, 3, 2, 1, 0, 7, 5,
1, 1, 102, 0, 0, 10, 34, 1, 32, 1, 1, 127, 65, 0, 33, 0, 2, 64, 3, 64,
32, 0, 65, 1, 106, 33, 0, 32, 0, 65, 144, 206, 0, 72, 13, 0, 11, 11, 32,
0, 11,
]);
const inst = new WebAssembly.Instance(new WebAssembly.Module(bytes));
(inst.exports.f as () => number)();

// 4. A setTimeout that must fire — the bug makes this hang forever.
await new Promise(r => setTimeout(r, 10));
console.log("EVENT_LOOP_ALIVE");
`;

// `await using` scopes the child to this block — if the test-runner
// timeout fires because the bug reproduced, `Bun.spawn`'s disposer
// kills the child on the way out, no harness-side watchdog needed.
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", script],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});

const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);

expect(stderr).toBe("");
Comment thread
robobun marked this conversation as resolved.
Outdated
expect(stdout).toContain("EVENT_LOOP_ALIVE");
expect(exitCode).toBe(0);
},
15_000,
);
Loading