diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index 1c695be2d5fa..af54f2af53aa 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -584,11 +584,15 @@ static void Execve(const FunctionCallbackInfo& args) { int saved_stdio_flags[3] = {-1, -1, -1}; for (int fd = 0; fd < 3; fd++) { int prev = persist_standard_stream(fd); - if (prev < 0) { + if (prev < 0 && errno != EBADF) { int fcntl_errno = errno; // Undo changes already applied to earlier fds before throwing. for (int j = 0; j < fd; j++) { - fcntl(j, F_SETFD, saved_stdio_flags[j]); + if (saved_stdio_flags[j] >= 0 && + fcntl(j, F_SETFD, saved_stdio_flags[j]) < 0) { + fcntl_errno = errno; + break; + } } env->ThrowErrnoException(fcntl_errno, "fcntl"); return; @@ -610,7 +614,11 @@ static void Execve(const FunctionCallbackInfo& args) { // throw an ErrnoException so JS can catch it. int execve_errno = errno; for (int fd = 0; fd < 3; fd++) { - fcntl(fd, F_SETFD, saved_stdio_flags[fd]); + if (saved_stdio_flags[fd] >= 0 && + fcntl(fd, F_SETFD, saved_stdio_flags[fd]) < 0) { + env->ThrowErrnoException(errno, "fcntl"); + return; + } } env->ThrowErrnoException(execve_errno, "execve", nullptr, *executable); } diff --git a/test/parallel/test-process-execve-closed-stdio.js b/test/parallel/test-process-execve-closed-stdio.js new file mode 100644 index 000000000000..69c128de6625 --- /dev/null +++ b/test/parallel/test-process-execve-closed-stdio.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const { closeSync } = require('fs'); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('process.execve is not available in Workers'); +} else if (common.isWindows || common.isIBMi) { + common.skip('process.execve is not available in Windows or IBM i'); +} + +if (process.argv[2] === 'child') { + closeSync(Number(process.argv[3])); + process.execve( + process.execPath, + [process.execPath, '-e', ''], + process.env, + ); + assert.fail('process.execve failed'); +} + +for (let fd = 0; fd < 3; fd++) { + const child = spawnSync( + process.execPath, + [__filename, 'child', String(fd)], + ); + assert.ifError(child.error); + assert.strictEqual( + child.status, + 0, + `process.execve failed with fd ${fd} closed: ${child.stderr}`, + ); +}