summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES9
-rw-r--r--configure.in7
-rw-r--r--include/ap_config.h3
-rw-r--r--os/unix/unixd.c13
-rw-r--r--server/listen.c20
-rw-r--r--server/mpm/netware/mpm_netware.c3
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
@@ -531,6 +531,19 @@ AP_DECLARE(apr_status_t) unixd_accept(void **accepted, ap_listen_rec *lr,
#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
case 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;
}