]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Fix starvation issue on listening sockets where a short-lived
authorSander Striker <striker@apache.org>
Thu, 18 Mar 2004 07:36:53 +0000 (07:36 +0000)
committerSander Striker <striker@apache.org>
Thu, 18 Mar 2004 07:36:53 +0000 (07:36 +0000)
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

CHANGES
configure.in
include/ap_config.h
os/unix/unixd.c
server/listen.c
server/mpm/netware/mpm_netware.c

diff --git a/CHANGES b/CHANGES
index 91c64bf7eb29363a73a0f39732693699c31f728a..48d4394b67bd2dc8001b713bc2fb3f913cd65cca 100644 (file)
--- 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]
 
index aa4dceabfd4baf94d913f7f695b757005f14efe7..24bd014d0041a83008a91a58c954eebee819f485 100644 (file)
@@ -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)])
index 62c41c01aa9b208967ebf8b25bc274e559d59886..3d071349d6fc461d0951244855c51d757c8801af 100644 (file)
 #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 */
 
index a47cb7be53cf49f52987899b449281571a4c7762..a7d73711edc79261ec2b010664b6fb1df5d5466e 100644 (file)
@@ -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
index 1ff9b0fea8b7c0678fd2dfe30932ed4594fb3af4..cad3fc90aed7c26f63c5187ce3049b3748d477ce 100644 (file)
@@ -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
index 7d259ea56fcf93e2e6c05f5e906b0b6c8731f144..f53b32cb9019ffb0725c232dadc52d3a53fd5c5e 100644 (file)
@@ -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;
 }