From 903f64796e65324db71ddedee5216a0704f42c1a Mon Sep 17 00:00:00 2001 From: Ryuhei Shima <65934663+islandryu@users.noreply.github.com> Date: Wed, 14 Jan 2026 19:39:15 +0900 Subject: [PATCH 1/6] cluster: fix port reuse between cluster Fixes: https://github.com/nodejs/node/issues/60086 PR-URL: https://github.com/nodejs/node/pull/60141 Reviewed-By: Matteo Collina --- lib/internal/cluster/primary.js | 25 +++-- lib/internal/cluster/round_robin_handle.js | 4 + lib/internal/cluster/shared_handle.js | 4 + ...test-cluster-port-reuse-between-workers.js | 93 +++++++++++++++++++ 4 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 test/sequential/test-cluster-port-reuse-between-workers.js diff --git a/lib/internal/cluster/primary.js b/lib/internal/cluster/primary.js index af17d39de44964..6ab845c6d122aa 100644 --- a/lib/internal/cluster/primary.js +++ b/lib/internal/cluster/primary.js @@ -271,8 +271,12 @@ function queryServer(worker, message) { return; const key = `${message.address}:${message.port}:${message.addressType}:` + - `${message.fd}:${message.index}`; - let handle = handles.get(key); + `${message.fd}` + (message.port === 0 ? `:${message.index}` : ''); + const cachedHandle = handles.get(key); + let handle; + if (cachedHandle && !cachedHandle.has(worker)) { + handle = cachedHandle; + } if (handle === undefined) { let address = message.address; @@ -298,17 +302,22 @@ function queryServer(worker, message) { handle = new RoundRobinHandle(key, address, message); } - handles.set(key, handle); + if (!cachedHandle) { + handles.set(key, handle); + } } handle.data ||= message.data; // Set custom server data - handle.add(worker, (errno, reply, handle) => { + handle.add(worker, (errno, reply, serverHandle) => { + if (!errno) { + handles.set(key, handle); // Update in case it was replaced. + } const { data } = handles.get(key); - - if (errno) - handles.delete(key); // Gives other workers a chance to retry. + if (!cachedHandle && errno) { + handles.delete(key); + } send(worker, { errno, @@ -316,7 +325,7 @@ function queryServer(worker, message) { ack: message.seq, data, ...reply, - }, handle); + }, serverHandle); }); } diff --git a/lib/internal/cluster/round_robin_handle.js b/lib/internal/cluster/round_robin_handle.js index 7ba28d7f4e6a51..c2453adc8067f9 100644 --- a/lib/internal/cluster/round_robin_handle.js +++ b/lib/internal/cluster/round_robin_handle.js @@ -137,3 +137,7 @@ RoundRobinHandle.prototype.handoff = function(worker) { this.handoff(worker); }); }; + +RoundRobinHandle.prototype.has = function(worker) { + return this.all.has(worker.id); +}; diff --git a/lib/internal/cluster/shared_handle.js b/lib/internal/cluster/shared_handle.js index 88e981fdf07038..b6b9ee1ffb1af5 100644 --- a/lib/internal/cluster/shared_handle.js +++ b/lib/internal/cluster/shared_handle.js @@ -47,3 +47,7 @@ SharedHandle.prototype.remove = function(worker) { this.handle = null; return true; }; + +SharedHandle.prototype.has = function(worker) { + return this.workers.has(worker.id); +}; diff --git a/test/sequential/test-cluster-port-reuse-between-workers.js b/test/sequential/test-cluster-port-reuse-between-workers.js new file mode 100644 index 00000000000000..77d6902964e76a --- /dev/null +++ b/test/sequential/test-cluster-port-reuse-between-workers.js @@ -0,0 +1,93 @@ +'use strict'; + +const common = require('../common'); +const cluster = require('cluster'); +const assert = require('assert'); + +const acts = { + WORKER1_SERVER1_CLOSED: { cmd: 'WORKER1_SERVER1_CLOSED' }, + WORKER2_SERVER1_STARTED: { cmd: 'WORKER2_SERVER1_STARTED' }, + WORKER1_SERVER2_CLOSED: { cmd: 'WORKER1_SERVER2_CLOSED' }, +}; + +if (cluster.isMaster) { + const currentHost = '::'; + const worker1 = cluster.fork({ + WORKER_ID: 'worker1', + HOST: currentHost, + }); + let worker2; + worker1.on('error', common.mustNotCall()); + worker1.on('message', onMessage); + + function createWorker2() { + worker2 = cluster.fork({ + WORKER_ID: 'worker2', + HOST: currentHost, + }); + worker2.on('error', common.mustNotCall()); + worker2.on('message', onMessage); + } + + function onMessage(msg) { + switch (msg.cmd) { + case acts.WORKER1_SERVER1_CLOSED.cmd: + createWorker2(); + break; + case acts.WORKER2_SERVER1_STARTED.cmd: + worker1.send(acts.WORKER2_SERVER1_STARTED); + break; + case acts.WORKER1_SERVER2_CLOSED.cmd: + worker1.kill(); + worker2.kill(); + break; + default: + assert.fail(`Unexpected message ${msg.cmd}`); + } + } +} else { + const WORKER_ID = process.env.WORKER_ID; + function createServer() { + return new Promise((resolve, reject) => { + const net = require('net'); + const PORT = 8000; + const server = net + .createServer((socket) => { + socket.end( + `Handled by worker ${process.env.WORKER_ID} (${process.pid})\n` + ); + }) + .on('error', (e) => { + reject(e); + }); + + server.listen( + { + port: PORT, + host: process.env.HOST, + }, + () => resolve(server) + ); + }); + } + (async () => { + const server1 = await createServer(); + if (WORKER_ID === 'worker2') { + process.send(acts.WORKER2_SERVER1_STARTED); + } else { + await createServer().catch(common.mustCall()); + await new Promise((r) => server1.close(r)); + process.send(acts.WORKER1_SERVER1_CLOSED); + + process.on('message', async (msg) => { + if (msg.cmd === acts.WORKER2_SERVER1_STARTED.cmd) { + const server2 = await createServer(); + await new Promise((r) => server2.close(r)); + process.send(acts.WORKER1_SERVER2_CLOSED); + } else { + assert.fail(`Unexpected message ${msg.cmd}`); + } + }); + } + })().then(common.mustCall()); +} From 6c96a63891044e7b23421d675d79cb1e1be647d8 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 13 Jan 2026 15:54:02 +0100 Subject: [PATCH 2/6] doc: fix v20 changelog after security release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/61371 Reviewed-By: Rafael Gonzaga Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Ulises Gascón Reviewed-By: Benjamin Gruenbaum --- doc/changelogs/CHANGELOG_V20.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/changelogs/CHANGELOG_V20.md b/doc/changelogs/CHANGELOG_V20.md index c25c70b830c072..366d1c4e3c8c1f 100644 --- a/doc/changelogs/CHANGELOG_V20.md +++ b/doc/changelogs/CHANGELOG_V20.md @@ -89,17 +89,11 @@ This is a security release. ### Notable Changes -lib: - * (CVE-2025-55132) disable futimes when permission model is enabled (RafaelGSS) * (CVE-2025-59465) add TLSSocket default error handler (RafaelGSS) - lib,permission: * (CVE-2025-55130) require full read and write to symlink APIs (RafaelGSS) - src: * (CVE-2025-59466) rethrow stack overflow exceptions in async\_hooks (Matteo Collina) - src,lib: * (CVE-2025-55131) refactor unsafe buffer creation to remove zero-fill toggle (Сковорода Никита Андреевич) - tls: * (CVE-2026-21637) route callback exceptions through error handlers (Matteo Collina) ### Commits From 659fd01b3ebb101a692c8e9a795c9d46397e4ea0 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 13 Jan 2026 15:54:21 +0100 Subject: [PATCH 3/6] doc: fix v22 changelog after security release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/61371 Reviewed-By: Rafael Gonzaga Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Ulises Gascón Reviewed-By: Benjamin Gruenbaum --- doc/changelogs/CHANGELOG_V22.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/changelogs/CHANGELOG_V22.md b/doc/changelogs/CHANGELOG_V22.md index 042ed5e8daffa5..17311bbd1df77a 100644 --- a/doc/changelogs/CHANGELOG_V22.md +++ b/doc/changelogs/CHANGELOG_V22.md @@ -79,17 +79,11 @@ This is a security release. ### Notable Changes -lib: - * (CVE-2025-59465) add TLSSocket default error handler * (CVE-2025-55132) disable futimes when permission model is enabled - lib,permission: * (CVE-2025-55130) require full read and write to symlink APIs - src: * (CVE-2025-59466) rethrow stack overflow exceptions in async\_hooks - src,lib: * (CVE-2025-55131) refactor unsafe buffer creation to remove zero-fill toggle - tls: * (CVE-2026-21637) route callback exceptions through error handlers ### Commits From d4cc54b8c8efad7c329ad56f75dc190279bccf5d Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 13 Jan 2026 15:54:43 +0100 Subject: [PATCH 4/6] doc: fix v24 changelog after security release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/61371 Reviewed-By: Rafael Gonzaga Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Ulises Gascón Reviewed-By: Benjamin Gruenbaum --- doc/changelogs/CHANGELOG_V24.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/changelogs/CHANGELOG_V24.md b/doc/changelogs/CHANGELOG_V24.md index bd0dd62352d0b3..d72707281ee547 100644 --- a/doc/changelogs/CHANGELOG_V24.md +++ b/doc/changelogs/CHANGELOG_V24.md @@ -68,17 +68,11 @@ This is a security release. ### Notable Changes -lib: - * (CVE-2025-59465) add TLSSocket default error handler (RafaelGSS) * (CVE-2025-55132) disable futimes when permission model is enabled (RafaelGSS) - lib,permission: * (CVE-2025-55130) require full read and write to symlink APIs (RafaelGSS) - src: * (CVE-2025-59466) rethrow stack overflow exceptions in async\_hooks (Matteo Collina) - src,lib: * (CVE-2025-55131) refactor unsafe buffer creation to remove zero-fill toggle (Сковорода Никита Андреевич) - tls: * (CVE-2026-21637) route callback exceptions through error handlers (Matteo Collina) ### Commits From b1ac7e49a464ecc3391f0d9286d69c3a61149a1d Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 13 Jan 2026 15:55:04 +0100 Subject: [PATCH 5/6] doc: fix v25 changelog after security release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/61371 Reviewed-By: Rafael Gonzaga Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Ulises Gascón Reviewed-By: Benjamin Gruenbaum --- doc/changelogs/CHANGELOG_V25.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/changelogs/CHANGELOG_V25.md b/doc/changelogs/CHANGELOG_V25.md index 53c4393f434dd8..44e0d369601ed5 100644 --- a/doc/changelogs/CHANGELOG_V25.md +++ b/doc/changelogs/CHANGELOG_V25.md @@ -52,18 +52,12 @@ This is a security release. ### Notable Changes -lib: - * (CVE-2025-59465) add TLSSocket default error handler (RafaelGSS) - permission: * (CVE-2026-21636) add network check on pipe\_wrap connect (RafaelGSS) * (CVE-2025-55130) require full read and write to symlink APIs (RafaelGSS) * (CVE-2025-55132) disable futimes when permission model is enabled (RafaelGSS) - src: * (CVE-2025-59466) rethrow stack overflow exceptions in async\_hooks (Matteo Collina) - src,lib: * (CVE-2025-55131) refactor unsafe buffer creation to remove zero-fill toggle (Сковорода Никита Андреевич) - tls: * (CVE-2026-21637) route callback exceptions through error handlers (Matteo Collina) ### Commits From 1409ac4ffbc168359656dfbf23b923379976e59c Mon Sep 17 00:00:00 2001 From: Daijiro Wachi Date: Wed, 14 Jan 2026 20:07:37 +0900 Subject: [PATCH 6/6] doc: restore @watilde to collaborators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/nodejs/TSC/issues/1813 PR-URL: https://github.com/nodejs/node/pull/61350 Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: Michaël Zasso Reviewed-By: Richard Lau Reviewed-By: Marco Ippolito Reviewed-By: Ulises Gascón Reviewed-By: Gürgün Dayıoğlu Reviewed-By: Ruben Bridgewater Reviewed-By: Xuguang Mei Reviewed-By: Benjamin Gruenbaum --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a7ea15defe7152..795739a9b07f5e 100644 --- a/README.md +++ b/README.md @@ -453,6 +453,8 @@ For information about the governance of the Node.js project, see **Vladimir Morozov** <> (he/him) * [VoltrexKeyva](https://github.com/VoltrexKeyva) - **Mohammed Keyvanzadeh** <> (he/him) +* [watilde](https://github.com/watilde) - + **Daijiro Wachi** <> (he/him) * [zcbenz](https://github.com/zcbenz) - **Cheng Zhao** <> (he/him) * [ZYSzys](https://github.com/ZYSzys) - @@ -717,8 +719,6 @@ For information about the governance of the Node.js project, see **Vladimir Kurchatkin** <> * [vsemozhetbyt](https://github.com/vsemozhetbyt) - **Vse Mozhet Byt** <> (he/him) -* [watilde](https://github.com/watilde) - - **Daijiro Wachi** <> (he/him) * [watson](https://github.com/watson) - **Thomas Watson** <> * [whitlockjc](https://github.com/whitlockjc) -