Linux sandbox: node child_process.spawnSync/execFileSync returns EPERM after the child exits successfully (status 0, stdout captured)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
Summary
Inside the Linux sandbox (codex sandbox -- …, and equally inside an agent turn under sandbox_mode = "workspace-write"), Node's child_process.spawnSync / execFileSync fails with EPERM after the child has already run successfully. The child executes and its stdout is captured (status: 0), but Node reports error: spawnSync <cmd> EPERM (errno -1), so execFileSync throws. Any build/tooling script that shells out from Node (e.g. git rev-parse HEAD) breaks. The same command outside the sandbox works.
Environment
- codex-cli 0.150.1 (native Linux binary), WSL2 Ubuntu 26.04 LTS, kernel per WSL2, system bubblewrap 0.11.1 at
/usr/bin/bwrap - Node 24.20.0 / 22.23.2 / 20.20.2 (all reproduce identically)
- macOS (seatbelt) does not reproduce
Repro
$ codex sandbox -- git rev-parse --short HEAD
16ce57d # plain git is fine
$ codex sandbox -- node -e 'const r=require("child_process").spawnSync("git",["rev-parse","--short","HEAD"]);console.log("status",r.status,"err",r.error?r.error.code:"none","out",String(r.stdout).trim())'
status 0 err EPERM out 16ce57d # child ran, output captured, but error=EPERM
execFileSync('git', ['rev-parse','HEAD']) therefore throws spawnSync git EPERM even though git returned 0 and printed the sha.
What we ruled out
- Node version: 24 / 22 / 20 identical.
features.use_legacy_landlock = true(via--enable,-c, and inCODEX_HOME/config.toml): still EPERM.use_linux_sandbox_bwraptrue/false in config.toml: still EPERM.- Running the same node one-liner directly under
bwrap(including--unshare-pid --as-pid-1) does not reproduce, so it does not look like the pid namespace alone; it looks like the syscall filter the sandbox runner applies aroundexec(the child's post-exit reap path in libuv —waitid/pidfd_*/kill(pid,0)class — gets EPERM).
Expected
spawnSync/execFileSync of an allowed command inside the sandbox should return without error when the child exits 0, as it does on macOS.
Impact
Any Node-based build step that spawns processes (git, npm run scripts calling git, etc.) fails inside the Linux sandbox, forcing users to either move work off Linux or use danger-full-access, which we'd rather not.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the issue with codex sandbox -- node -e ... and compare it with the direct bwrap reproduction described above. Trace the Linux sandbox runner's syscall filter around exec and the child's post-exit reap path, including the waitid/pidfd/kill class of operations. Done means allowed Node spawnSync and execFileSync calls return without EPERM after a successful child exit.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, node.js
- Domain
- operating-systems, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100