Bug 24472

Summary: libc_r does not honor SO_SNDTIMEO/SO_RCVTIMEO socket options
Product: Base System Reporter: mikko <mikko>
Component: threadsAssignee: freebsd-threads (Nobody) <threads>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.2-STABLE   
Hardware: Any   
OS: Any   

Description mikko 2001-01-20 02:30:01 UTC
The socket options SO_SNDTIMEO and SO_RCVTIMEO which can be used to
make blocking socket I/O operations time out are ignored by libc_r.

Fix: The patch below adds support for the missing socket options
in the uthread library.  In short:

 - Timeout values are cached per fd
 - The timeouts are imported when data on the fd is initialised
 - They are updated when calling {set,get}sockopt()
 - When blocking on an I/O operation, and the fd is a socket, and it
   has a timeout defined, limit the time to sleep, and return an
   error code (or in some cases a short count) on timeout.

Affected functions are:

 Sets timeout values:  getsockopt, setsockopt, dup, dup2, fcntl

 I/O: read, readv, recvfrom, recvmsg, sendfile, sendmsg, sendto,
      write, writev

The patch has been somewhat tested, but I wouldn't install it in a
live system controlling nuclear power plants without some additional
review.

       $.02,
	/Mikko



Mikko Työläjärvi_______________________________________mikko@rsasecurity.com
 RSA Security--8sT367tQyNSfDkM9mDF7DArpPdMRIzk2Oncrernwoh6l0eTG
Content-Type: text/plain; name="file.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="file.diff"

diff -ru uthread.org/pthread_private.h uthread/pthread_private.h
--- uthread.org/pthread_private.h	Sat Nov 25 10:10:27 2000
+++ uthread/pthread_private.h	Fri Jan 19 17:26:49 2001
@@ -550,6 +550,8 @@
 	int			r_lockcount;	/* Count for FILE read locks.         */
 	int			w_lockcount;	/* Count for FILE write locks.        */
 	int			flags;		/* Flags used in open.                */
+	struct timespec		rcvtimeo;	/* Input timeout for sockets          */
+	struct timespec		sndtimeo;	/* Output timeout for sockets         */
 };
 
 struct pthread_poll_data {
@@ -1193,6 +1195,11 @@
 #define	_FD_LOCK(_fd,_type,_ts)		_thread_fd_lock(_fd, _type, _ts)
 #define _FD_UNLOCK(_fd,_type)		_thread_fd_unlock(_fd, _type)
 #endif
+
+/* Get a suitable argument for _thread_kern_set_timeout(), given an fd */
+#define _FD_TIMEO(_ts)		(((_ts)->tv_sec || (_ts)->tv_nsec) ? (_ts) : NULL)
+#define _FD_RCVTIMEO(_fd)	_FD_TIMEO(&_thread_fd_table[(_fd)]->rcvtimeo)
+#define _FD_SNDTIMEO(_fd)	_FD_TIMEO(&_thread_fd_table[(_fd)]->sndtimeo)
 
 /*
  * Function prototype definitions.
diff -ru uthread.org/uthread_dup.c uthread/uthread_dup.c
--- uthread.org/uthread_dup.c	Sat Jan 29 14:53:41 2000
+++ uthread/uthread_dup.c	Fri Jan 19 16:26:34 2001
@@ -59,6 +59,10 @@
 			 * checked later: 
 			 */
 			_thread_fd_table[ret]->flags = _thread_fd_table[fd]->flags;
+
+			/* Copy socket timeouts: */
+			_thread_fd_table[ret]->rcvtimeo = _thread_fd_table[fd]->rcvtimeo;
+			_thread_fd_table[ret]->sndtimeo = _thread_fd_table[fd]->sndtimeo;
 		}
 
 		/* Unlock the file descriptor: */
diff -ru uthread.org/uthread_dup2.c uthread/uthread_dup2.c
--- uthread.org/uthread_dup2.c	Sat Jan 29 14:53:42 2000
+++ uthread/uthread_dup2.c	Fri Jan 19 16:26:53 2001
@@ -71,6 +71,10 @@
 				 * be checked     later: 
 				 */
 				_thread_fd_table[ret]->flags = _thread_fd_table[fd]->flags;
