diff options
Diffstat (limited to 'sys/kern/sys_generic.c')
-rw-r--r-- | sys/kern/sys_generic.c | 247 |
1 files changed, 204 insertions, 43 deletions
diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index b926f98892a5..94e44d888181 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -34,21 +34,24 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include "opt_capsicum.h" #include "opt_ktrace.h" +#define EXTERR_CATEGORY EXTERR_CAT_GENIO #include <sys/param.h> #include <sys/systm.h> #include <sys/sysproto.h> #include <sys/capsicum.h> +#include <sys/exterrvar.h> #include <sys/filedesc.h> #include <sys/filio.h> #include <sys/fcntl.h> #include <sys/file.h> +#include <sys/inotify.h> #include <sys/lock.h> #include <sys/proc.h> #include <sys/signalvar.h> +#include <sys/protosw.h> #include <sys/socketvar.h> #include <sys/uio.h> #include <sys/eventfd.h> @@ -193,7 +196,7 @@ sys_read(struct thread *td, struct read_args *uap) int error; if (uap->nbyte > IOSIZE_MAX) - return (EINVAL); + return (EXTERROR(EINVAL, "length > iosize_max")); aiov.iov_base = uap->buf; aiov.iov_len = uap->nbyte; auio.uio_iov = &aiov; @@ -231,7 +234,7 @@ kern_pread(struct thread *td, int fd, void *buf, size_t nbyte, off_t offset) int error; if (nbyte > IOSIZE_MAX) - return (EINVAL); + return (EXTERROR(EINVAL, "length > iosize_max")); aiov.iov_base = buf; aiov.iov_len = nbyte; auio.uio_iov = &aiov; @@ -327,7 +330,7 @@ kern_preadv(struct thread *td, int fd, struct uio *auio, off_t offset) error = ESPIPE; else if (offset < 0 && (fp->f_vnode == NULL || fp->f_vnode->v_type != VCHR)) - error = EINVAL; + error = EXTERROR(EINVAL, "neg offset"); else error = dofileread(td, fd, fp, auio, offset, FOF_OFFSET); fdrop(fp, td); @@ -394,7 +397,7 @@ sys_write(struct thread *td, struct write_args *uap) int error; if (uap->nbyte > IOSIZE_MAX) - return (EINVAL); + return (EXTERROR(EINVAL, "length > iosize_max")); aiov.iov_base = (void *)(uintptr_t)uap->buf; aiov.iov_len = uap->nbyte; auio.uio_iov = &aiov; @@ -433,7 +436,7 @@ kern_pwrite(struct thread *td, int fd, const void *buf, size_t nbyte, int error; if (nbyte > IOSIZE_MAX) - return (EINVAL); + return (EXTERROR(EINVAL, "length > iosize_max")); aiov.iov_base = (void *)(uintptr_t)buf; aiov.iov_len = nbyte; auio.uio_iov = &aiov; @@ -529,7 +532,7 @@ kern_pwritev(struct thread *td, int fd, struct uio *auio, off_t offset) error = ESPIPE; else if (offset < 0 && (fp->f_vnode == NULL || fp->f_vnode->v_type != VCHR)) - error = EINVAL; + error = EXTERROR(EINVAL, "neg offset"); else error = dofilewrite(td, fd, fp, auio, offset, FOF_OFFSET); fdrop(fp, td); @@ -600,14 +603,14 @@ kern_ftruncate(struct thread *td, int fd, off_t length) AUDIT_ARG_FD(fd); if (length < 0) - return (EINVAL); + return (EXTERROR(EINVAL, "negative length")); error = fget(td, fd, &cap_ftruncate_rights, &fp); if (error) return (error); AUDIT_ARG_FILE(td->td_proc, fp); if (!(fp->f_flag & FWRITE)) { fdrop(fp, td); - return (EINVAL); + return (EXTERROR(EINVAL, "non-writable")); } error = fo_truncate(fp, length, td->td_ucred, td); fdrop(fp, td); @@ -838,8 +841,10 @@ kern_posix_fallocate(struct thread *td, int fd, off_t offset, off_t len) int error; AUDIT_ARG_FD(fd); - if (offset < 0 || len <= 0) - return (EINVAL); + if (offset < 0) + return (EXTERROR(EINVAL, "negative offset")); + if (len <= 0) + return (EXTERROR(EINVAL, "negative length")); /* Check for wrap. */ if (offset > OFF_MAX - len) return (EFBIG); @@ -896,16 +901,21 @@ kern_fspacectl(struct thread *td, int fd, int cmd, AUDIT_ARG_FFLAGS(flags); if (rqsr == NULL) - return (EINVAL); + return (EXTERROR(EINVAL, "no range")); rmsr = *rqsr; if (rmsrp != NULL) *rmsrp = rmsr; - if (cmd != SPACECTL_DEALLOC || - rqsr->r_offset < 0 || rqsr->r_len <= 0 || - rqsr->r_offset > OFF_MAX - rqsr->r_len || - (flags & ~SPACECTL_F_SUPPORTED) != 0) - return (EINVAL); + if (cmd != SPACECTL_DEALLOC) + return (EXTERROR(EINVAL, "cmd", cmd)); + if (rqsr->r_offset < 0) + return (EXTERROR(EINVAL, "neg offset")); + if (rqsr->r_len <= 0) + return (EXTERROR(EINVAL, "neg len")); + if (rqsr->r_offset > OFF_MAX - rqsr->r_len) + return (EXTERROR(EINVAL, "offset too large")); + if ((flags & ~SPACECTL_F_SUPPORTED) != 0) + return (EXTERROR(EINVAL, "reserved flags", flags)); error = fget_write(td, fd, &cap_pwrite_rights, &fp); if (error != 0) @@ -937,7 +947,6 @@ int kern_specialfd(struct thread *td, int type, void *arg) { struct file *fp; - struct specialfd_eventfd *ae; int error, fd, fflags; fflags = 0; @@ -946,14 +955,24 @@ kern_specialfd(struct thread *td, int type, void *arg) return (error); switch (type) { - case SPECIALFD_EVENTFD: + case SPECIALFD_EVENTFD: { + struct specialfd_eventfd *ae; + ae = arg; if ((ae->flags & EFD_CLOEXEC) != 0) fflags |= O_CLOEXEC; error = eventfd_create_file(td, fp, ae->initval, ae->flags); break; + } + case SPECIALFD_INOTIFY: { + struct specialfd_inotify *si; + + si = arg; + error = inotify_create_file(td, fp, si->flags, &fflags); + break; + } default: - error = EINVAL; + error = EXTERROR(EINVAL, "invalid type", type); break; } @@ -968,13 +987,14 @@ kern_specialfd(struct thread *td, int type, void *arg) int sys___specialfd(struct thread *td, struct __specialfd_args *args) { - struct specialfd_eventfd ae; int error; switch (args->type) { - case SPECIALFD_EVENTFD: + case SPECIALFD_EVENTFD: { + struct specialfd_eventfd ae; + if (args->len != sizeof(struct specialfd_eventfd)) { - error = EINVAL; + error = EXTERROR(EINVAL, "eventfd params ABI"); break; } error = copyin(args->req, &ae, sizeof(ae)); @@ -982,13 +1002,27 @@ sys___specialfd(struct thread *td, struct __specialfd_args *args) break; if ((ae.flags & ~(EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE)) != 0) { - error = EINVAL; + error = EXTERROR(EINVAL, "reserved flag"); break; } error = kern_specialfd(td, args->type, &ae); break; + } + case SPECIALFD_INOTIFY: { + struct specialfd_inotify si; + + if (args->len != sizeof(si)) { + error = EINVAL; + break; + } + error = copyin(args->req, &si, sizeof(si)); + if (error != 0) + break; + error = kern_specialfd(td, args->type, &si); + break; + } default: - error = EINVAL; + error = EXTERROR(EINVAL, "unknown type", args->type); break; } return (error); @@ -1049,14 +1083,26 @@ kern_pselect(struct thread *td, int nd, fd_set *in, fd_set *ou, fd_set *ex, if (error != 0) return (error); td->td_pflags |= TDP_OLDMASK; + } + error = kern_select(td, nd, in, ou, ex, tvp, abi_nfdbits); + if (uset != NULL) { /* * Make sure that ast() is called on return to * usermode and TDP_OLDMASK is cleared, restoring old - * sigmask. + * sigmask. If we didn't get interrupted, then the caller is + * likely not expecting a signal to hit that should normally be + * blocked by its signal mask, so we restore the mask before + * any signals could be delivered. */ - ast_sched(td, TDA_SIGSUSPEND); + if (error == EINTR) { + ast_sched(td, TDA_SIGSUSPEND); + } else { + /* *select(2) should never restart. */ + MPASS(error != ERESTART); + ast_sched(td, TDA_PSELECT); + } } - error = kern_select(td, nd, in, ou, ex, tvp, abi_nfdbits); + return (error); } @@ -1152,7 +1198,7 @@ kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou, int error, lf, ndu; if (nd < 0) - return (EINVAL); + return (EXTERROR(EINVAL, "negative ndescs")); fdp = td->td_proc->p_fd; ndu = nd; lf = fdp->fd_nfiles; @@ -1245,7 +1291,7 @@ kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou, rtv = *tvp; if (rtv.tv_sec < 0 || rtv.tv_usec < 0 || rtv.tv_usec >= 1000000) { - error = EINVAL; + error = EXTERROR(EINVAL, "invalid timeval"); goto done; } if (!timevalisset(&rtv)) @@ -1477,7 +1523,7 @@ sys_poll(struct thread *td, struct poll_args *uap) if (uap->timeout != INFTIM) { if (uap->timeout < 0) - return (EINVAL); + return (EXTERROR(EINVAL, "invalid timeout")); ts.tv_sec = uap->timeout / 1000; ts.tv_nsec = (uap->timeout % 1000) * 1000000; tsp = &ts; @@ -1502,7 +1548,7 @@ kern_poll_kfds(struct thread *td, struct pollfd *kfds, u_int nfds, precision = 0; if (tsp != NULL) { if (!timespecvalid_interval(tsp)) - return (EINVAL); + return (EXTERROR(EINVAL, "invalid timespec")); if (tsp->tv_sec == 0 && tsp->tv_nsec == 0) sbt = 0; else { @@ -1528,12 +1574,6 @@ kern_poll_kfds(struct thread *td, struct pollfd *kfds, u_int nfds, if (error) return (error); td->td_pflags |= TDP_OLDMASK; - /* - * Make sure that ast() is called on return to - * usermode and TDP_OLDMASK is cleared, restoring old - * sigmask. - */ - ast_sched(td, TDA_SIGSUSPEND); } seltdinit(td); @@ -1556,6 +1596,22 @@ kern_poll_kfds(struct thread *td, struct pollfd *kfds, u_int nfds, error = EINTR; if (error == EWOULDBLOCK) error = 0; + + if (uset != NULL) { + /* + * Make sure that ast() is called on return to + * usermode and TDP_OLDMASK is cleared, restoring old + * sigmask. If we didn't get interrupted, then the caller is + * likely not expecting a signal to hit that should normally be + * blocked by its signal mask, so we restore the mask before + * any signals could be delivered. + */ + if (error == EINTR) + ast_sched(td, TDA_SIGSUSPEND); + else + ast_sched(td, TDA_PSELECT); + } + return (error); } @@ -1595,7 +1651,7 @@ kern_poll(struct thread *td, struct pollfd *ufds, u_int nfds, int error; if (kern_poll_maxfds(nfds)) - return (EINVAL); + return (EXTERROR(EINVAL, "too large nfds")); if (nfds > nitems(stackfds)) kfds = mallocarray(nfds, sizeof(*kfds), M_TEMP, M_WAITOK); else @@ -1772,7 +1828,7 @@ selsocket(struct socket *so, int events, struct timeval *tvp, struct thread *td) rtv = *tvp; if (rtv.tv_sec < 0 || rtv.tv_usec < 0 || rtv.tv_usec >= 1000000) - return (EINVAL); + return (EXTERROR(EINVAL, "invalid timeval")); if (!timevalisset(&rtv)) asbt = 0; else if (rtv.tv_sec <= INT32_MAX) { @@ -1795,7 +1851,7 @@ selsocket(struct socket *so, int events, struct timeval *tvp, struct thread *td) */ for (;;) { selfdalloc(td, NULL); - if (sopoll(so, events, NULL, td) != 0) { + if (so->so_proto->pr_sopoll(so, events, td) != 0) { error = 0; break; } @@ -2092,11 +2148,16 @@ kcmp_cmp(uintptr_t a, uintptr_t b) static int kcmp_pget(struct thread *td, pid_t pid, struct proc **pp) { + int error; + if (pid == td->td_proc->p_pid) { *pp = td->td_proc; return (0); } - return (pget(pid, PGET_CANDEBUG | PGET_NOTWEXIT | PGET_HOLD, pp)); + error = pget(pid, PGET_NOTID | PGET_CANDEBUG | PGET_NOTWEXIT | + PGET_HOLD, pp); + MPASS(*pp != td->td_proc); + return (error); } int @@ -2144,7 +2205,7 @@ kern_kcmp(struct thread *td, pid_t pid1, pid_t pid2, int type, (uintptr_t)p2->p_vmspace); break; default: - error = EINVAL; + error = EXTERROR(EINVAL, "unknown op"); break; } @@ -2172,3 +2233,103 @@ file_kcmp_generic(struct file *fp1, struct file *fp2, struct thread *td) return (3); return (kcmp_cmp((uintptr_t)fp1->f_data, (uintptr_t)fp2->f_data)); } + +int +exterr_to_ue(struct thread *td, struct uexterror *ue) +{ + if ((td->td_pflags2 & TDP2_EXTERR) == 0) + return (ENOENT); + + memset(ue, 0, sizeof(*ue)); + ue->error = td->td_kexterr.error; + ue->cat = td->td_kexterr.cat; + ue->src_line = td->td_kexterr.src_line; + ue->p1 = td->td_kexterr.p1; + ue->p2 = td->td_kexterr.p2; + if (td->td_kexterr.msg != NULL) + strlcpy(ue->msg, td->td_kexterr.msg, sizeof(ue->msg)); + return (0); +} + +void +exterr_copyout(struct thread *td) +{ + struct uexterror ue; + ksiginfo_t ksi; + void *uloc; + size_t sz; + int error; + + MPASS((td->td_pflags2 & TDP2_UEXTERR) != 0); + + uloc = (char *)td->td_exterr_ptr + __offsetof(struct uexterror, + error); + error = exterr_to_ue(td, &ue); + if (error != 0) { + ue.error = 0; + sz = sizeof(ue.error); + } else { + sz = sizeof(ue) - __offsetof(struct uexterror, error); + } + error = copyout(&ue.error, uloc, sz); + if (error != 0) { + td->td_pflags2 &= ~TDP2_UEXTERR; + ksiginfo_init_trap(&ksi); + ksi.ksi_signo = SIGSEGV; + ksi.ksi_code = SEGV_ACCERR; + ksi.ksi_addr = uloc; + trapsignal(td, &ksi); + } +} + +int +sys_exterrctl(struct thread *td, struct exterrctl_args *uap) +{ + uint32_t ver; + int error; + + if ((uap->flags & ~(EXTERRCTLF_FORCE)) != 0) + return (EINVAL); + switch (uap->op) { + case EXTERRCTL_ENABLE: + if ((td->td_pflags2 & TDP2_UEXTERR) != 0 && + (uap->flags & EXTERRCTLF_FORCE) == 0) + return (EBUSY); + td->td_pflags2 &= ~TDP2_UEXTERR; + error = copyin(uap->ptr, &ver, sizeof(ver)); + if (error != 0) + return (error); + if (ver != UEXTERROR_VER) + return (EINVAL); + td->td_pflags2 |= TDP2_UEXTERR; + td->td_exterr_ptr = uap->ptr; + return (0); + case EXTERRCTL_DISABLE: + if ((td->td_pflags2 & TDP2_UEXTERR) == 0) + return (EINVAL); + td->td_pflags2 &= ~TDP2_UEXTERR; + return (0); + default: + return (EINVAL); + } +} + +int +exterr_set(int eerror, int category, const char *mmsg, uintptr_t pp1, + uintptr_t pp2, int line) +{ + struct thread *td; + + td = curthread; + if ((td->td_pflags2 & TDP2_UEXTERR) != 0) { + td->td_pflags2 |= TDP2_EXTERR; + td->td_kexterr.error = eerror; + td->td_kexterr.cat = category; + td->td_kexterr.msg = mmsg; + td->td_kexterr.p1 = pp1; + td->td_kexterr.p2 = pp2; + td->td_kexterr.src_line = line; + ktrexterr(td); + } + return (eerror); +} |