You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
process.execve() passes non-blocking stdio to the new program #66824
// repro.mjs// Touching process.stdout opens a libuv handle for the pipe, which sets O_NONBLOCK// on its open file description. Loading `node:util` does the same for stderr// (internal/util/colors reads process.stderr), so most programs hit this.process.stdout;constcheck='import fcntl, os; print("O_NONBLOCK on stdout:", bool(fcntl.fcntl(1, fcntl.F_GETFL) & os.O_NONBLOCK))';if(process.argv[2]==='spawn'){(awaitimport('node:child_process')).spawnSync('python3',['-c',check],{stdio: 'inherit'});}else{process.execve('/usr/bin/python3',['python3','-c',check]);}
$ node repro.mjs | catO_NONBLOCK on stdout: True
$ node repro.mjs spawn | catO_NONBLOCK on stdout: False
A program that writes more than a pipe buffer then fails:
How often does it reproduce? Is there a required condition?
Always, when a stdio fd is a pipe (or socket) and Node.js has opened its handle before process.execve().
What is the expected behavior? Why is that the expected behavior?
The new program gets blocking stdio, as a child started with child_process.spawn() does (libuv clears O_NONBLOCK on a child's fds 0-2) and as the next process does after Node.js exits normally (ResetStdio() restores the flags recorded at startup). Most programs assume blocking stdio.
What do you see instead?
The new program inherits O_NONBLOCK on fds 0-2, so its writes fail with EAGAIN once a pipe is full. Execve() in src/node_process_methods.cc clears FD_CLOEXEC on fds 0-2 but does not touch O_NONBLOCK, and it skips the exit path that would call ResetStdio().
Additional information
Restoring the startup O_NONBLOCK state of fds 0-2 before execve(2), as ResetStdio() does on exit, would fix it.
As a workaround, a caller can clear the flag through each stdio stream's handle before exec:
Activity
fix(vpx): exec the script in place from the project-local bin on Unix
fix(vpx): exec the script in place from the project-local bin on Unix