+
+				/* Copy socket timeouts: */
+				_thread_fd_table[ret]->rcvtimeo = _thread_fd_table[fd]->rcvtimeo;
+				_thread_fd_table[ret]->sndtimeo = _thread_fd_table[fd]->sndtimeo;
 			}
 
 			/* Unlock the file descriptor: */
diff -ru uthread.org/uthread_fcntl.c uthread/uthread_fcntl.c
--- uthread.org/uthread_fcntl.c	Fri Jan 28 14:10:27 2000
+++ uthread/uthread_fcntl.c	Fri Jan 19 16:27:12 2001
@@ -78,6 +78,10 @@
 				 * be         checked later: 
 				 */
 				_thread_fd_table[ret]->flags = _thread_fd_table[fd]->flags;
+
+				/* Copy socket timeouts: */
+				_thread_fd_table[ret]->rcvtimeo = _thread_fd_table[fd]->rcvtimeo;
+				_thread_fd_table[ret]->sndtimeo = _thread_fd_table[fd]->sndtimeo;
 			}
 			break;
 		case F_SETFD:
diff -ru uthread.org/uthread_fd.c uthread/uthread_fd.c
--- uthread.org/uthread_fd.c	Sat Nov 25 10:10:27 2000
+++ uthread/uthread_fd.c	Fri Jan 19 17:40:31 2001
@@ -39,6 +39,7 @@
 #ifdef _THREAD_SAFE
 #include <pthread.h>
 #include "pthread_private.h"
+#include <sys/socket.h>
 
 #define FDQ_INSERT(q,p)					\
 do {							\
@@ -76,6 +77,8 @@
 	int	ret = 0;
 	struct fd_table_entry *entry;
 	int	saved_errno;
+	struct timeval tv;
+	socklen_t tlen;
 
 	/* Check if the file descriptor is out of range: */
 	if (fd < 0 || fd >= _thread_dtablesize) {
@@ -112,6 +115,19 @@
 		/* Initialise the read/write queues: */
 		TAILQ_INIT(&entry->r_queue);
 		TAILQ_INIT(&entry->w_queue);
+
+		/* Initialise socket timeouts: */
+		tlen = sizeof(tv);
+		if (_thread_sys_getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &tlen) < 0) {
+			entry->rcvtimeo.tv_sec = 0;
+			entry->rcvtimeo.tv_nsec = 0;
+			entry->sndtimeo.tv_sec = 0;
+			entry->sndtimeo.tv_nsec = 0;
+		} else {
+			TIMEVAL_TO_TIMESPEC(&tv, &entry->rcvtimeo);
+			_thread_sys_getsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, &tlen);
+			TIMEVAL_TO_TIMESPEC(&tv, &entry->sndtimeo);
+		}
 
 		/* Get the flags for the file: */
 		if (((fd >= 3) || (_pthread_stdio_flags[fd] == -1)) &&
diff -ru uthread.org/uthread_getsockopt.c uthread/uthread_getsockopt.c
--- uthread.org/uthread_getsockopt.c	Sat Jan 29 14:53:48 2000
+++ uthread/uthread_getsockopt.c	Fri Jan 19 17:24:47 2001
@@ -45,6 +45,16 @@
 
 	if ((ret = _FD_LOCK(fd, FD_RDWR, NULL)) == 0) {
 		ret = _thread_sys_getsockopt(fd, level, optname, optval, optlen);
+		if (ret == 0 && level == SOL_SOCKET) {
+			switch (optname) {
+			case SO_SNDTIMEO:
+				TIMEVAL_TO_TIMESPEC((struct timeval *)optval, &_thread_fd_table[fd]->sndtimeo);
+				break;
+			case SO_RCVTIMEO:
+				TIMEVAL_TO_TIMESPEC((struct timeval *)optval, &_thread_fd_table[fd]->rcvtimeo);
+				break;
+			}
+		}
 		_FD_UNLOCK(fd, FD_RDWR);
 	}
 	return ret;
diff -ru uthread.org/uthread_read.c uthread/uthread_read.c
--- uthread.org/uthread_read.c	Thu Jan 27 15:07:13 2000
+++ uthread/uthread_read.c	Fri Jan 19 16:56:15 2001
@@ -70,10 +70,11 @@
 			if ((_thread_fd_table[fd]->flags & O_NONBLOCK) == 0 &&
 			    (errno == EWOULDBLOCK || errno == EAGAIN)) {
 				_thread_run->data.fd.fd = fd;
-				_thread_kern_set_timeout(NULL);
+				_thread_kern_set_timeout(_FD_RCVTIMEO(fd));
 
 				/* Reset the interrupted operation flag: */
 				_thread_run->interrupted = 0;
+				_thread_run->timeout = 0;
 
 				_thread_kern_sched_state(PS_FDR_WAIT,
 				    __FILE__, __LINE__);
@@ -87,6 +88,10 @@
 					ret = -1;
 					break;
 				}
+
+				/* Socket timer timed out: */
+				if (_thread_run->timeout)
+					break;
 			} else {
 				break;
 			}
