aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/sys_generic.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/sys_generic.c')
-rw-r--r--sys/kern/sys_generic.c247
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);
+}