From b5f25a963cada60ada3230124585ad1fc9a1ad7a Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 18 Feb 2015 03:43:29 +0100 Subject: [PATCH] src: ensure that file descriptors 0-2 are valid Check that stdin, stdout and stderr map to open file descriptors and remap them to /dev/null if that isn't the case. Protects against information leaks or worse when io.js is started with closed stdio file descriptors. PR-URL: https://github.com/iojs/io.js/pull/875 Reviewed-By: Colin Ihrig Reviewed-By: Vladimir Kurchatkin --- src/node.cc | 17 ++++++++++++++++- test/parallel/test-stdio-closed.js | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-stdio-closed.js diff --git a/src/node.cc b/src/node.cc index 48c93b8b6a8a61..e060304bffc25c 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3366,7 +3366,22 @@ inline void PlatformInit() { sigset_t sigmask; sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR1); - CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr)); + const int err = pthread_sigmask(SIG_SETMASK, &sigmask, nullptr); + + // Make sure file descriptors 0-2 are valid before we start logging anything. + for (int fd = STDIN_FILENO; fd <= STDERR_FILENO; fd += 1) { + struct stat ignored; + if (fstat(fd, &ignored) == 0) + continue; + // Anything but EBADF means something is seriously wrong. We don't + // have to special-case EINTR, fstat() is not interruptible. + if (errno != EBADF) + abort(); + if (fd != open("/dev/null", O_RDWR)) + abort(); + } + + CHECK_EQ(err, 0); // Restore signal dispositions, the parent process may have changed them. struct sigaction act; diff --git a/test/parallel/test-stdio-closed.js b/test/parallel/test-stdio-closed.js new file mode 100644 index 00000000000000..fd081b8c11a798 --- /dev/null +++ b/test/parallel/test-stdio-closed.js @@ -0,0 +1,24 @@ +var common = require('../common'); +var assert = require('assert'); +var spawn = require('child_process').spawn; + +if (process.platform === 'win32') { + console.log('Skipping test, platform not supported.'); + return; +} + +if (process.argv[2] === 'child') { + process.stdout.write('stdout', function() { + process.stderr.write('stderr', function() { + process.exit(42); + }); + }); +} + +// Run the script in a shell but close stdout and stderr. +var cmd = '"' + process.execPath + '" "' + __filename + '" child 1>&- 2>&-'; +var proc = spawn('/bin/sh', ['-c', cmd], { stdio: 'inherit' }); + +proc.on('exit', common.mustCall(function(exitCode) { + assert.equal(exitCode, 42); +}));