diff -ru uthread.org/uthread_readv.c uthread/uthread_readv.c
--- uthread.org/uthread_readv.c	Sat Jan 29 14:53:49 2000
+++ uthread/uthread_readv.c	Fri Jan 19 16:56:05 2001
@@ -65,10 +65,11 @@
 			if ((_thread_fd_table[fd]->flags & O_NONBLOCK) == 0 &&
 			    (errno == EWOULDBLOCK || errno == EAGAIN)) {
 				_thread_run->data.fd.fd = fd;
-				_thread_kern_set_timeout(NULL);
+				_thread_kern_set_timeout(_FD_RCVTIMEO(fd));
 
 				/* Reset the interrupted operation flag: */
 				_thread_run->interrupted = 0;
+				_thread_run->timeout = 0;
 
 				_thread_kern_sched_state(PS_FDR_WAIT,
 				    __FILE__, __LINE__);
@@ -82,6 +83,10 @@
 					ret = -1;
 					break;
 				}
+
+				/* Socket timer timed out: */
+				if (_thread_run->timeout)
+					break;
 			} else {
 				break;
 			}
diff -ru uthread.org/uthread_recvfrom.c uthread/uthread_recvfrom.c
--- uthread.org/uthread_recvfrom.c	Sat Jan 29 14:53:50 2000
+++ uthread/uthread_recvfrom.c	Fri Jan 19 16:57:03 2001
@@ -51,8 +51,9 @@
 				_thread_run->data.fd.fd = fd;
 
 				/* Set the timeout: */
-				_thread_kern_set_timeout(NULL);
+				_thread_kern_set_timeout(_FD_RCVTIMEO(fd));
 				_thread_run->interrupted = 0;
+				_thread_run->timeout = 0;
 				_thread_kern_sched_state(PS_FDR_WAIT, __FILE__, __LINE__);
 
 				/* Check if the wait was interrupted: */
@@ -62,6 +63,8 @@
 					ret = -1;
 					break;
 				}
+				if (_thread_run->timeout)
+					break;
 			} else {
 				ret = -1;
 				break;
diff -ru uthread.org/uthread_recvmsg.c uthread/uthread_recvmsg.c
--- uthread.org/uthread_recvmsg.c	Sat Jan 29 14:53:50 2000
+++ uthread/uthread_recvmsg.c	Fri Jan 19 16:57:55 2001
@@ -50,8 +50,9 @@
 				_thread_run->data.fd.fd = fd;
 
 				/* Set the timeout: */
-				_thread_kern_set_timeout(NULL);
+				_thread_kern_set_timeout(_FD_RCVTIMEO(fd));
 				_thread_run->interrupted = 0;
+				_thread_run->timeout = 0;
 				_thread_kern_sched_state(PS_FDR_WAIT, __FILE__, __LINE__);
 
 				/* Check if the wait was interrupted: */
@@ -61,6 +62,8 @@
 					ret = -1;
 					break;
 				}
