Repository navigation
process: restore blocking stdio before execve - #66985
AdamMagued wants to merge 1 commit into
Conversation
When Node.js initializes stdio handles (such as process.stdout or process.stderr), libuv configures O_NONBLOCK on the open file descriptions for fds 0, 1, and 2. Because process.execve() replaces the process image without invoking the standard exit cleanup path (which triggers ResetStdio()), the executed binary inherits fds 0-2 in non-blocking mode. Programs assuming blocking stdio fail with EAGAIN or BlockingIOError when writes exceed pipe capacity. Clear O_NONBLOCK on standard descriptors 0, 1, and 2 before invoking execve(2), matching the behavior of child_process.spawn(). If execve(2) subsequently fails, restore the previous file status flags alongside the file descriptor close-on-exec flags so no side effects remain. Fixes: nodejs#66824 Signed-off-by: AdamMagued <adamismailmageud@gmail.com>
|
Welcome to Node.js, and thank you for your first contribution! Before review, please take a moment to read:
Please make sure every commit is signed off. For a first pull request, GitHub Actions require collaborator approval and Jenkins CI must be started by a collaborator or triager, so an initial wait is normal. Caution AgentScan found account activity patterns that may be consistent with automation. This is a heuristic, not proof that this pull request was opened by an agent or violates policy. AI-assisted contributions are permitted, but automated tooling must not open pull requests without advance approval, and contributors must personally understand, test, verify, and take responsibility for every submitted change. See the AgentScan analysis, AI use policy, and automation policy for additional context. |
Description
When Node.js initializes stdio streams (such as evaluating
process.stdoutorprocess.stderr), libuv setsO_NONBLOCKon the underlying open file descriptions for standard descriptors 0, 1, and 2.In
src/node_process_methods.cc,Execve()clearsFD_CLOEXECon file descriptors 0-2 so they persist acrossexecve(2). However,Execve()bypasses the normal process exit sequence that invokesResetStdio(). Consequently, the replacement binary executed viaprocess.execve()inherits file descriptors 0-2 in non-blocking mode. Programs assuming blocking stdio (such aspython3,cat, oryes) fail withEAGAIN(Resource temporarily unavailable) once a pipe buffer fills up.This change:
O_NONBLOCKon standard streams (descriptors 0, 1, and 2) before callingexecve(2), matching the blocking stdio behavior provided to child processes inchild_process.spawn().F_GETFLfile status flags alongsideFD_CLOEXECifexecve(2)fails, preserving the invariant that a failedprocess.execve()leaves no observable side effects before throwing to JavaScript.test/parallel/test-process-execve-stdio-blocking.jsverifying that a program executed viaprocess.execve()receives blocking stdio across fds 0-2 and completes writes that exceed pipe buffer capacity withoutEAGAIN.Fixes: #66824