• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2const common = require('../common');
3
4// Before https://github.com/nodejs/node/pull/2847 a child process trying
5// (asynchronously) to use the closed channel to it's creator caused a segfault.
6
7const assert = require('assert');
8const cluster = require('cluster');
9const net = require('net');
10
11if (!cluster.isMaster) {
12  // Exit on first received handle to leave the queue non-empty in master
13  process.on('message', function() {
14    process.exit(1);
15  });
16  return;
17}
18
19const server = net
20  .createServer(function(s) {
21    if (common.isWindows) {
22      s.on('error', function(err) {
23        // Prevent possible ECONNRESET errors from popping up
24        if (err.code !== 'ECONNRESET') throw err;
25      });
26    }
27    setTimeout(function() {
28      s.destroy();
29    }, 100);
30  })
31  .listen(0, function() {
32    const worker = cluster.fork();
33
34    worker.on('error', function(err) {
35      if (
36        err.code !== 'ECONNRESET' &&
37        err.code !== 'ECONNREFUSED' &&
38        err.code !== 'EMFILE'
39      ) {
40        throw err;
41      }
42    });
43
44    function send(callback) {
45      const s = net.connect(server.address().port, function() {
46        worker.send({}, s, callback);
47      });
48
49      // https://github.com/nodejs/node/issues/3635#issuecomment-157714683
50      // ECONNREFUSED or ECONNRESET errors can happen if this connection is
51      // still establishing while the server has already closed.
52      // EMFILE can happen if the worker __and__ the server had already closed.
53      s.on('error', function(err) {
54        if (
55          err.code !== 'ECONNRESET' &&
56          err.code !== 'ECONNREFUSED' &&
57          err.code !== 'EMFILE'
58        ) {
59          throw err;
60        }
61      });
62    }
63
64    worker.process.once(
65      'close',
66      common.mustCall(function() {
67        // Otherwise the crash on `channel.fd` access may happen
68        assert.strictEqual(worker.process.channel, null);
69        server.close();
70      })
71    );
72
73    worker.on('online', function() {
74      send(function(err) {
75        assert.ifError(err);
76        send(function(err) {
77          // Ignore errors when sending the second handle because the worker
78          // may already have exited.
79          if (err && err.code !== 'ERR_IPC_CHANNEL_CLOSED' &&
80                     err.code !== 'ECONNRESET' &&
81                     err.code !== 'ECONNREFUSED' &&
82                     err.code !== 'EMFILE') {
83            throw err;
84          }
85        });
86      });
87    });
88  });
89