+				if (_thread_run->timeout)
+					break;
 			} else {
 				ret = -1;
 				break;
diff -ru uthread.org/uthread_sendfile.c uthread/uthread_sendfile.c
--- uthread.org/uthread_sendfile.c	Sat Nov 25 10:10:28 2000
+++ uthread/uthread_sendfile.c	Fri Jan 19 17:17:59 2001
@@ -109,10 +109,11 @@
 			num += n;
 
 			_thread_run->data.fd.fd = fd;
-			_thread_kern_set_timeout(NULL);
+			_thread_kern_set_timeout(_FD_SNDTIMEO(fd));
 
 			/* Reset the interrupted operation flag. */
 			_thread_run->interrupted = 0;
+			_thread_run->timeout = 0;
 
 			_thread_kern_sched_state(PS_FDW_WAIT, __FILE__,
 			    __LINE__);
@@ -121,6 +122,8 @@
 				/* Interrupted by a signal.  Return an error. */
 				break;
 			}
+			if (_thread_run->timeout)
+				break;
 		} else {
 			/* Incomplete non-blocking syscall, or error. */
 			break;
diff -ru uthread.org/uthread_sendmsg.c uthread/uthread_sendmsg.c
--- uthread.org/uthread_sendmsg.c	Sat Jan 29 14:53:51 2000
+++ uthread/uthread_sendmsg.c	Fri Jan 19 17:08:02 2001
@@ -50,8 +50,9 @@
 				_thread_run->data.fd.fd = fd;
 
 				/* Set the timeout: */
-				_thread_kern_set_timeout(NULL);
+				_thread_kern_set_timeout(_FD_SNDTIMEO(fd));
 				_thread_run->interrupted = 0;
+				_thread_run->timeout = 0;
 				_thread_kern_sched_state(PS_FDW_WAIT, __FILE__, __LINE__);
 
 				/* Check if the operation was interrupted: */
@@ -60,6 +61,8 @@
 					ret = -1;
 					break;
 				}
+				if (_thread_run->timeout)
+					break;
 			} else {
 				ret = -1;
 				break;
diff -ru uthread.org/uthread_sendto.c uthread/uthread_sendto.c
--- uthread.org/uthread_sendto.c	Sat Jan 29 14:53:51 2000
+++ uthread/uthread_sendto.c	Fri Jan 19 17:09:12 2001
@@ -51,8 +51,9 @@
 				_thread_run->data.fd.fd = fd;
 
 				/* Set the timeout: */
-				_thread_kern_set_timeout(NULL);
+				_thread_kern_set_timeout(_FD_SNDTIMEO(fd));
 				_thread_run->interrupted = 0;
+				_thread_run->timeout = 0;
 				_thread_kern_sched_state(PS_FDW_WAIT, __FILE__, __LINE__);
 
 				/* Check if the operation was interrupted: */
@@ -61,6 +62,8 @@
 					ret = -1;
 					break;
 				}
