Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions lib/internal/child_process.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,21 @@ function flushStdio(subprocess) {
}


function createSocket(pipe, readable) {
return net.Socket({ handle: pipe, readable });
function createSocket(pipe, readable, index) {
// Determine socket readability based on stdio index:
// - stdin (0): parent writes → readable: false
// - stdout/stderr (1,2): parent reads → readable: true
// - custom pipes (3+): parent writes by default → readable: false
// See: https://github.com/nodejs/node/issues/[fork-stdio-bug]
let actualReadable;
if (index !== undefined) {
// Use index-based logic for correctness
actualReadable = (index === 1 || index === 2);
} else {
// Fallback for when index is not provided (for compatibility)
actualReadable = readable;
}
return net.Socket({ handle: pipe, readable: actualReadable, writable: !actualReadable });
}


Expand Down Expand Up @@ -489,7 +502,7 @@ ChildProcess.prototype.spawn = function spawn(options) {

if (stream.handle) {
stream.socket = createSocket(this.pid !== 0 ?
stream.handle : null, i > 0);
stream.handle : null, i > 0, i);

if (i > 0 && this.pid !== 0) {
this._closesNeeded++;
Expand Down
39 changes: 39 additions & 0 deletions test/parallel/test-child-process-custom-pipe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const cp = require('child_process');

// Test that custom stdio pipes have correct readable/writable properties
// Regression test for pre-existing fork-stdio bug where custom pipes (stdio[3+])
// were incorrectly marked as readable instead of writable.

if (process.argv[2] === 'child') {
// Child process just exits
process.exit(0);
} else {
// Parent process: verify socket properties
const child = cp.fork(__filename, ['child'], {
stdio: [0, 'ignore', 'pipe', 'ipc', 'pipe']
});

// stdio[0] = inherit (stdin)
// stdio[1] = ignore
// stdio[2] = pipe (stderr)
// stdio[3] = ipc
// stdio[4] = pipe (custom pipe)

const customPipe = child.stdio[4];

// Custom pipe should be writable (parent writes data to child)
assert.strictEqual(customPipe.writable, true,
'Custom stdio pipe should be writable');

// Custom pipe should not be readable by default
assert.strictEqual(customPipe.readable, false,
'Custom stdio pipe should not be readable by default');

child.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 0);
}));
}
Loading