nodejs / nodejs/node

Concurrent PGlite WebAssembly initialization intermittently SIGSEGVs Node.js

Ouverte
#64,500 5 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Langage dominant
JavaScript
Étoiles
122k
Forks
37.3k
Merge moyen
4 j 2 h
PR mergées (30 j)
283

Description

Version

v26.5.0 (also reproduced on Node 22, 24, 25)

Platform
The primary Docker reproduction ran on this host:


Linux dragon-pro18 6.18.35-1-cachyos-lts #1 SMP PREEMPT_DYNAMIC Sat, 13 Jun 2026 12:39:40 +0000 x86_64 GNU/Linux
Intel Core Ultra 9 285HX, 24 cores online


Docker replaces the userspace and Node installation but shares the host kernel and CPU. The same failure occurs with the host Node binary.
Subsystem

V8 / WebAssembly (observed through child_process)

What steps will reproduce the bug?

The minimal repository is:

https://github.com/gadicc/node-pglite-wasm-sigsegv-repro

It has one runtime dependency, @electric-sql/pglite@0.5.4. PGlite ships its PostgreSQL engine as WebAssembly; there is no native addon.

On a machine with at least 20 GiB available:

git clone https://github.com/gadicc/node-pglite-wasm-sigsegv-repro.git
cd node-pglite-wasm-sigsegv-repro
docker build -t pglite-node-sigsegv-repro .
docker run --rm pglite-node-sigsegv-repro

Equivalently, with a local Node installation:

npm ci
npm run repro

Each of 16 child processes independently creates an in-memory PGlite client, runs SELECT 1, awaits client.close() in finally, and exits. The parent repeats that wave up to 50 times and stops on the first failure. There is no Vitest, Vite, test runner, application code, native addon, IPC dependency, or database persistence.

The memory warning matters: the observed cgroup peak was approximately 19 GiB. The recorded failing run had oom=0 and oom_kill=0.

How often does it reproduce? Is there a required condition?

It is intermittent and requires concurrent processes in the measured setup.

Two clean Node 26 official-image runs failed at waves 13 and 8. Other results
without V8 CLI overrides were:

Node Result
25 1/20 waves failed
24 4/45 waves failed
22 1/10 waves failed
20 0/40 waves failed

The Node 20 result is a non-reproduction in an intermittent test, not evidence that it is unaffected. Official Node 20, 22, 24, and 26 images expose the same relevant defaults: Liftoff, lazy compilation, dynamic tiering, and WASM tier-up are enabled, with up to 128 compilation tasks. Node 20 did reproduce with --no-liftoff, which forces the optimizing compiler.

The Node 27 V8 canary failed at wave 17 with child_process.fork() and failed 2/50 waves with ordinary child_process.spawn(). A sequential one-child control passed 50/50 waves.

What is the expected behavior? Why is that the expected behavior?

Every child should complete the query, close the PGlite client, and exit with code 0.

What do you see instead?

An intermittent child is terminated by SIGSEGV:

node=v26.5.0 v8=14.6.202.34-node.24 platform=linux arch=x64 children=16 waves=50
wave=1 passed=16/16
...
wave=8 passed=15/16
child=10 code=null signal=SIGSEGV elapsedMs=4315
failedWaves=1 completedWaves=8 requestedWaves=50
Additional information

Native diagnostics from a sanitized-environment reproduction:

  1. strace recorded an initial SIGSEGV with SEGV_MAPERR at a large guarded WASM address.
  2. Node's node::TrapWebAssemblyOrContinue called raise(SIGSEGV) after V8 declined to handle the address as a valid WASM trap.
  3. The retained core records the resulting SI_TKILL signal.
  4. GDB placed another crash in the lazy WASM compilation path through LiftoffAssembler::PrepareCall, ExecuteLiftoffCompilation, CompileLazy, and Runtime_WasmCompileLazy, with background Turboshaft compilation active. A separate retained crash was in v8::internal::MarkingBarrier::MarkValueLocal.

Relevant flag controls on Node 25:

  • default: reproduced;
  • --wasm-enforce-bounds-checks: reproduced;
  • --wasm-num-compilation-tasks=1: reproduced;
  • --no-wasm-tier-up: passed 100/100 waves in that version;
  • --no-liftoff: reproduced.

The Node 27 canary nevertheless reproduced with both --no-wasm-tier-up and --liftoff-only, so no single WASM tier is universally required.

I also built the exact PGlite 0.5.4 tag using its official pnpm build:all:debug workflow. That PostgreSQL WASM used -g,
-gsource-map, and --no-wasm-opt; LLVM verified 1,322 DWARF compilation units without errors. With Node 25.2.1, a sequential control passed 5/5 waves and eight concurrent children passed 5/5 waves, while 16 concurrent children
reproduced SIGSEGV in wave 2. The cgroup peak was 67,306,729,472 bytes (62.7 GiB), with oom=0 and oom_kill=0.

Runtime controls using the same PGlite version and concurrency:

  • Bun 1.3.11 / JavaScriptCore: 50/50 waves passed;
  • Deno 2.8.0 / V8 14.9.207.2, default: 50/50 passed;
  • Deno 2.8.0 / V8 14.9.207.2, --no-liftoff: 50/50 passed.

The evidence therefore establishes a Node-runtime-specific interaction in the tested configurations, but does not yet distinguish Node's WASM trap/embedding layer or build configuration from a V8 defect exposed by Node. d8 is V8's standalone JavaScript shell; reproducing there without Node or its APIs would prove that the bug exists independently in V8. I would appreciate guidance on whether this should be routed or cross-filed to V8.

On 2026-07-14 I searched open Node and V8 reports for PGlite, TrapWebAssemblyOrContinue, WasmCompileLazy, LiftoffAssembler, and concurrent WASM SIGSEGVs, and found no exact duplicate. The reproduction README records the related but materially different reports.

Lastly,

  • Co-authored with Codex gpt-5.6-sol, reasoning: high, with significant human input.
  • This issue was opened by me personally by hand and without any automation.

Edit 1: Added PGLite debug build info in "Additional Information" above.

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Clonez et exécutez le node-pglite-wasm-sigsegv-repro indiqué, en commençant par la reproduction Docker ou npm documentée et son contrôle séquentiel. Comparez le comportement défaillant de Node avec d8, puis examinez les chemins mentionnés de node::TrapWebAssemblyOrContinue et de la compilation WASM différée de V8. Le travail est terminé lorsque vous avez déterminé si le défaut relève de Node ou de V8 et fourni un correctif confirmé ou une recommandation d’orientation accompagnée d’un cas de régression reproductible.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
docker, javascript, nodejs, wasm
Domaine
backend, compilers, operating-systems
Type d'issue
Bug
Difficulté
5/5
Temps estimé
Plus d'une semaine
Activité
Active
Clarté
À clarifier
Accessibilité débutants
25/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.