diff --git a/pkgs/development/web/nodejs/fix-libuv-1.53-support.patch b/pkgs/development/web/nodejs/fix-libuv-1.53-support.patch new file mode 100644 index 000000000000..b446b11fbe81 --- /dev/null +++ b/pkgs/development/web/nodejs/fix-libuv-1.53-support.patch @@ -0,0 +1,62 @@ +Extracted from https://github.com/nodejs/node/pull/66282 + +diff --git a/src/node_sockaddr.cc b/src/node_sockaddr.cc +index 3f903f548e17..7c2285de32dd 100644 +--- a/src/node_sockaddr.cc ++++ b/src/node_sockaddr.cc +@@ -1046,8 +1046,8 @@ bool SocketAddressBlockListWrap::FastCheckString( + // FastOneByteString is not NUL-terminated. Copy onto the stack + // before any other work: a GC would invalidate `address`. + // +- // uv_ip6_addr only needs the address part (≤39 chars) plus an +- // optional %zone. A zone longer than UV_IF_NAMESIZE is unknown ++ // uv_ip6_addr only needs the address part (< INET6_ADDRSTRLEN chars) ++ // plus an optional %zone. A zone longer than UV_IF_NAMESIZE is unknown + // (scope_id 0), same as omitting it. Keep that behavior when the + // full string does not fit so Fast API and CheckString agree. + constexpr size_t kMax = INET6_ADDRSTRLEN + UV_IF_NAMESIZE; +@@ -1058,14 +1058,14 @@ bool SocketAddressBlockListWrap::FastCheckString( + } else if (family == AF_INET6) { + // uv_ip4_addr rejects %zone. Only IPv6 may drop an overlong zone + // (unknown zone → scope_id 0, same as uv_ip6_addr). +- // uv_ip6_addr copies the address part into a 40-byte buffer +- // (39 chars + NUL). A 40+ char prefix (mixed notation) is +- // truncated and fails inet_pton — do not keep the full prefix. ++ // uv_ip6_addr copies the address part into an INET6_ADDRSTRLEN ++ // buffer and truncates a longer one to INET6_ADDRSTRLEN - 1 chars ++ // before inet_pton. Do the same so both paths parse the same prefix. + const char* percent = + static_cast(memchr(address.data, '%', address.length)); + if (percent == nullptr) return false; +- const size_t addr_len = static_cast(percent - address.data); +- if (addr_len >= 40) return false; ++ size_t addr_len = static_cast(percent - address.data); ++ if (addr_len >= INET6_ADDRSTRLEN) addr_len = INET6_ADDRSTRLEN - 1; + memcpy(buf, address.data, addr_len); + buf[addr_len] = '\0'; + } else { +diff --git a/test/parallel/test-blocklist.js b/test/parallel/test-blocklist.js +index b0fe4303bfb2..c548917b1c77 100644 +--- a/test/parallel/test-blocklist.js ++++ b/test/parallel/test-blocklist.js +@@ -883,12 +883,17 @@ const util = require('util'); + blockList.addAddress('1.1.1.1'); + assert.strictEqual(blockList.check(`1.1.1.1%${'z'.repeat(200)}`), false); + +- // Mixed-notation IPv6 is 45 chars. uv_ip6_addr truncates the +- // address part to 39 when a zone is present, so this is a miss. ++ // Mixed-notation IPv6 is 45 chars. uv_ip6_addr keeps up to ++ // INET6_ADDRSTRLEN - 1 chars of the address part when a zone is ++ // present, so this is a hit on libuv 1.53+. + const mixed = 'ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255'; + blockList.addAddress(mixed, 'ipv6'); + assert.strictEqual(blockList.check(mixed, 'ipv6'), true); +- assert.strictEqual(blockList.check(`${mixed}%${'z'.repeat(200)}`, 'ipv6'), false); ++ const isModernLibuv = ((versionString) => { ++ const [major, minor] = versionString.split('.', 2).map(Number); ++ return major >= 1 && minor >= 53; ++ })(process.versions.uv); ++ assert.strictEqual(blockList.check(`${mixed}%${'z'.repeat(200)}`, 'ipv6'), isModernLibuv); + } + + // check() family parameter is case-insensitive. diff --git a/pkgs/development/web/nodejs/v26.nix b/pkgs/development/web/nodejs/v26.nix index fde53e108b69..a9d83fe617b0 100644 --- a/pkgs/development/web/nodejs/v26.nix +++ b/pkgs/development/web/nodejs/v26.nix @@ -50,6 +50,9 @@ buildNodejs { # https://github.com/NixOS/nixpkgs/issues/568974 ./memcpy-climits.patch + + # TODO: remove once support for libuv 1.53+ has landed upstream + ./fix-libuv-1.53-support.patch ] ++ gypPatches; }