From owner-freebsd-bugs Fri Jan 19 18:30:32 2001 Delivered-To: freebsd-bugs@hub.freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.org [216.136.204.21]) by hub.freebsd.org (Postfix) with ESMTP id D010337B699 for ; Fri, 19 Jan 2001 18:30:01 -0800 (PST) Received: (from gnats@localhost) by freefall.freebsd.org (8.11.1/8.11.1) id f0K2U1011045; Fri, 19 Jan 2001 18:30:01 -0800 (PST) (envelope-from gnats) Received: from atlas.home.dynas.se (adsl-64-165-200-109.dsl.snfc21.pacbell.net [64.165.200.109]) by hub.freebsd.org (Postfix) with ESMTP id 6E4C437B698 for ; Fri, 19 Jan 2001 18:23:34 -0800 (PST) Received: (from mikko@localhost) by atlas.home.dynas.se (8.11.1/8.11.1) id f0K2NSe08062; Fri, 19 Jan 2001 18:23:28 -0800 (PST) (envelope-from mikko) Message-Id: Date: Fri, 19 Jan 2001 18:23:11 -0800 (PST) From: =?ISO-8859-1?Q?Mikko_Ty=F6l=E4j=E4rvi?= Reply-To: mikko@dynas.se To: FreeBSD-gnats-submit@freebsd.org Subject: bin/24472: libc_r does not honor SO_SNDTIMEO/SO_RCVTIMEO socket options Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >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 #include "pthread_private.h" +#include #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