diff options
-rw-r--r-- | sys/kern/sys_generic.c | 66 |
1 files changed, 63 insertions, 3 deletions
diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index 8d4ba09..3be2689 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -831,6 +831,54 @@ sys_select(struct thread *td, struct select_args *uap) NFDBITS)); } +/* + * In the unlikely case when user specified n greater then the last + * open file descriptor, check that no bits are set after the last + * valid fd. We must return EBADF if any is set. + * + * There are applications that rely on the behaviour. + * + * nd is fd_lastfile + 1. + */ +static int +select_check_badfd(fd_set *fd_in, int nd, int ndu, int abi_nfdbits) +{ + char *addr, *oaddr; + int b, i, res; + uint8_t bits; + + if (nd >= ndu || fd_in == NULL) + return (0); + + oaddr = NULL; + bits = 0; /* silence gcc */ + for (i = nd; i < ndu; i++) { + b = i / NBBY; +#if BYTE_ORDER == LITTLE_ENDIAN + addr = (char *)fd_in + b; +#else + addr = (char *)fd_in; + if (abi_nfdbits == NFDBITS) { + addr += rounddown(b, sizeof(fd_mask)) + + sizeof(fd_mask) - 1 - b % sizeof(fd_mask); + } else { + addr += rounddown(b, sizeof(uint32_t)) + + sizeof(uint32_t) - 1 - b % sizeof(uint32_t); + } +#endif + if (addr != oaddr) { + res = fubyte(addr); + if (res == -1) + return (EFAULT); + oaddr = addr; + bits = res; + } + if ((bits & (1 << (i % NBBY))) != 0) + return (EBADF); + } + return (0); +} + int kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou, fd_set *fd_ex, struct timeval *tvp, int abi_nfdbits) @@ -845,14 +893,26 @@ kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou, fd_mask s_selbits[howmany(2048, NFDBITS)]; fd_mask *ibits[3], *obits[3], *selbits, *sbp; struct timeval atv, rtv, ttv; - int error, timo; + int error, lf, ndu, timo; u_int nbufbytes, ncpbytes, ncpubytes, nfdbits; if (nd < 0) return (EINVAL); fdp = td->td_proc->p_fd; - if (nd > fdp->fd_lastfile + 1) - nd = fdp->fd_lastfile + 1; + ndu = nd; + lf = fdp->fd_lastfile; + if (nd > lf + 1) + nd = lf + 1; + + error = select_check_badfd(fd_in, nd, ndu, abi_nfdbits); + if (error != 0) + return (error); + error = select_check_badfd(fd_ou, nd, ndu, abi_nfdbits); + if (error != 0) + return (error); + error = select_check_badfd(fd_ex, nd, ndu, abi_nfdbits); + if (error != 0) + return (error); /* * Allocate just enough bits for the non-null fd_sets. Use the |