From 4d8f4c26d62142dd48570038ab80d9e98f307a9b Mon Sep 17 00:00:00 2001 From: Jeff Trawick Date: Fri, 19 Mar 2004 11:16:03 +0000 Subject: [PATCH] *) SECURITY: CAN-2004-0174 (cve.mitre.org) Fix starvation issue on listening sockets where a short-lived connection on a rarely-accessed listening socket will cause a child to hold the accept mutex and block out new connections until another connection arrives on that rarely-accessed listening socket. With Apache 2.x there is no performance concern about enabling the logic for platforms which don't need it, so it is enabled everywhere except for Win32. [Jeff Trawick] (already in 2.0.49, propagating to mirrors now) git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@103029 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 9 +++++++++ configure.in | 7 +++++++ include/ap_config.h | 3 +++ os/unix/unixd.c | 13 +++++++++++++ server/listen.c | 20 ++++++++++++++++++++ server/mpm/netware/mpm_netware.c | 3 --- 6 files changed, 52 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 197396dafa..0e793cdf84 100644 --- a/CHANGES +++ b/CHANGES @@ -283,6 +283,15 @@ Changes with Apache 2.1.0-dev Changes with Apache 2.0.49 + *) SECURITY: CAN-2004-0174 (cve.mitre.org) + Fix starvation issue on listening sockets where a short-lived + connection on a rarely-accessed listening socket will cause a + child to hold the accept mutex and block out new connections until + another connection arrives on that rarely-accessed listening socket. + With Apache 2.x there is no performance concern about enabling the + logic for platforms which don't need it, so it is enabled everywhere + except for Win32. [Jeff Trawick] + *) mod_cgid: Fix storage corruption caused by use of incorrect pool. [Jeff Trawick] diff --git a/configure.in b/configure.in index 5f393e1004..68bc23bb0e 100644 --- a/configure.in +++ b/configure.in @@ -235,6 +235,8 @@ case $host in ;; esac +APR_SETVAR(AP_NONBLOCK_WHEN_MULTI_LISTEN, [1]) + dnl dnl Process command line arguments. This is done early in the process so the dnl user can get feedback quickly in case of an error. @@ -486,6 +488,11 @@ if test "$SINGLE_LISTEN_UNSERIALIZED_ACCEPT" = "1"; then [This platform doesn't suffer from the thundering herd problem]) fi +if test "$AP_NONBLOCK_WHEN_MULTI_LISTEN" = "1"; then + AC_DEFINE(AP_NONBLOCK_WHEN_MULTI_LISTEN, 1, + [Listening sockets are non-blocking when there are more than 1]) +fi + AC_DEFINE_UNQUOTED(AP_SIG_GRACEFUL, SIG$AP_SIG_GRACEFUL, [Signal used to gracefully restart]) AC_DEFINE_UNQUOTED(AP_SIG_GRACEFUL_STRING, "SIG$AP_SIG_GRACEFUL", [Signal used to gracefully restart (as a quoted string)]) AC_DEFINE_UNQUOTED(AP_SIG_GRACEFUL_SHORT, $AP_SIG_GRACEFUL, [Signal used to gracefully restart (without SIG prefix)]) diff --git a/include/ap_config.h b/include/ap_config.h index 04d01a4d08..6c0fd50a4b 100644 --- a/include/ap_config.h +++ b/include/ap_config.h @@ -230,6 +230,9 @@ #include "ap_config_auto.h" #include "ap_config_layout.h" #endif +#if defined(NETWARE) +#define AP_NONBLOCK_WHEN_MULTI_LISTEN 1 +#endif /* TODO - We need to put OS detection back to make all the following work */ diff --git a/os/unix/unixd.c b/os/unix/unixd.c index 082b3cf6a7..6d43fe70f5 100644 --- a/os/unix/unixd.c +++ b/os/unix/unixd.c @@ -530,6 +530,19 @@ AP_DECLARE(apr_status_t) unixd_accept(void **accepted, ap_listen_rec *lr, #endif #ifdef ENETUNREACH case ENETUNREACH: +#endif + /* EAGAIN/EWOULDBLOCK can be returned on BSD-derived + * TCP stacks when the connection is aborted before + * we call connect, but only because our listener + * sockets are non-blocking (AP_NONBLOCK_WHEN_MULTI_LISTEN) + */ +#ifdef EAGAIN + case EAGAIN: +#endif +#ifdef EWOULDBLOCK +#if !defined(EAGAIN) || EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif #endif break; #ifdef ENETDOWN diff --git a/server/listen.c b/server/listen.c index 56a99751bc..e6873baec4 100644 --- a/server/listen.c +++ b/server/listen.c @@ -383,6 +383,26 @@ static int ap_listen_open(apr_pool_t *pool, apr_port_t port) } old_listeners = NULL; +#if AP_NONBLOCK_WHEN_MULTI_LISTEN + /* if multiple listening sockets, make them non-blocking so that + * if select()/poll() reports readability for a reset connection that + * is already forgotten about by the time we call accept, we won't + * be hung until another connection arrives on that port + */ + if (ap_listeners->next) { + for (lr = ap_listeners; lr; lr = lr->next) { + apr_status_t status; + + status = apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, 1); + if (status != APR_SUCCESS) { + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, pool, + "ap_listen_open: unable to make socket non-blocking"); + return -1; + } + } + } +#endif /* AP_NONBLOCK_WHEN_MULTI_LISTEN */ + /* we come through here on both passes of the open logs phase * only register the cleanup once... otherwise we try to close * listening sockets twice when cleaning up prior to exec diff --git a/server/mpm/netware/mpm_netware.c b/server/mpm/netware/mpm_netware.c index 34e8cbf56e..27f0e51720 100644 --- a/server/mpm/netware/mpm_netware.c +++ b/server/mpm/netware/mpm_netware.c @@ -828,9 +828,6 @@ static int setup_listeners(server_rec *s) if (sockdes > listenmaxfd) { listenmaxfd = sockdes; } - /* Use non-blocking listen sockets so that we - never get hung up. */ - apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, 1); } return 0; } -- 2.40.0