From 99c033ed98ada56a81b2318f34a9d0efa110c8e8 Mon Sep 17 00:00:00 2001 From: Santiago Gimeno Date: Wed, 28 Dec 2022 12:59:51 +0100 Subject: [PATCH] src: fix crash on OnStreamRead on Windows On Windows it's perfectly possible that the `uv_tcp_t` `read_cb` is called with an error and a null `uv_buf_t` if it corresponds to a `UV_HANDLE_ZERO_READ` read. Handle this case without crashing. Fixes: https://github.com/nodejs/node/issues/40764 PR-URL: https://github.com/nodejs/node/pull/45878 Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- src/stream_base.cc | 7 +-- .../test-net-child-process-connect-reset.js | 47 +++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-net-child-process-connect-reset.js diff --git a/src/stream_base.cc b/src/stream_base.cc index 389aab28c5adff..31cbf8fa199f7f 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -583,9 +583,10 @@ void CustomBufferJSListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) { HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); - // To deal with the case where POLLHUP is received and UV_EOF is returned, as - // libuv returns an empty buffer (on unices only). - if (nread == UV_EOF && buf.base == nullptr) { + // In the case that there's an error and buf is null, return immediately. + // This can happen on unices when POLLHUP is received and UV_EOF is returned + // or when getting an error while performing a UV_HANDLE_ZERO_READ on Windows. + if (buf.base == nullptr && nread < 0) { stream->CallJSOnreadMethod(nread, Local()); return; } diff --git a/test/parallel/test-net-child-process-connect-reset.js b/test/parallel/test-net-child-process-connect-reset.js new file mode 100644 index 00000000000000..228ba8ed57fccc --- /dev/null +++ b/test/parallel/test-net-child-process-connect-reset.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { spawn } = require('child_process'); +const net = require('net'); + +if (process.argv[2] === 'child') { + const server = net.createServer(common.mustCall()); + server.listen(0, common.mustCall(() => { + process.send({ type: 'ready', data: { port: server.address().port } }); + })); +} else { + const cp = spawn(process.execPath, + [__filename, 'child'], + { + stdio: ['ipc', 'inherit', 'inherit'] + }); + + cp.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, null); + assert.strictEqual(signal, 'SIGKILL'); + })); + + cp.on('message', common.mustCall((msg) => { + const { type, data } = msg; + assert.strictEqual(type, 'ready'); + const port = data.port; + + const conn = net.createConnection({ + port, + onread: { + buffer: Buffer.alloc(65536), + callback: () => {}, + } + }); + + conn.on('error', (err) => { + // Error emitted on Windows. + assert.strictEqual(err.code, 'ECONNRESET'); + }); + + conn.on('connect', common.mustCall(() => { + cp.kill('SIGKILL'); + })); + })); +}