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
This commit is contained in:
John Polstra 1999-11-18 03:01:06 +00:00
parent 1815eed869
commit e2e3d0a401
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=53323

View File

@ -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:
@ -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)
*/