diff options
author | jdp <jdp@FreeBSD.org> | 1999-11-18 03:01:06 +0000 |
---|---|---|
committer | jdp <jdp@FreeBSD.org> | 1999-11-18 03:01:06 +0000 |
commit | 1cd372d24f6ec30dbb7398b7b6fc79ceccb59b71 (patch) | |
tree | 1ea3f99b25d3246b7e2c966bb5e32cec83f0760a /lib/libc | |
parent | 26433095088568aa67001274ae59df87613c88f8 (diff) | |
download | FreeBSD-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/libc')
-rw-r--r-- | lib/libc/rpc/svc_tcp.c | 18 |
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); |