Skip to content

Classify Wasm traps as POSIX signals#685

Merged
brandonpayton merged 3 commits into
mainfrom
emdash/why-segmentation-fault-undb6
Jun 13, 2026
Merged

Classify Wasm traps as POSIX signals#685
brandonpayton merged 3 commits into
mainfrom
emdash/why-segmentation-fault-undb6

Conversation

@brandonpayton

@brandonpayton brandonpayton commented Jun 12, 2026

Copy link
Copy Markdown
Member

Purpose

Kandelo was reporting unexpected Wasm process traps through the POSIX-shaped wait status as generic SIGSEGV/139, so users saw Segmentation fault even when the engine trap was arithmetic or illegal control flow. This change makes those crash statuses more informative while preserving the existing wait-status contract.

The goal is not to claim perfect POSIX provenance from Wasm. It is to use the trap reason exposed by the engine when it is reliable enough, keep SIGSEGV for memory/stack/generic bounds failures, and avoid masking arbitrary user-space unreachable traps as successful exits.

Summary

  • add shared Wasm trap-to-signal classification for memory/bounds, stack, arithmetic, and illegal control-flow traps
  • propagate classified signal numbers through Node and browser process-worker crash finalization
  • reject non-Wasm executable bytes before worker launch with ENOEXEC, so native host binaries cannot surface as opaque WebAssembly.compile() crashes
  • keep guest examples from inheriting the host PATH, preventing posix_spawnp() from resolving native host tools such as /usr/bin/echo
  • make local Node workers avoid stale ignored host/dist/node-kernel-worker-entry.js artifacts when the TypeScript source is newer
  • cover trap classification in host tests and deterministic Chromium/Firefox/WebKit browser tests
  • make host kernel worker changes trigger browser smoke coverage, including the trap classifier spec

Notes

  • Firefox reports both linear-memory and table bounds traps as RuntimeError: index out of bounds, so the classifier treats generic Wasm bounds traps as SIGSEGV rather than claiming a memory/table distinction.
  • The staging browser suite runs the full fast app suite in Chromium and the trap classifier spec in Chromium/Firefox. WebKit trap-classifier coverage runs in the Browser demo smoke workflow because WebKit is not reliable inside the Nix staging browser job.
  • Replaces fork PR Classify Wasm traps as POSIX signals #680 so the same-repo staging/test gate can run.

Validation

  • nix develop -c bash -c 'cd host && npm exec vitest -- run test/trap-signals.test.ts test/wasm-trap.test.ts test/centralized-spawn.test.ts'
  • nix develop -c bash -c 'scripts/run-libc-tests.sh functional spawn'
  • nix develop -c bash -c 'cd apps/browser-demos && CI=true KANDELO_PLAYWRIGHT_PORT=5414 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npx playwright test wasm-trap-signal.spec.ts --project=chromium --project=firefox --project=webkit --workers=1'
  • nix develop -c bash -c 'cd host && npm run build'
  • Browser demo smoke tests: https://github.com/Automattic/kandelo/actions/runs/27442470901
  • Staging build: https://github.com/Automattic/kandelo/actions/runs/27442470918

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown

Phase B-1 matrix build status — pr-685-staging

ABI v15. 5 built, 0 failed, 5 total.

Package Arch Status Sha
kandelo-sdk wasm32 built 130a0f57
lamp wasm32 built 16a623c3
shell wasm32 built d8126d7e
wordpress wasm32 built 803c15e7
node-vfs wasm32 built 3963ff62

Auto-generated; replaced on each push. Raw data in the publish-status workflow artifact.

@brandonpayton brandonpayton force-pushed the emdash/why-segmentation-fault-undb6 branch 4 times, most recently from 637f6ae to 6d63ee8 Compare June 12, 2026 20:19
@brandonpayton brandonpayton force-pushed the emdash/why-segmentation-fault-undb6 branch from 6d63ee8 to b091822 Compare June 12, 2026 20:54
@github-actions github-actions Bot enabled auto-merge (squash) June 12, 2026 22:46
@github-actions

Copy link
Copy Markdown

prepare-merge: test-gate passed against the synthetic PR merge and binaries-abi-v15. Missing entries were built or promoted from PR staging only when their merged-tree cache key matched; merge-gate=success posted on PR HEAD and squash auto-merge enabled.

@brandonpayton brandonpayton disabled auto-merge June 13, 2026 04:07
@brandonpayton brandonpayton merged commit 057e789 into main Jun 13, 2026
57 checks passed
@brandonpayton brandonpayton deleted the emdash/why-segmentation-fault-undb6 branch June 13, 2026 04:07
brandonpayton added a commit that referenced this pull request Jun 13, 2026
## Purpose

Kandelo was reporting unexpected Wasm process traps through the
POSIX-shaped wait status as generic `SIGSEGV`/`139`, so users saw
`Segmentation fault` even when the engine trap was arithmetic or illegal
control flow. This change makes those crash statuses more informative
while preserving the existing wait-status contract.

The goal is not to claim perfect POSIX provenance from Wasm. It is to
use the trap reason exposed by the engine when it is reliable enough,
keep `SIGSEGV` for memory/stack/generic bounds failures, and avoid
masking arbitrary user-space `unreachable` traps as successful exits.

## Summary

- add shared Wasm trap-to-signal classification for memory/bounds,
stack, arithmetic, and illegal control-flow traps
- propagate classified signal numbers through Node and browser
process-worker crash finalization
- reject non-Wasm executable bytes before worker launch with `ENOEXEC`,
so native host binaries cannot surface as opaque `WebAssembly.compile()`
crashes
- keep guest examples from inheriting the host `PATH`, preventing
`posix_spawnp()` from resolving native host tools such as
`/usr/bin/echo`
- make local Node workers avoid stale ignored
`host/dist/node-kernel-worker-entry.js` artifacts when the TypeScript
source is newer
- cover trap classification in host tests and deterministic
Chromium/Firefox/WebKit browser tests
- make host kernel worker changes trigger browser smoke coverage,
including the trap classifier spec

## Notes

- Firefox reports both linear-memory and table bounds traps as
`RuntimeError: index out of bounds`, so the classifier treats generic
Wasm bounds traps as `SIGSEGV` rather than claiming a memory/table
distinction.
- The staging browser suite runs the full fast app suite in Chromium and
the trap classifier spec in Chromium/Firefox. WebKit trap-classifier
coverage runs in the Browser demo smoke workflow because WebKit is not
reliable inside the Nix staging browser job.
- Replaces fork PR #680 so the same-repo staging/test gate can run.

## Validation

- `nix develop -c bash -c 'cd host && npm exec vitest -- run
test/trap-signals.test.ts test/wasm-trap.test.ts
test/centralized-spawn.test.ts'`
- `nix develop -c bash -c 'scripts/run-libc-tests.sh functional spawn'`
- `nix develop -c bash -c 'cd apps/browser-demos && CI=true
KANDELO_PLAYWRIGHT_PORT=5414 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npx
playwright test wasm-trap-signal.spec.ts --project=chromium
--project=firefox --project=webkit --workers=1'`
- `nix develop -c bash -c 'cd host && npm run build'`
- Browser demo smoke tests:
https://github.com/Automattic/kandelo/actions/runs/27442470901
- Staging build:
https://github.com/Automattic/kandelo/actions/runs/27442470918
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant