Browse Source

chore: backport EPROTOTYPE fixes from libuv (#32856)

This commit backports three commits from libuv's 1.x branch to fix
issues with CPU going to 100% on macOS when EPROTOTYPE is returned.

See: https://github.com/libuv/libuv/commit/abb109f30f7c0b3615b75156376d2e886c365df8
See: https://github.com/libuv/libuv/commit/3a7b95593acaab9739404bd120baa62c2007a18a
See: https://github.com/libuv/libuv/commit/de24da8c111687a2871d528052f5442e9b371ca1
Fedor Indutny 3 years ago
parent
commit
fe43296f7f

+ 3 - 0
patches/node/.patches

@@ -28,3 +28,6 @@ fix_crash_caused_by_gethostnamew_on_windows_7.patch
 fix_suppress_clang_-wdeprecated-declarations_in_libuv.patch
 fix_don_t_create_console_window_when_creating_process.patch
 fix_serdes_test.patch
+darwin_remove_eprototype_error_workaround_3405.patch
+darwin_translate_eprototype_to_econnreset_3413.patch
+darwin_bump_minimum_supported_version_to_10_15_3406.patch

+ 25 - 0
patches/node/darwin_bump_minimum_supported_version_to_10_15_3406.patch

@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Ben Noordhuis <[email protected]>
+Date: Tue, 8 Feb 2022 14:18:29 +0100
+Subject: darwin: bump minimum supported version to 10.15 (#3406)
+
+We can't realistically claim to support 10.7 or any version that Apple
+no longer supports so let's bump the baseline to something more
+realistic.
+
+Refs: https://github.com/libuv/libuv/pull/482
+Refs: https://github.com/libuv/libuv/pull/3405
+
+diff --git a/deps/uv/SUPPORTED_PLATFORMS.md b/deps/uv/SUPPORTED_PLATFORMS.md
+index 30e0ea617a6fcaa5b4b7c7c5b117652e61f367d3..dc57dfb12dc7ddf8d29308ac44f46084a933d5ca 100644
+--- a/deps/uv/SUPPORTED_PLATFORMS.md
++++ b/deps/uv/SUPPORTED_PLATFORMS.md
+@@ -3,7 +3,7 @@
+ |  System | Support type | Supported versions | Notes |
+ |---|---|---|---|
+ | GNU/Linux | Tier 1 | Linux >= 2.6.32 with glibc >= 2.12 | |
+-| macOS | Tier 1 | macOS >= 10.7 | |
++| macOS | Tier 1 | macOS >= 10.15 | Current and previous macOS release |
+ | Windows | Tier 1 | >= Windows 8 | VS 2015 and later are supported |
+ | FreeBSD | Tier 1 | >= 10 | |
+ | AIX | Tier 2 | >= 6 | Maintainers: @libuv/aix |

+ 65 - 0
patches/node/darwin_remove_eprototype_error_workaround_3405.patch

@@ -0,0 +1,65 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Ben Noordhuis <[email protected]>
+Date: Sun, 9 Jan 2022 12:20:15 +0100
+Subject: darwin: remove EPROTOTYPE error workaround (#3405)
+
+It's been reported in the past that OS X 10.10, because of a race
+condition in the XNU kernel, sometimes returns a transient EPROTOTYPE
+error when trying to write to a socket. Libuv handles that by retrying
+the operation until it succeeds or fails with a different error.
+
+Recently it's been reported that current versions of the operating
+system formerly known as OS X fail permanently with EPROTOTYPE under
+certain conditions, resulting in an infinite loop.
+
+Because Apple isn't exactly forthcoming with bug fixes or even details,
+I'm opting to simply remove the workaround and have the error bubble up.
+
+Refs: https://github.com/libuv/libuv/pull/482
+
+diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c
+index bc64fe8f44b26d9f4c0d4d0d282b65cdf11a531b..1af448e7691392c3f7794eed1905d9132394e207 100644
+--- a/deps/uv/src/unix/stream.c
++++ b/deps/uv/src/unix/stream.c
+@@ -58,20 +58,6 @@ struct uv__stream_select_s {
+   fd_set* swrite;
+   size_t swrite_sz;
+ };
+-
+-/* Due to a possible kernel bug at least in OS X 10.10 "Yosemite",
+- * EPROTOTYPE can be returned while trying to write to a socket that is
+- * shutting down. If we retry the write, we should get the expected EPIPE
+- * instead.
+- */
+-# define RETRY_ON_WRITE_ERROR(errno) (errno == EINTR || errno == EPROTOTYPE)
+-# define IS_TRANSIENT_WRITE_ERROR(errno, send_handle) \
+-    (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS || \
+-     (errno == EMSGSIZE && send_handle != NULL))
+-#else
+-# define RETRY_ON_WRITE_ERROR(errno) (errno == EINTR)
+-# define IS_TRANSIENT_WRITE_ERROR(errno, send_handle) \
+-    (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
+ #endif /* defined(__APPLE__) */
+ 
+ static void uv__stream_connect(uv_stream_t*);
+@@ -866,17 +852,17 @@ static int uv__try_write(uv_stream_t* stream,
+ 
+     do
+       n = sendmsg(uv__stream_fd(stream), &msg, 0);
+-    while (n == -1 && RETRY_ON_WRITE_ERROR(errno));
++    while (n == -1 && errno == EINTR);
+   } else {
+     do
+       n = uv__writev(uv__stream_fd(stream), iov, iovcnt);
+-    while (n == -1 && RETRY_ON_WRITE_ERROR(errno));
++    while (n == -1 && errno == EINTR);
+   }
+ 
+   if (n >= 0)
+     return n;
+ 
+-  if (IS_TRANSIENT_WRITE_ERROR(errno, send_handle))
++  if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
+     return UV_EAGAIN;
+ 
+   return UV__ERR(errno);

+ 44 - 0
patches/node/darwin_translate_eprototype_to_econnreset_3413.patch

@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Ben Noordhuis <[email protected]>
+Date: Wed, 12 Jan 2022 16:11:43 +0100
+Subject: darwin: translate EPROTOTYPE to ECONNRESET (#3413)
+
+macOS versions 10.10 and 10.15 - and presumbaly 10.11 to 10.14, too -
+have a bug where a race condition causes the kernel to return EPROTOTYPE
+because the socket isn't fully constructed.
+
+It's probably the result of the peer closing the connection and that is
+why libuv translates it to ECONNRESET.
+
+Previously, libuv retried until the EPROTOTYPE error went away but some
+VPN software causes the same behavior except the error is permanent, not
+transient, turning the retry mechanism into an infinite loop.
+
+Refs: https://github.com/libuv/libuv/pull/482
+Refs: https://github.com/libuv/libuv/pull/3405
+
+diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c
+index 1af448e7691392c3f7794eed1905d9132394e207..9d22debf2bf5bd5912ade152e55a85ad652e3819 100644
+--- a/deps/uv/src/unix/stream.c
++++ b/deps/uv/src/unix/stream.c
+@@ -865,6 +865,20 @@ static int uv__try_write(uv_stream_t* stream,
+   if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
+     return UV_EAGAIN;
+ 
++#ifdef __APPLE__
++  /* macOS versions 10.10 and 10.15 - and presumbaly 10.11 to 10.14, too -
++   * have a bug where a race condition causes the kernel to return EPROTOTYPE
++   * because the socket isn't fully constructed. It's probably the result of
++   * the peer closing the connection and that is why libuv translates it to
++   * ECONNRESET. Previously, libuv retried until the EPROTOTYPE error went
++   * away but some VPN software causes the same behavior except the error is
++   * permanent, not transient, turning the retry mechanism into an infinite
++   * loop. See https://github.com/libuv/libuv/pull/482.
++   */
++  if (errno == EPROTOTYPE)
++    return UV_ECONNRESET;
++#endif  /* __APPLE__ */
++
+   return UV__ERR(errno);
+ }
+