sendfile() not detecting closed connections.

Ian FREISLICH ianf at clue.co.za
Tue Nov 6 03:53:17 PST 2007


Andre Oppermann wrote:
> Ian FREISLICH wrote:
> > Andre Oppermann wrote:
> > 
> >>Ian FREISLICH wrote:
> >>
> >>>Hi
> >>>
> >>>System is 8.0-CURRENT.  I have the following piece of code:
> >>>
> >>>	rename(path, data);
> >>>	stat(data, &sb);
> >>>	len = snprintf(buffer, MAXBUFLEN, "BYTES %lld\r\n", sb.st_size);
> >>>	write(connection, buffer, len);
> >>>	sleep(10);
> >>>	if ((sendfile(fd, connection, 0, sb.st_size, NULL,
> >>>	    &sbytes, 0)) == -1 || sbytes != sb.st_size) {
> >>>		syslog(facility, "Problem writing data: %s, wrote %lld",
> >>>		    strerror(errno), sbytes);
> >>>		respool(fd, path);
> >>>		unlink(data)
> >>>		close(fd);
> >>>		return(-1);
> >>>	}
> >>>	else
> >>>		syslog(facility, "Download successful %ld", sbytes);
> >>>	close(fd);
> >>>	unlink(data);
> >>>
> >>>If, during the sleep, I terminate the connection so that netstat
> >>>reports:
> >>>
> >>>tcp4       0      0  127.0.0.1.666          127.0.0.1.58239        CLOSE_W
A
> > 
> > IT
> > 
> >>>tcp4       0      0  127.0.0.1.58239        127.0.0.1.666          FIN_WAI
T
> > 
> > _2
> > 
> >>>sendfile() reports success for files less than about 64k in size,
> >>>but I haven't been able to figure out where the threshold is.  It
> >>>erroneously reports that 41000 of the 64k were sent, but will say
> >>>the whole file was transferred up to about 64k.  The connection
> >>>filedescriptor is blocking.
> >>>
> >>>Any ideas?
> >>
> >>sendfile() reports the bytes written into the send socket buffer.  If
> >>there is a connection error it doesn't (and never did) look at how
> >>much data was still in the socket buffer. The sendfile(2) man page
> >>says: "[sbytes] If non-NULL, the system will write the total number
> >>of bytes sent on the socket to the variable pointed to by sbytes."
> >>This could be changed to subtract the remaining data in the socket
> >>buffer before reporting back.  One has to be careful though about
> >>other writes so that the number never goes negative.  There may be
> >>more data remaining in the socket buffer than from this write attempt
> >>alone.
> > 
> > 
> > The connection was closed about 6 seconds before I called sendfile().
> > Would sendfile() write to the socket buffer of a socket closed that
> > long ago?
> 
> The sendfile(2) syscall checks if the socket is still connected:
> 
> 	if ((so->so_state & SS_ISCONNECTED) == 0) {
> 		error = ENOTCONN;
> 		goto out;
> 	}
> 
> Due to timing circumstances the connection may still be technically
> connected while at the same time disconnecting when FIN exchange has
> not completed yet.
> 
> To prevent a sendfile(2) call on a disconnecting socket another test
> has to be added:
> 
> 	if (so->so_state & SS_ISDISCONNECTING) {
> 		error = EPIPE;		/* or ESHUTDOWN? */
> 		goto out;
> 	}
> 
> Please add this test after line 1841 in sys/kern/uipc_syscalls.c and
> test again.  The error code ESHUTDOWN seems more appropriate but is
> so far not documented in the sendfile(2) man page.  As it seems this
> bug has been present since the introduction of the sendfile syscall.

I'm not sure that this has changed much.  It errors with errno ==
EPIPE, but still claims it's placed 40960 bytes in the socket buffer.

A tight loop of netstat -anf inet never reveals a Send-Q greater
than 0 bytes, although it's entirely likely it missed the event.

But if I terminate the connection mid stream I can see a send queue
sticking around for a bit:
tcp4       0  43640  127.0.0.1.666          127.0.0.1.50916        CLOSE_WAIT

Ian

--
Ian Freislich



More information about the freebsd-current mailing list