Bug 32827

Summary: small SO_RCVTIMEO values are taken to be zero
Product: Base System Reporter: brandt <brandt>
Component: kernAssignee: Maxim Konovalov <maxim>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 5.0-CURRENT   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
file.diff none

Description brandt 2001-12-14 11:10:01 UTC
If the receive timeout of a socket is set to a number of microseconds larger
than 0 but lesser than the value of 'tick', the code in uipc_socket.c 

			/* assert(tick > 0); */
			/* assert(ULONG_MAX - SHRT_MAX >= 1000000); */
1250:			val = (u_long)(tv.tv_sec * hz) + tv.tv_usec / tick;
			if (val > SHRT_MAX) {
				error = EDOM;
				goto bad;

computes a timeout value for the socket of 0, causing subsequence receive
operations to block as if no timeout had been specified. This is unexpected
and not easily controllable by the application (one has to either fetch the
value of tick via sysctl or always make a getsockopt after the setsockopt).
Only timeout values with tv_sec==0 and tv_usec==0 should block indefinitely.

Fix: Apply the following patch to /usr/src/sys/kern/uipc_socket.c (this fixes
the same problem for the send case):
How-To-Repeat: 
Compile and run the following program and find it to timeout the first
receive after 1 second and to block forever on thesecond receive (given
no UDP packet is seen on 127.0.0.1:10000).

# include <sys/types.h>
# include <sys/socket.h>
# include <sys/time.h>
# include <stdio.h>
# include <errno.h>
# include <string.h>
# include <err.h>
# include <netinet/in.h>

int
main(int argc, char *argv[])
{
	int s;
	struct timeval tv;
	struct sockaddr_in sin;
	u_char buf[1000];

	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
		err(1, "socket");

	sin.sin_len = sizeof(sin);
	sin.sin_family = AF_INET;
	sin.sin_port = htons(10000);
	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
		err(1, "bind");

	tv.tv_sec = 1;
	tv.tv_usec = 0;
	if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
		err(1, "setsockopt");

	printf("recv 1...\n");
	if (recv(s, buf, sizeof(buf), 0) == -1)
		warn("recv");

	tv.tv_sec = 0;
	tv.tv_usec = 100;
	if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
		err(1, "setsockopt");

	printf("recv 2...\n");
	if (recv(s, buf, sizeof(buf), 0) == -1)
		warn("recv");

	return (0);
}
Comment 1 Sheldon Hearn 2001-12-30 12:41:15 UTC
On Fri, 14 Dec 2001 12:08:41 +0100, Hartmut Brandt wrote:

> >Category:       kern
> >Synopsis:       small SO_RCVTIMEO values are taken to be zero

I've asked for review on the freebsd-audit mailing list.  Hopefully,
someone will take a look soon.

Ciao,
Sheldon.
Comment 2 Maxim Konovalov freebsd_committer freebsd_triage 2002-11-27 13:37:53 UTC
State Changed
From-To: open->patched

Fixed in rev. 1.138 src/sys/kern/uipc_socket.c in -CURRENT. 
Thanks for the patch! 


Comment 3 Maxim Konovalov freebsd_committer freebsd_triage 2002-11-27 13:37:53 UTC
Responsible Changed
From-To: freebsd-bugs->maxim

Will MFC the diff in two weeks.
Comment 4 Maxim Konovalov freebsd_committer freebsd_triage 2002-12-15 09:24:56 UTC
State Changed
From-To: patched->closed

Fixed in rev. 1.138 and rev. 1.68.2.22 src/sys/kern/uipc_socket.c in -CURRENT 
and -STABLE. Thanks!