-
Notifications
You must be signed in to change notification settings - Fork 83
Description
Describe the bug
[x64 on ARM64] Node.js child_process.spawn deadlocks when ZeroMQ background threads are active (Rosetta 2 interaction)
System Information
- Host OS: macOS (Apple Silicon / ARM64)
- Guest Linux: Debian/Ubuntu x64 (Running via Rosetta 2 emulation)
- Node.js Version: [email protected]
Description
I have encountered a consistent deadlock/hang issue when running a Node.js application in an x64 Linux container/VM on Apple Silicon (OrbStack).
The issue occurs when child_process.spawn() is called after initializing a ZeroMQ socket (zeromq.js). ZeroMQ initializes background I/O threads upon creation. When Node.js attempts to fork a child process while these threads are active in this emulated environment, the child process hangs indefinitely before it can exec the new command.
This appears to be a Fork-Safety / Deadlock issue exacerbated by the Rosetta 2 translation layer. The child process likely inherits a locked mutex (possibly from malloc or V8 internals) held by a ZeroMQ thread that no longer exists in the child process.
Note:
- This issue is 100% reproducible on OrbStack x64 emulation.
- It does not happen if I move the
spawncall before the ZeroMQ initialization. - Using
detached: trueandstdio: 'ignore'does not resolve the hang.
Reproduction Steps
- Start an x64 Linux instance in OrbStack on an M1/M2/M3 Mac.
- Install Node.js and
zeromq(npm install zeromq). - Run the following reproduction script:
const { spawn } = require("child_process");
const { Router } = require("zeromq");
// 1. Initialize ZeroMQ (Starts background C++ threads)
const server = new Router();
console.log("start ipc server");
// 2. Attempt to spawn a child process
// On OrbStack (x64), this hangs indefinitely.
const bashProcess = spawn("echo", ["hello"], {
stdio: ["ignore", "pipe", "pipe"],
});
// This line is never reached
console.log("bashProcess.pid", bashProcess.pid);
bashProcess.stdout.on("data", (data) => console.log(`stdout: ${data}`));
bashProcess.stderr.on("data", (data) => console.error(`stderr: ${data}`));
bashProcess.on("close", (code) => console.log(`child process exited with code ${code}`));Logs / Evidence
I attached strace -f to the process. The logs show a storm of SIGTRAP signals (typical for Rosetta/V8 JIT interaction) followed by the child process hanging on an unfinished syscall (likely a futex wait) immediately after memory allocation operations (brk).
The trace is dominated by syscall_0x... entries, indicating execution within the Rosetta translation layer.
strace -f -s 200 -o trace.log node index_zmq.jsClick to expand strace output
6884 --- SIGTRAP {si_signo=SIGTRAP, si_code=0xffefdbe9, si_pid=6879, si_uid=0} ---
6883 --- SIGTRAP {si_signo=SIGTRAP, si_code=0xffefdbe9, si_pid=6879, si_uid=0} ---
...
6886 brk(0x2 <unfinished ...>
...
6886 <... brk resumed>) = 0x8000001a1c30
...
6884 syscall_0x6aad140(0, 0, 0x6, 0, 0x1, 0x62) = 0x3fbfe
6884 syscall_0x6aad140(0, 0, 0x6, 0, 0x1, 0x62 <unfinished ...>
// The process hangs here indefinitely
Analysis
The strace output suggests that the child process is stuck waiting for a lock (Futex) immediately after creation. Since fork() only copies the calling thread, if a background thread (managed by ZeroMQ or V8) held a lock (like the heap allocator lock) during the fork, the child process sees the lock as "held" but the owner thread is gone.
While this is a known theoretical problem in multi-threaded programming (Fork-Safety), the Rosetta 2 emulation layer seems to significantly widen the race condition window or introduce additional locking complexities that make this fail consistently.
To Reproduce
No response
Expected behavior
The child process should spawn successfully and execute the command (e.g., print "hello"), regardless of whether background threads (like ZeroMQ) are running. The script should output the PID and the stdout content, rather than hanging indefinitely.
Diagnostic report (REQUIRED)
OrbStack info:
Version: 2.0.5
Commit: cfe47627f138ffd822c958553b0a93eaf2692c71 (v2.0.5)
System info:
macOS: 26.1 (25B78)
CPU: arm64, 12 cores
CPU model: Apple M3 Pro
Model: Mac15,6
Memory: 36 GiB
Full report: https://orbstack.dev/_admin/diag/orbstack-diagreport_2026-02-01T06-23-42.709549Z.zip
Screenshots and additional context (optional)
