Skip to content

Commit 1f16c43

Browse files
committed
child_process: fix handle passing w large payloads
Fix situations in which the handle passed along with a message that has a large payload and can’t be read entirely by a single `recvmsg()` call isn’t associated with the message to which it belongs. PR-URL: #14588 Fixes: #13778 Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Santiago Gimeno <[email protected]>
1 parent 264e434 commit 1f16c43

File tree

2 files changed

+49
-3
lines changed

2 files changed

+49
-3
lines changed

lib/internal/child_process.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -448,10 +448,14 @@ function setupChannel(target, channel) {
448448

449449
var decoder = new StringDecoder('utf8');
450450
var jsonBuffer = '';
451+
var pendingHandle = null;
451452
channel.buffering = false;
452453
channel.onread = function(nread, pool, recvHandle) {
453454
// TODO(bnoordhuis) Check that nread > 0.
454455
if (pool) {
456+
if (recvHandle)
457+
pendingHandle = recvHandle;
458+
455459
// Linebreak is used as a message end sign
456460
var chunks = decoder.write(pool).split('\n');
457461
var numCompleteChunks = chunks.length - 1;
@@ -471,10 +475,12 @@ function setupChannel(target, channel) {
471475
// read because SCM_RIGHTS messages don't get coalesced. Make sure
472476
// that we deliver the handle with the right message however.
473477
if (isInternal(message)) {
474-
if (message.cmd === 'NODE_HANDLE')
475-
handleMessage(message, recvHandle, true);
476-
else
478+
if (message.cmd === 'NODE_HANDLE') {
479+
handleMessage(message, pendingHandle, true);
480+
pendingHandle = null;
481+
} else {
477482
handleMessage(message, undefined, true);
483+
}
478484
} else {
479485
handleMessage(message, undefined, false);
480486
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const cluster = require('cluster');
5+
const net = require('net');
6+
7+
const payload = 'a'.repeat(800004);
8+
9+
if (cluster.isMaster) {
10+
const server = net.createServer();
11+
12+
server.on('connection', common.mustCall((socket) => socket.unref()));
13+
14+
const worker = cluster.fork();
15+
worker.on('message', common.mustCall(({ payload: received }, handle) => {
16+
assert.strictEqual(payload, received);
17+
assert(handle instanceof net.Socket);
18+
server.close();
19+
handle.destroy();
20+
}));
21+
22+
server.listen(0, common.mustCall(() => {
23+
const port = server.address().port;
24+
const socket = new net.Socket();
25+
socket.connect(port, (err) => {
26+
assert.ifError(err);
27+
worker.send({ payload }, socket);
28+
});
29+
}));
30+
} else {
31+
process.on('message', common.mustCall(({ payload: received }, handle) => {
32+
assert.strictEqual(payload, received);
33+
assert(handle instanceof net.Socket);
34+
process.send({ payload }, handle);
35+
36+
// Prepare for a clean exit.
37+
process.channel.unref();
38+
handle.unref();
39+
}));
40+
}

0 commit comments

Comments
 (0)