From bde62951accfc4bb0a516276fd0b4b307e140ce2 Mon Sep 17 00:00:00 2001 From: Ozan Tezcan Date: Wed, 14 May 2025 11:02:30 +0300 Subject: [PATCH] Retry accept() even if accepted connection reports an error (CVE-2025-48367) In case of accept4() returns an error, we should check errno value and decide if we should retry accept4() without waiting next event loop iteration. --- src/anet.c | 24 ++++++++++++++++++++++++ src/anet.h | 1 + src/cluster_legacy.c | 2 ++ src/socket.c | 2 ++ src/tls.c | 2 ++ src/unix.c | 2 ++ 6 files changed, 33 insertions(+) diff --git a/src/anet.c b/src/anet.c index ad4ac49d8..d79434cef 100644 --- a/src/anet.c +++ b/src/anet.c @@ -787,3 +787,27 @@ int anetIsFifo(char *filepath) { if (stat(filepath, &sb) == -1) return 0; return S_ISFIFO(sb.st_mode); } + +/* This function must be called after accept4() fails. It returns 1 if 'err' + * indicates accepted connection faced an error, and it's okay to continue + * accepting next connection by calling accept4() again. Other errors either + * indicate programming errors, e.g. calling accept() on a closed fd or indicate + * a resource limit has been reached, e.g. -EMFILE, open fd limit has been + * reached. In the latter case, caller might wait until resources are available. + * See accept4() documentation for details. */ +int anetAcceptFailureNeedsRetry(int err) { + if (err == ECONNABORTED) + return 1; + +#if defined(__linux__) + /* For details, see 'Error Handling' section on + * https://man7.org/linux/man-pages/man2/accept.2.html */ + if (err == ENETDOWN || err == EPROTO || err == ENOPROTOOPT || + err == EHOSTDOWN || err == ENONET || err == EHOSTUNREACH || + err == EOPNOTSUPP || err == ENETUNREACH) + { + return 1; + } +#endif + return 0; +} diff --git a/src/anet.h b/src/anet.h index 211421940..1d3aec9cd 100644 --- a/src/anet.h +++ b/src/anet.h @@ -53,5 +53,6 @@ int anetPipe(int fds[2], int read_flags, int write_flags); int anetSetSockMarkId(char *err, int fd, uint32_t id); int anetGetError(int fd); int anetIsFifo(char *filepath); +int anetAcceptFailureNeedsRetry(int err); #endif diff --git a/src/cluster_legacy.c b/src/cluster_legacy.c index 88dc99691..6f1635e9e 100644 --- a/src/cluster_legacy.c +++ b/src/cluster_legacy.c @@ -1253,6 +1253,8 @@ void clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) { while(max--) { cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport); if (cfd == ANET_ERR) { + if (anetAcceptFailureNeedsRetry(errno)) + continue; if (errno != EWOULDBLOCK) serverLog(LL_VERBOSE, "Error accepting cluster node: %s", server.neterr); diff --git a/src/socket.c b/src/socket.c index 241a54108..226b414f8 100644 --- a/src/socket.c +++ b/src/socket.c @@ -308,6 +308,8 @@ static void connSocketAcceptHandler(aeEventLoop *el, int fd, void *privdata, int while(max--) { cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport); if (cfd == ANET_ERR) { + if (anetAcceptFailureNeedsRetry(errno)) + continue; if (errno != EWOULDBLOCK) serverLog(LL_WARNING, "Accepting client connection: %s", server.neterr); diff --git a/src/tls.c b/src/tls.c index 65526c04f..a0733a4b6 100644 --- a/src/tls.c +++ b/src/tls.c @@ -771,6 +771,8 @@ static void tlsAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) while(max--) { cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport); if (cfd == ANET_ERR) { + if (anetAcceptFailureNeedsRetry(errno)) + continue; if (errno != EWOULDBLOCK) serverLog(LL_WARNING, "Accepting client connection: %s", server.neterr); diff --git a/src/unix.c b/src/unix.c index b61cb6d49..e26f0d27a 100644 --- a/src/unix.c +++ b/src/unix.c @@ -102,6 +102,8 @@ static void connUnixAcceptHandler(aeEventLoop *el, int fd, void *privdata, int m while(max--) { cfd = anetUnixAccept(server.neterr, fd); if (cfd == ANET_ERR) { + if (anetAcceptFailureNeedsRetry(errno)) + continue; if (errno != EWOULDBLOCK) serverLog(LL_WARNING, "Accepting client connection: %s", server.neterr);