summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorjdp <jdp@FreeBSD.org>1999-11-18 03:01:06 +0000
committerjdp <jdp@FreeBSD.org>1999-11-18 03:01:06 +0000
commit1cd372d24f6ec30dbb7398b7b6fc79ceccb59b71 (patch)
tree1ea3f99b25d3246b7e2c966bb5e32cec83f0760a /lib
parent26433095088568aa67001274ae59df87613c88f8 (diff)
downloadFreeBSD-src-1cd372d24f6ec30dbb7398b7b6fc79ceccb59b71.zip
FreeBSD-src-1cd372d24f6ec30dbb7398b7b6fc79ceccb59b71.tar.gz
For the TCP transport, put the listening socket in non-blocking
mode. This addresses a well-known race condition that can cause servers to hang in accept(). The relevant case is when somebody connects to the server and then immediately kills the connection by sending a TCP reset. On the server this causes select to report a ready condition on the socket, after which the accept call blocks because there is no longer any pending connection to accept. In -current there is already a work-around for this in the kernel. It was merged into -stable some time ago, but then David Greenman reverted it because it seemed to be causing a socket leak in some cases. (See uipc_socket.c revision 1.51.2.3.) Hence this userland fix is needed in -stable, and I plan to merge it into that branch soon because it fixes a potential DoS attack. It may also be needed in -current if the suspected socket leak turns out to be real. In any case, after thinking it over I believe the fix belongs in userland. An application shouldn't assume that a ready return from select guarantees that the subsequent I/O operation cannot block. A lot can happen between the select and the accept. A similar fix should most likely be applied to the Unix domain socket transport too. Submitted by: peter Reviewed by: jdp
Diffstat (limited to 'lib')
-rw-r--r--lib/libc/rpc/svc_tcp.c18
1 files changed, 18 insertions, 0 deletions
diff --git a/lib/libc/rpc/svc_tcp.c b/lib/libc/rpc/svc_tcp.c
index b0aafbf..90e37a0 100644
--- a/lib/libc/rpc/svc_tcp.c
+++ b/lib/libc/rpc/svc_tcp.c
@@ -49,6 +49,7 @@ static char *rcsid = "$FreeBSD$";
#include <string.h>
#include <rpc/rpc.h>
#include <sys/socket.h>
+#include <sys/ioctl.h>
#include <errno.h>
/*
@@ -131,6 +132,7 @@ svctcp_create(sock, sendsize, recvsize)
register struct tcp_rendezvous *r;
struct sockaddr_in addr;
int len = sizeof(struct sockaddr_in);
+ int on;
if (sock == RPC_ANYSOCK) {
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
@@ -139,6 +141,13 @@ svctcp_create(sock, sendsize, recvsize)
}
madesock = TRUE;
}
+ on = 1;
+ if (ioctl(sock, FIONBIO, &on) < 0) {
+ perror("svc_tcp.c - cannot turn on non-blocking mode");
+ if (madesock)
+ (void)close(sock);
+ return ((SVCXPRT *)NULL);
+ }
memset(&addr, 0, sizeof (addr));
addr.sin_len = sizeof(struct sockaddr_in);
addr.sin_family = AF_INET;
@@ -233,6 +242,7 @@ rendezvous_request(xprt)
struct tcp_rendezvous *r;
struct sockaddr_in addr;
int len;
+ int off;
r = (struct tcp_rendezvous *)xprt->xp_p1;
again:
@@ -251,6 +261,14 @@ rendezvous_request(xprt)
return (FALSE);
}
/*
+ * The listening socket is in FIONBIO mode and we inherit it.
+ */
+ off = 0;
+ if (ioctl(sock, FIONBIO, &off) < 0) {
+ close(sock);
+ return (FALSE);
+ }
+ /*
* make a new transporter (re-uses xprt)
*/
xprt = makefd_xprt(sock, r->sendsize, r->recvsize);
OpenPOWER on IntegriCloud