From: Sander Striker Date: Thu, 18 Mar 2004 07:36:53 +0000 (+0000) Subject: Fix starvation issue on listening sockets where a short-lived X-Git-Tag: 2.0.49~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a61b0a1aec8aa2d57f71b23b5928f3322217121d;p=thirdparty%2Fapache%2Fhttpd.git 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. Submitted by: Jeff Trawick Reviewed by: Jim Jagielski, Justin Erenkrantz git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/APACHE_2_0_BRANCH@103018 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 91c64bf7eb2..48d4394b67b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,14 @@ 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 aa4dceabfd4..24bd014d004 100644 --- a/configure.in +++ b/configure.in @@ -236,6 +236,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. @@ -489,6 +491,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 62c41c01aa9..3d071349d6f 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 a47cb7be53c..a7d73711edc 100644 --- a/os/unix/unixd.c +++ b/os/unix/unixd.c @@ -546,6 +546,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 1ff9b0fea8b..cad3fc90aed 100644 --- a/server/listen.c +++ b/server/listen.c @@ -329,6 +329,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 7d259ea56fc..f53b32cb901 100644 --- a/server/mpm/netware/mpm_netware.c +++ b/server/mpm/netware/mpm_netware.c @@ -829,9 +829,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; }