Summary
On Android < 12 (kernel < 5.3, e.g. kernel 4.9 / SDK 29), the app-sandbox seccomp policy TRAPs pidfd_open as SIGSYS. Bun calls pidfd_open when spawning child processes, so anything that spawns crashes:
bun x cowsay ... → exit 134 (SIGABRT)
bun run <script-with-script> → error: script was terminated by signal SIGSYS
Bun.spawn() → exit 159
Root cause
Two-part:
- Bun's spawn path calls
pidfd_open, which Android seccomp TRAPs as SIGSYS instead of returning ENOSYS.
- Bun then installs its own
SIGSYS crash handler (SA_RESETHAND) at startup, so the trap lands in Bun's abort path rather than letting the shim's fallback run.
bun -e '...' (no child spawn) works fine; only child-process spawning fails.
Reproduction
bun x cowsay "hi" # exit 134
bun run hi # error: script was terminated by signal SIGSYS
Proposed fix
Add a SIGSYS handler to bun-shim.so (same technique as opencode-termux's libseccomp-shim):
- A
SIGSYS handler returns -ENOSYS for seccomp-trapped syscalls, so Bun falls back to waitpid.
- Interpose
sigaction() so Bun's own SA_RESETHAND crash handler cannot override ours.
Verified on aarch64/Android 10 (kernel 4.9): all child spawn paths work, and tests/run-tests.sh passes 9/9.
Environment
- Termux (aarch64), Android 10, kernel 4.9.186
- Bun 1.3.14
Summary
On Android < 12 (kernel < 5.3, e.g. kernel 4.9 / SDK 29), the app-sandbox seccomp policy TRAPs
pidfd_openasSIGSYS. Bun callspidfd_openwhen spawning child processes, so anything that spawns crashes:bun x cowsay ...→ exit 134 (SIGABRT)bun run <script-with-script>→error: script was terminated by signal SIGSYSBun.spawn()→ exit 159Root cause
Two-part:
pidfd_open, which Android seccomp TRAPs asSIGSYSinstead of returningENOSYS.SIGSYScrash handler (SA_RESETHAND) at startup, so the trap lands in Bun's abort path rather than letting the shim's fallback run.bun -e '...'(no child spawn) works fine; only child-process spawning fails.Reproduction
Proposed fix
Add a
SIGSYShandler tobun-shim.so(same technique as opencode-termux's libseccomp-shim):SIGSYShandler returns-ENOSYSfor seccomp-trapped syscalls, so Bun falls back towaitpid.sigaction()so Bun's ownSA_RESETHANDcrash handler cannot override ours.Verified on aarch64/Android 10 (kernel 4.9): all child spawn paths work, and
tests/run-tests.shpasses 9/9.Environment