Skip to content

wasi: number WASI_SIG* constants in wasi_snapshot_preview1 order - #39392

Open
Jarred-Sumner wants to merge 2 commits into
mainfrom
claude/ledger-15257-wasi-signal-numbers
Open

wasi: number WASI_SIG* constants in wasi_snapshot_preview1 order#39392
Jarred-Sumner wants to merge 2 commits into
mainfrom
claude/ledger-15257-wasi-signal-numbers

Conversation

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

What

bun prog.wasm: WASI proc_raise(sig) looked the guest signal up in a table whose WASI_SIG* constants were numbered alphabetically (the pre-preview1 wasi_unstable order) instead of the wasi_snapshot_preview1 signal enum, so e.g. guest SIGTERM (15) was delivered to the host as SIGTRAP — which also printed the "Bun has crashed" banner. Renumbered to preview1 order (SIGHUP=1 … SIGSYS=30, matching wasi-libc __WASI_SIGNAL_*), added the five signals missing from SIGNAL_MAP, and proc_raise returns ENOTSUP when the host has no such signal (e.g. SIGPOLL/SIGPWR on macOS) instead of throwing out of the import.

Repro (before)

A hand-assembled wasm module that calls wasi_snapshot_preview1.proc_raise(15): bun raise.wasmsignal=SIGTRAP + crash banner (expected SIGTERM). proc_raise(6)SIGHUP, proc_raise(1)SIGALRM.

Tests

test/js/bun/wasm/wasi.test.js — "proc_raise uses wasi_snapshot_preview1 signal numbering" (all 30 + out-of-range → EINVAL, mocked kill) and "bun prog.wasm: proc_raise(SIGTERM) delivers SIGTERM to the host process". Fail on the current canary; pass here.

proc_raise(sig) looked the guest signal up in a table numbered
alphabetically, so e.g. SIGTERM (15) was delivered to the host as
SIGTRAP. Use the preview1 enum order (SIGHUP=1 .. SIGSYS=30) and add
the five signals that were missing.
@robobun

robobun commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator
Updated 9:32 PM PT - Aug 16th, 2026

@Jarred-Sumner, your commit 7b351964b94469fc905d9eb0d3c7ecde8d00a0b1 passed in Build #99839! 🎉


🧪   To try this PR locally:

bunx bun-pr 39392

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

bun-39392 --bun

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 25 minutes

Limit details: You’ve used all 1 included review currently available under your plan. You completed 71 included PR reviews in the past 7 days; at that activity level, included reviews refill at 1 review per hour.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6cbe8fd3-c682-4147-86be-dbc860764dc5

📥 Commits

Reviewing files that changed from the base of the PR and between fea1829 and 7b35196.

📒 Files selected for processing (2)
  • src/js/node/wasi.ts
  • test/js/bun/wasm/wasi.test.js

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

@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.

LGTM — the preview1 signal renumbering is correct against the spec and SIGNAL_MAP/proc_raise are the only consumers. The two inline notes are nits (a vacuous backstop assertion in the wasm fixture and an untested/broad catch on the ENOTSUP path); neither affects the fix itself.

Extended reasoning...

Overview

This PR renumbers the WASI_SIG* constants in src/js/node/wasi.ts from the old alphabetical (wasi_unstable) order to the wasi_snapshot_preview1 signal enum order (SIGHUP=1 … SIGSYS=30), adds the five previously-missing entries to SIGNAL_MAP (SIGPROF/SIGWINCH/SIGPOLL/SIGPWR/SIGSYS), and wraps bindings.kill in a try/catch that returns WASI_ENOTSUP when the host lacks a signal. Two tests are added: a mocked-kill table check for all 30 signals plus out-of-range → EINVAL, and an end-to-end bun raise.wasm subprocess that asserts proc_raise(15) delivers SIGTERM.

Security risks

None. This is a constant-table correction in the WASI polyfill; proc_raise already targeted process.pid and the change only fixes which signal name is passed. No new capability is exposed.

Level of scrutiny

Low-to-medium. The renumbering is mechanical and spec-verifiable — I checked it against the preview1 signal variant order and it matches exactly. Grep confirms WASI_SIG* constants are referenced nowhere outside wasi.ts, and SIGNAL_MAP is consumed only by proc_raise, so there are no other call sites to break. The end-to-end test's primary assertion (signalCode === "SIGTERM") is load-bearing and would fail on the pre-fix build where 15 mapped to SIGTRAP.

Other factors

Two nit-level findings will be posted inline: (1) the hand-assembled wasm fixture encodes i32.const 77 as a single byte, which signed-LEB128 decodes as −51, so the .not.toBe(77) backstop is vacuous — but the SIGTERM assertion above it still catches the regression; (2) the bare catch {} maps every error to ENOTSUP rather than only ERR_UNKNOWN_SIGNAL, and neither test exercises that branch. Both are worth tidying but don't affect correctness of the fix, so approving.

Comment thread test/js/bun/wasm/wasi.test.js
Comment thread src/js/node/wasi.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.

LGTM — both earlier nits are addressed: the catch is now narrowed to ERR_UNKNOWN_SIGNAL with rethrow (and covered by the new "reports ENOTSUP … rethrows anything else" test), and the wasm fixture's sentinel is now 42 so the .not.toBe(42) backstop is reachable.

Also checked: the WASI_SIG* constants are referenced nowhere outside SIGNAL_MAP, so the renumbering has no other consumers to break; the new preview1 order matches the cited spec and wasi-libc's __WASI_SIGNAL_*; proc_raise(0)/proc_raise(31)EINVAL bounds are asserted.

Extended reasoning...

Overview

Renumbers the WASI_SIG* constants in src/js/node/wasi.ts from the (buggy) alphabetical 0-24 order to the wasi_snapshot_preview1 signal enum order (1-30), adds the five previously-missing entries to SIGNAL_MAP, and makes proc_raise return WASI_ENOTSUP when the host's process.kill throws ERR_UNKNOWN_SIGNAL (rethrowing anything else). Three new tests in test/js/bun/wasm/wasi.test.js: a mocked-kill enumeration of all 30 signals + out-of-range → EINVAL, an ENOTSUP/rethrow test, and an end-to-end bun raise.wasm subprocess check that guest proc_raise(15) delivers host SIGTERM.

Prior review follow-up

My previous run left two 🟡 nits. The follow-up commit (7b351964) addresses both:

  • Over-broad catch {} → now catch (err) { if (err?.code === "ERR_UNKNOWN_SIGNAL") return WASI_ENOTSUP; throw err; }, and a dedicated test asserts both the ENOTSUP mapping (mock throws with code: "ERR_UNKNOWN_SIGNAL"proc_raise(28) === 58) and the rethrow path (mock throws a plain Error.toThrow("kill binding bug")). Deleting either clause of the handler now breaks a test.
  • LEB128 sentinel77 replaced with 42 (= 0x2A, bit 6 clear → decodes as +42) in the wasm body, the WAT comment, and the .not.toBe(42) assertion, so the fall-through backstop is no longer vacuous.

Security risks

None. proc_raise already called process.kill(process.pid, …) on a string from a fixed table; this PR only corrects which string maps to which guest integer and narrows an error handler. No new capability is exposed.

Level of scrutiny

Low-to-medium. This is a constant-table correction in a JS-side polyfill (node:wasi in Bun is a temporary vendored wasi-js shim, per the file header). The new numbering is spec-derived and the test hard-codes the same table independently with a spec citation. I grep-verified WASI_SIG* has no consumers outside SIGNAL_MAP in this file, so the renumbering can't silently break another call site.

Other factors

  • The mocked-kill test asserts the exact strong invariant (raised deep-equals the full 30-entry preview1 list in order), covers both boundaries (0 and 31 → EINVAL), and asserts raised.length didn't grow after the EINVAL calls.
  • The end-to-end test is skipIf(isWindows) (POSIX signals), uses tempDir/bunEnv/bunExe(), and its load-bearing assertion signalCode === "SIGTERM" would fail on the pre-fix build (which delivered SIGTRAP).
  • No outstanding human reviewer comments; CodeRabbit was rate-limited and left no findings.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants