diff options
author | cheloha <cheloha@cvs.openbsd.org> | 2019-01-21 23:41:27 +0000 |
---|---|---|
committer | cheloha <cheloha@cvs.openbsd.org> | 2019-01-21 23:41:27 +0000 |
commit | 7e196d2c9a1b76d7756ba8d07c381bbb29c73fda (patch) | |
tree | 31f62258705bc4ff6c3f193abb7222a3da1b049a /sys | |
parent | 1ef1f440c4093edf0d32d021abe7373627e65569 (diff) |
select(2), pselect(2), poll(2), ppoll(2): Support full timeout range.
Remove the arbitrary and undocumented 24hr limits for timeouts from these
interfaces. To do so, loop tsleep(9) to chip away at timeouts larger than
what tsleep(9) can handle in one call.
Use timerisvalid(3)/timespecisvalid() for input validation instead of
itimerfix()/timespecfix() to avoid the 100 million second upper bounds
those functions introduce.
POSIX requires support for timeouts of at least 31 days for select(2) and
pselect(2), so these changes make our implementation more compliant.
Other improvements here include better variable names for the time stuff
and more consolidated timeout logic with less backwards goto jumping, all
of which made dopselect() and doppoll() a bear to read.
Naming improvements prompted by tedu@ in a prior patch for nanosleep(2).
With input from deraadt@. Validation bug spotted by matthew@ in an earlier
version.
ok visa@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/sys_generic.c | 121 |
1 files changed, 58 insertions, 63 deletions
diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index df0ec40c11d..ccb8502ed21 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sys_generic.c,v 1.122 2018/08/20 16:00:22 mpi Exp $ */ +/* $OpenBSD: sys_generic.c,v 1.123 2019/01/21 23:41:26 cheloha Exp $ */ /* $NetBSD: sys_generic.c,v 1.24 1996/03/29 00:25:32 cgd Exp $ */ /* @@ -52,6 +52,7 @@ #include <sys/uio.h> #include <sys/kernel.h> #include <sys/stat.h> +#include <sys/time.h> #include <sys/malloc.h> #include <sys/poll.h> #ifdef KTRACE @@ -69,8 +70,8 @@ int selscan(struct proc *, fd_set *, fd_set *, int, int, register_t *); void pollscan(struct proc *, struct pollfd *, u_int, register_t *); int pollout(struct pollfd *, struct pollfd *, u_int); int dopselect(struct proc *, int, fd_set *, fd_set *, fd_set *, - const struct timespec *, const sigset_t *, register_t *); -int doppoll(struct proc *, struct pollfd *, u_int, const struct timespec *, + struct timespec *, const sigset_t *, register_t *); +int doppoll(struct proc *, struct pollfd *, u_int, struct timespec *, const sigset_t *, register_t *); int @@ -548,8 +549,8 @@ sys_select(struct proc *p, void *v, register_t *retval) struct timeval tv; if ((error = copyin(SCARG(uap, tv), &tv, sizeof tv)) != 0) return (error); - if ((error = itimerfix(&tv)) != 0) - return (error); + if (tv.tv_sec < 0 || !timerisvalid(&tv)) + return (EINVAL); #ifdef KTRACE if (KTRPOINT(p, KTR_STRUCT)) ktrreltimeval(p, &tv); @@ -581,8 +582,8 @@ sys_pselect(struct proc *p, void *v, register_t *retval) if (SCARG(uap, ts) != NULL) { if ((error = copyin(SCARG(uap, ts), &ts, sizeof ts)) != 0) return (error); - if ((error = timespecfix(&ts)) != 0) - return (error); + if (ts.tv_sec < 0 || !timespecisvalid(&ts)) + return (EINVAL); #ifdef KTRACE if (KTRPOINT(p, KTR_STRUCT)) ktrreltimespec(p, &ts); @@ -601,11 +602,11 @@ sys_pselect(struct proc *p, void *v, register_t *retval) int dopselect(struct proc *p, int nd, fd_set *in, fd_set *ou, fd_set *ex, - const struct timespec *tsp, const sigset_t *sigmask, register_t *retval) + struct timespec *timeout, const sigset_t *sigmask, register_t *retval) { fd_mask bits[6]; fd_set *pibits[3], *pobits[3]; - struct timespec ats, rts, tts; + struct timespec elapsed, start, stop; int s, ncoll, error = 0, timo; u_int ni; @@ -651,15 +652,6 @@ dopselect(struct proc *p, int nd, fd_set *in, fd_set *ou, fd_set *ex, } #endif - if (tsp) { - getnanouptime(&rts); - timespecadd(tsp, &rts, &ats); - } else { - ats.tv_sec = 0; - ats.tv_nsec = 0; - } - timo = 0; - if (sigmask) dosigsuspend(p, *sigmask &~ sigcantmask); @@ -669,24 +661,30 @@ retry: error = selscan(p, pibits[0], pobits[0], nd, ni, retval); if (error || *retval) goto done; - if (tsp) { - getnanouptime(&rts); - if (timespeccmp(&rts, &ats, >=)) - goto done; - timespecsub(&ats, &rts, &tts); - timo = tts.tv_sec > 24 * 60 * 60 ? - 24 * 60 * 60 * hz : tstohz(&tts); - } - s = splhigh(); - if ((p->p_flag & P_SELECT) == 0 || nselcoll != ncoll) { + while (timeout == NULL || timespecisset(timeout)) { + timo = (timeout == NULL) ? 0 : tstohz(timeout); + if (timeout != NULL) + getnanouptime(&start); + s = splhigh(); + if ((p->p_flag & P_SELECT) == 0 || nselcoll != ncoll) { + splx(s); + goto retry; + } + atomic_clearbits_int(&p->p_flag, P_SELECT); + error = tsleep(&selwait, PSOCK | PCATCH, "select", timo); splx(s); - goto retry; + if (timeout != NULL) { + getnanouptime(&stop); + timespecsub(&stop, &start, &elapsed); + timespecsub(timeout, &elapsed, timeout); + if (timeout->tv_sec < 0) + timespecclear(timeout); + } + if (error == 0) + goto retry; + if (error != EWOULDBLOCK) + break; } - atomic_clearbits_int(&p->p_flag, P_SELECT); - error = tsleep(&selwait, PSOCK | PCATCH, "select", timo); - splx(s); - if (error == 0) - goto retry; done: atomic_clearbits_int(&p->p_flag, P_SELECT); /* select is not restarted after signals... */ @@ -908,8 +906,8 @@ sys_ppoll(struct proc *p, void *v, register_t *retval) if (SCARG(uap, ts) != NULL) { if ((error = copyin(SCARG(uap, ts), &ts, sizeof ts)) != 0) return (error); - if ((error = timespecfix(&ts)) != 0) - return (error); + if (ts.tv_sec < 0 || !timespecisvalid(&ts)) + return (EINVAL); #ifdef KTRACE if (KTRPOINT(p, KTR_STRUCT)) ktrreltimespec(p, &ts); @@ -929,11 +927,11 @@ sys_ppoll(struct proc *p, void *v, register_t *retval) int doppoll(struct proc *p, struct pollfd *fds, u_int nfds, - const struct timespec *tsp, const sigset_t *sigmask, register_t *retval) + struct timespec *timeout, const sigset_t *sigmask, register_t *retval) { size_t sz; struct pollfd pfds[4], *pl = pfds; - struct timespec ats, rts, tts; + struct timespec elapsed, start, stop; int timo, ncoll, i, s, error; /* Standards say no more than MAX_OPEN; this is possibly better. */ @@ -958,15 +956,6 @@ doppoll(struct proc *p, struct pollfd *fds, u_int nfds, pl[i].revents = 0; } - if (tsp != NULL) { - getnanouptime(&rts); - timespecadd(tsp, &rts, &ats); - } else { - ats.tv_sec = 0; - ats.tv_nsec = 0; - } - timo = 0; - if (sigmask) dosigsuspend(p, *sigmask &~ sigcantmask); @@ -976,24 +965,30 @@ retry: pollscan(p, pl, nfds, retval); if (*retval) goto done; - if (tsp != NULL) { - getnanouptime(&rts); - if (timespeccmp(&rts, &ats, >=)) - goto done; - timespecsub(&ats, &rts, &tts); - timo = tts.tv_sec > 24 * 60 * 60 ? - 24 * 60 * 60 * hz : tstohz(&tts); - } - s = splhigh(); - if ((p->p_flag & P_SELECT) == 0 || nselcoll != ncoll) { + while (timeout == NULL || timespecisset(timeout)) { + timo = (timeout == NULL) ? 0 : tstohz(timeout); + if (timeout != NULL) + getnanouptime(&start); + s = splhigh(); + if ((p->p_flag & P_SELECT) == 0 || nselcoll != ncoll) { + splx(s); + goto retry; + } + atomic_clearbits_int(&p->p_flag, P_SELECT); + error = tsleep(&selwait, PSOCK | PCATCH, "poll", timo); splx(s); - goto retry; + if (timeout != NULL) { + getnanouptime(&stop); + timespecsub(&stop, &start, &elapsed); + timespecsub(timeout, &elapsed, timeout); + if (timeout->tv_sec < 0) + timespecclear(timeout); + } + if (error == 0) + goto retry; + if (error != EWOULDBLOCK) + break; } - atomic_clearbits_int(&p->p_flag, P_SELECT); - error = tsleep(&selwait, PSOCK | PCATCH, "poll", timo); - splx(s); - if (error == 0) - goto retry; done: atomic_clearbits_int(&p->p_flag, P_SELECT); |