Skip to content

Commit 4141e22

Browse files
lazergaduh95
authored andcommitted
src: don't kill own process group on failed spawn
libuv only assigns a pid to the process handle once uv_spawn() has succeeded, so a child that never started keeps pid 0. Calling kill() on such a child still reached uv_process_kill(), which ended up in kill(0, signal) and signalled every process in the caller's own process group, Node included. Return ESRCH when the handle has no pid, and zero the pid in the constructor so the check never reads an unassigned value. Signed-off-by: Lazizbek Ergashev <lazerg2@gmail.com> PR-URL: #65054 Fixes: #65052 Reviewed-By: Aviv Keller <me@aviv.sh> Reviewed-By: Robert Nagy <ronagy@icloud.com>
1 parent 816790e commit 4141e22

3 files changed

Lines changed: 25 additions & 1 deletion

File tree

src/process_wrap.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class ProcessWrap : public HandleWrap {
112112
object,
113113
reinterpret_cast<uv_handle_t*>(&process_),
114114
AsyncWrap::PROVIDER_PROCESSWRAP) {
115+
process_.pid = 0;
115116
MarkAsUninitialized();
116117
}
117118

@@ -355,7 +356,10 @@ class ProcessWrap : public HandleWrap {
355356
signal = SIGKILL;
356357
}
357358
#endif
358-
int err = uv_process_kill(&wrap->process_, signal);
359+
// uv_spawn() only assigns a pid when it succeeds, and kill(0, signal)
360+
// signals every process in our own process group.
361+
int err = wrap->process_.pid > 0 ? uv_process_kill(&wrap->process_, signal)
362+
: UV_ESRCH;
359363
args.GetReturnValue().Set(err);
360364
}
361365

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const { spawn } = require('child_process');
2+
const child = spawn('foo123');
3+
child.on('error', () => {});
4+
if (child.kill() !== false || child.killed !== false) process.exit(1);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict';
2+
const common = require('../common');
3+
const fixtures = require('../common/fixtures');
4+
const assert = require('assert');
5+
const { spawn } = require('child_process');
6+
7+
// Killing a child process that never spawned must not signal the process
8+
// group of the caller. The check runs in a detached child so that a
9+
// regression cannot take the test runner down with it.
10+
const childPath = fixtures.path('child-process-kill-spawn-error.js');
11+
const child = spawn(process.execPath, [childPath], { detached: true });
12+
13+
child.on('exit', common.mustCall((code, signal) => {
14+
assert.strictEqual(signal, null);
15+
assert.strictEqual(code, 0);
16+
}));

0 commit comments

Comments
 (0)