+				if (_thread_run->timeout)
+					break;
 			} else {
 				ret = -1;
 				break;
diff -ru uthread.org/uthread_setsockopt.c uthread/uthread_setsockopt.c
--- uthread.org/uthread_setsockopt.c	Sat Jan 29 14:53:52 2000
+++ uthread/uthread_setsockopt.c	Fri Jan 19 17:25:41 2001
@@ -45,6 +45,16 @@
 
 	if ((ret = _FD_LOCK(fd, FD_RDWR, NULL)) == 0) {
 		ret = _thread_sys_setsockopt(fd, level, optname, optval, optlen);
+		if (ret == 0 && level == SOL_SOCKET) {
+			switch (optname) {
+			case SO_SNDTIMEO:
+				TIMEVAL_TO_TIMESPEC((struct timeval *)optval, &_thread_fd_table[fd]->sndtimeo);
+				break;
+			case SO_RCVTIMEO:
+				TIMEVAL_TO_TIMESPEC((struct timeval *)optval, &_thread_fd_table[fd]->rcvtimeo);
+				break;
+			}
+		}
 		_FD_UNLOCK(fd, FD_RDWR);
 	}
 	return ret;
diff -ru uthread.org/uthread_write.c uthread/uthread_write.c
--- uthread.org/uthread_write.c	Sat Nov 25 10:10:31 2000
+++ uthread/uthread_write.c	Fri Jan 19 17:15:57 2001
@@ -95,10 +95,11 @@
 			if (blocking && ((n < 0 && (errno == EWOULDBLOCK ||
 			    errno == EAGAIN)) || (n >= 0 && num < nbytes))) {
 				_thread_run->data.fd.fd = fd;
-				_thread_kern_set_timeout(NULL);
+				_thread_kern_set_timeout(_FD_SNDTIMEO(fd));
 
 				/* Reset the interrupted operation flag: */
 				_thread_run->interrupted = 0;
+				_thread_run->timeout = 0;
 
 				_thread_kern_sched_state(PS_FDW_WAIT,
 				    __FILE__, __LINE__);
@@ -110,6 +111,15 @@
 				if (_thread_run->interrupted) {
 					/* Return an error: */
 					ret = -1;
+				}
+				if (_thread_run->timeout) {
+					/* Return a short count or an error */
+					if (num > 0) {
+						ret = num;
+					} else {
+						ret = -1;
+						errno = EWOULDBLOCK;
+					}
 				}
 
 			/*
diff -ru uthread.org/uthread_writev.c uthread/uthread_writev.c
--- uthread.org/uthread_writev.c	Sat Jan 29 14:53:55 2000
+++ uthread/uthread_writev.c	Fri Jan 19 17:20:59 2001
@@ -159,10 +159,11 @@
 			if (blocking && ((n < 0 && (errno == EWOULDBLOCK ||
 			    errno == EAGAIN)) || (n >= 0 && idx < iovcnt))) {
 				_thread_run->data.fd.fd = fd;
-				_thread_kern_set_timeout(NULL);
+				_thread_kern_set_timeout(_FD_SNDTIMEO(fd));
 
 				/* Reset the interrupted operation flag: */
 				_thread_run->interrupted = 0;
+				_thread_run->timeout = 0;
 
 				_thread_kern_sched_state(PS_FDW_WAIT,
 				    __FILE__, __LINE__);
@@ -174,6 +175,15 @@
 				if (_thread_run->interrupted) {
 					/* Return an error: */
 					ret = -1;
+				}
+				if (_thread_run->timeout) {
+					/* Return a short count or an error */
+					if (num > 0) {
+						ret = num;
+					} else {
+						ret = -1;
+						errno = EWOULDBLOCK;
+					}
 				}
 
 			/*
How-To-Repeat: 
Uh, like, try to use them...  Operations will block forever.
Comment 1 Kris Kennaway freebsd_committer freebsd_triage 2003-07-13 02:32:49 UTC
Responsible Changed
From-To: freebsd-bugs->freebsd-threads

Assign to threads mailing list
Comment 2 Maxim Konovalov freebsd_committer freebsd_triage 2006-04-24 20:26:53 UTC
State Changed
From-To: open->suspended

In RELENG_5,6 and HEAD libc_r is deprecated in favour of 
libpthread and libthr.  Nobody is working on libc_r bugs 
so mark this PR as suspended.
Comment 3 Alexander Best freebsd_committer freebsd_triage 2010-11-18 21:15:22 UTC
State Changed
From-To: suspended->feedback

This looks like it may still be a problem with libthr/libpthread today. We 
need to verify/falsify that.
Comment 4 Mikko Työläjärvi 2010-11-27 23:33:22 UTC
Hi,

I submitted this bug from an email address that has now been defunct for years.

As far as I can tell after some basic testing, FreeBSD 7-stable, 8-stable and
9-current do not have this problem.

I think this bug can safely be closed.

  Thanks,
  /Mikko
Comment 5 Mark Linimon freebsd_committer freebsd_triage 2010-11-29 15:42:36 UTC
State Changed
From-To: feedback->closed

Submitter notes that this can be closed.