openai / openai/codex

Linux sandbox: node child_process.spawnSync/execFileSync returns EPERM after the child exits successfully (status 0, stdout captured)

Open
#41,175 2 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug CLI sandbox windows-os
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 in CODEX_HOME/config.toml): still EPERM.
  • use_linux_sandbox_bwrap true/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 around exec (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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.