From 39ea429d03041e35577f030e009a134cd73a347f 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 705b9e5ce..4d893a19c 100644 --- a/src/anet.c +++ b/src/anet.c @@ -786,3 +786,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 8ad5f4b0b..f1285c454 100644 --- a/src/anet.h +++ b/src/anet.h @@ -52,5 +52,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 18d7ff81f..b4ad6dc46 100644 --- a/src/cluster_legacy.c +++ b/src/cluster_legacy.c @@ -1250,6 +1250,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 33c28588a..48cee622e 100644 --- a/src/socket.c +++ b/src/socket.c @@ -298,6 +298,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 3cc504ad1..2f33bbc37 100644 --- a/src/tls.c +++ b/src/tls.c @@ -755,6 +755,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 eb5850765..abfce101a 100644 --- a/src/unix.c +++ b/src/unix.c @@ -101,6 +101,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);