Date: Fri, 19 Jan 2001 18:23:11 -0800 (PST) From: =?ISO-8859-1?Q?Mikko_Ty=F6l=E4j=E4rvi?= <mikko@dynas.se> To: FreeBSD-gnats-submit@freebsd.org Subject: bin/24472: libc_r does not honor SO_SNDTIMEO/SO_RCVTIMEO socket options Message-ID: <Pine.BSF.4.21.0101191815561.520-100000@atlas.home.dynas.se>
next in thread | raw e-mail | index | archive | help
>Number: 24472
>Category: bin
>Synopsis: libc_r does not honor SO_SNDTIMEO/SO_RCVTIMEO socket options
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Fri Jan 19 18:30:01 PST 2001
>Closed-Date:
>Last-Modified:
>Originator: Mikko Tyolajarvi
>Release: FreeBSD 4.2-STABLE i386
>Organization:
>Environment:
4.2-STABLE as of Jan 19, 2001
>Description:
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.
>How-To-Repeat:
Uh, like, try to use them... Operations will block forever.
>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
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;
+ }
}
/*
Mikko Työläjärvi_______________________________________mikko@rsasecurity.com
RSA Security
>Release-Note:
>Audit-Trail:
>Unformatted:
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.4.21.0101191815561.520-100000>
