From e2e3d0a401f5e0cfcf049dc6063cc7f9f9407426 Mon Sep 17 00:00:00 2001 From: John Polstra Date: Thu, 18 Nov 1999 03:01:06 +0000 Subject: [PATCH] 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 --- lib/libc/rpc/svc_tcp.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/libc/rpc/svc_tcp.c b/lib/libc/rpc/svc_tcp.c index b0aafbfe745..90e37a00dfa 100644 --- a/lib/libc/rpc/svc_tcp.c +++ b/lib/libc/rpc/svc_tcp.c @@ -49,6 +49,7 @@ static char *rcsid = "$FreeBSD$"; #include #include #include +#include #include /* @@ -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: @@ -250,6 +260,14 @@ rendezvous_request(xprt) close(sock); 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) */ -- 2.45.2