Skip to content

Crashes with SIGILL on RISC-V (riscv64): next-server dies ~5s after Ready — needs --no-wasm-lazy-compilation #685

Description

@Acidmoon

pi-web crashes with SIGILL on RISC-V (riscv64): next-server dies ~5s after "Ready" — needs --no-wasm-lazy-compilation

Summary

On RISC-V single-board computers (SpacemiT K1 / Lichee Pi 3A in our case), pi-web starts normally but the next-server child process is killed by SIGILL (illegal instruction) about 5–8 seconds after ✓ Ready, with no client traffic involved. The parent monitor reports:

▲ Next.js 16.3.1
- Local:         http://127.0.0.1:30141
✓ Ready in 1632ms
⚠ next-swc does not have native bindings support for target triple [object Object]. ...
[pi-web] Next.js exited unexpectedly (signal SIGILL)

We traced it to a V8 bug in wasm lazy-compilation on riscv64 (see linked node report below). A one-line change to the spawn() call in bin/pi-web.js fully fixes it on our hardware. Verified stable for 90+ s with real page rendering.

Environment

Board Sipeed Lichee Pi 3A — SpacemiT K1, 8× T-Heng X60, 16 GB RAM
OS Bianbu 1.0.15 "Mantic Minotaur" (Ubuntu mantic base), kernel 6.1.15, glibc 2.38
CPU ISA rv64imafdcv_sscofpmf_sstc_svpbmt_zicbom_zicboz_zicbop_zihintpauseno Zba/Zbb/Zcb, no riscv_hwprobe syscall on this kernel
Node v26.0.0, unofficial-builds linux-riscv64 (non-pointer-compression); also reproduced on v24.20.0
pi-web 0.8.11 (Next.js 16.3.1)

Root cause chain

  1. Next.js has no native SWC binding for linux/riscv64, so it falls back to the wasm build of SWC (next/wasm/@next/swc-wasm-nodejs/wasm_bg.wasm). (The loader in next/dist/build/swc/index.js also lacks a riscv64 entry entirely — same failure mode documented here: https://dev.to/gounthar/the-one-line-patch-that-unlocked-nextjs-on-risc-v-a-detective-story-71h)
  2. V8's wasm lazy compilation is broken on riscv64 in these builds: a JS→wasm call jumps into a zero-filled (never-populated) code page → SIGILL. gdb shows the crash ra inside Builtins_JSToWasmWrapperAsm+184, crash PC in an anonymous code-space mapping filled with 0x00000000:
    Thread 1 "next-server (v1" received signal SIGILL, Illegal instruction.
    => 0x0000003fa86cba28:  unimp   (x/8i: eight consecutive `unimp`)
    x/8xw $pc-16: 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
    ra = Builtins_JSToWasmWrapperAsm + 184
    
    Kernel dump (identical in every crash, page offset 0xa28 constant, t0=0x6bae constant):
    next-server (v1[4259]: unhandled signal 4 code 0x1 at 0x00000011d183fa28
    cause: 0000000000000002   (RISC-V: illegal instruction)
    
  3. Disabling wasm lazy compilation (--no-wasm-lazy-compilation, i.e. eager compile at module instantiation) completely avoids the crash — everything else in pi-web works.

We verified that the following did not help (all still SIGILL): --no-wasm-code-gc, --no-wasm-tier-up, --no-liftoff, --no-flush-baseline-code --no-flush-liftoff-code --no-flush-bytecode --no-flush-code-based-on-time, OPENSSL_riscvcap= (empty), NEXT_TELEMETRY_DISABLED=1, downgrading to Node v24.20.0. Simple hand-written wasm (tiny function ×500k calls, and a 5000-function module) runs fine — the trigger requires the big Rust→wasm module (SWC).

Suggested fix

In bin/pi-web.js, the child spawn currently looks like:

const child = spawn(process.execPath, [nextBin, ...nextArgs], {
  cwd: pkgDir,
  stdio: ["inherit", "pipe", "inherit"],
  env: { ...process.env, PI_WEB_HOSTNAME: hostname },
});

Pass the flag as a real argv element (guard on process.arch, zero impact on other platforms):

-const child = spawn(process.execPath, [nextBin, ...nextArgs], {
+const child = spawn(
+  process.execPath,
+  [
+    // riscv64-wasm-lazy-workaround: V8 wasm lazy-compilation on riscv64
+    // jumps into unpopulated code pages -> SIGILL — nodejs/node#65724.
+    ...(process.arch === "riscv64" ? ["--no-wasm-lazy-compilation"] : []),
+    nextBin,
+    ...nextArgs,
+  ],
+  {
   cwd: pkgDir,
   stdio: ["inherit", "pipe", "inherit"],
   env: { ...process.env, PI_WEB_HOSTNAME: hostname },
-});
+  }
+);

Notes from our testing:

Reproduction (any riscv64 SBC with ≥4 GB RAM)

npm i -g @agegr/pi-web@0.8.11
pi-web --port 30141
# dies ~5-8s after "Ready"; monitor prints:
#   [pi-web] Next.js exited unexpectedly (signal SIGILL)
# control:
node --no-wasm-lazy-compilation "$(npm root -g)/@agegr/pi-web/bin/pi-web.js" --port 30141
# stays alive; GET / returns the real page (we served 9756-byte HTML repeatedly for 90s, 0 SIGILL)

Upstream links

— Acidmoon, KUBUDS Tech

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions