summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorVisa Hankala <visa@cvs.openbsd.org>2018-11-12 15:09:18 +0000
committerVisa Hankala <visa@cvs.openbsd.org>2018-11-12 15:09:18 +0000
commit77fd9e73582f45aac9306658b8e330cf582b96cf (patch)
treec9ced8835ad9c317aaaa8ceda23630c960677071 /sys/kern
parentc331e297d58751510813f05e89fe8f43e6f59c02 (diff)
Add a mechanism for managing asynchronous IO signal registrations.
It centralizes IO signal privilege checking and makes possible to revoke a registration when the target process or process group is deleted. Adapted from FreeBSD. OK kettenis@ mpi@ guenther@
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_exit.c4
-rw-r--r--sys/kern/kern_fork.c3
-rw-r--r--sys/kern/kern_proc.c4
-rw-r--r--sys/kern/kern_sig.c272
4 files changed, 279 insertions, 4 deletions
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 1526ce6118f..b190eb6a7ac 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_exit.c,v 1.170 2018/10/04 20:07:54 kettenis Exp $ */
+/* $OpenBSD: kern_exit.c,v 1.171 2018/11/12 15:09:17 visa Exp $ */
/* $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $ */
/*
@@ -186,6 +186,8 @@ exit1(struct proc *p, int rv, int flags)
#endif
if ((p->p_flag & P_THREAD) == 0) {
+ sigio_freelist(&pr->ps_sigiolst);
+
/* close open files and release open-file table */
fdfree(p);
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 93d80d221ae..dd26b0ac5c8 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_fork.c,v 1.207 2018/08/30 03:30:25 visa Exp $ */
+/* $OpenBSD: kern_fork.c,v 1.208 2018/11/12 15:09:17 visa Exp $ */
/* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */
/*
@@ -208,6 +208,7 @@ process_initialize(struct process *pr, struct proc *p)
LIST_INIT(&pr->ps_children);
LIST_INIT(&pr->ps_ftlist);
LIST_INIT(&pr->ps_kqlist);
+ LIST_INIT(&pr->ps_sigiolst);
timeout_set(&pr->ps_realit_to, realitexpire, pr);
}
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
index 0ce3d285796..934cf455ba1 100644
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_proc.c,v 1.84 2018/06/21 13:58:21 visa Exp $ */
+/* $OpenBSD: kern_proc.c,v 1.85 2018/11/12 15:09:17 visa Exp $ */
/* $NetBSD: kern_proc.c,v 1.14 1996/02/09 18:59:41 christos Exp $ */
/*
@@ -278,6 +278,7 @@ enternewpgrp(struct process *pr, struct pgrp *pgrp, struct session *newsess)
}
pgrp->pg_id = pr->ps_pid;
LIST_INIT(&pgrp->pg_members);
+ LIST_INIT(&pgrp->pg_sigiolst);
LIST_INSERT_HEAD(PGRPHASH(pr->ps_pid), pgrp, pg_hash);
pgrp->pg_jobc = 0;
@@ -328,6 +329,7 @@ leavepgrp(struct process *pr)
void
pgdelete(struct pgrp *pgrp)
{
+ sigio_freelist(&pgrp->pg_sigiolst);
if (pgrp->pg_session->s_ttyp != NULL &&
pgrp->pg_session->s_ttyp->t_pgrp == pgrp)
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 100615dd9b8..de93fa68682 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_sig.c,v 1.224 2018/08/03 14:47:56 deraadt Exp $ */
+/* $OpenBSD: kern_sig.c,v 1.225 2018/11/12 15:09:17 visa Exp $ */
/* $NetBSD: kern_sig.c,v 1.54 1996/04/22 01:38:32 christos Exp $ */
/*
@@ -87,6 +87,10 @@ int cansignal(struct proc *, struct process *, int);
struct pool sigacts_pool; /* memory pool for sigacts structures */
+void sigio_del(struct sigiolst *);
+void sigio_unlink(struct sigio_ref *, struct sigiolst *);
+struct mutex sigio_lock = MUTEX_INITIALIZER(IPL_HIGH);
+
/*
* Can thread p, send the signal signum to process qr?
*/
@@ -698,6 +702,9 @@ killpg1(struct proc *cp, int signum, int pgid, int all)
(euid) == (pr)->ps_ucred->cr_svuid || \
(euid) == (pr)->ps_ucred->cr_uid)
+#define CANSIGIO(cr, pr) \
+ CANDELIVER((cr)->cr_ruid, (cr)->cr_uid, (pr))
+
/*
* Deliver signum to pgid, but first check uid/euid against each
* process and see if it is permitted.
@@ -753,6 +760,37 @@ pgsignal(struct pgrp *pgrp, int signum, int checkctty)
}
/*
+ * Send a SIGIO or SIGURG signal to a process or process group using stored
+ * credentials rather than those of the current process.
+ */
+void
+pgsigio(struct sigio_ref *sir, int sig, int checkctty)
+{
+ struct process *pr;
+ struct sigio *sigio;
+
+ if (sir->sir_sigio == NULL)
+ return;
+
+ mtx_enter(&sigio_lock);
+ sigio = sir->sir_sigio;
+ if (sigio == NULL)
+ goto out;
+ if (sigio->sio_pgid > 0) {
+ if (CANSIGIO(sigio->sio_ucred, sigio->sio_proc))
+ prsignal(sigio->sio_proc, sig);
+ } else if (sigio->sio_pgid < 0) {
+ LIST_FOREACH(pr, &sigio->sio_pgrp->pg_members, ps_pglist) {
+ if (CANSIGIO(sigio->sio_ucred, pr) &&
+ (checkctty == 0 || (pr->ps_flags & PS_CONTROLT)))
+ prsignal(pr, sig);
+ }
+ }
+out:
+ mtx_leave(&sigio_lock);
+}
+
+/*
* Recalculate the signal mask and reset the signal disposition after
* usermode frame for delivery is formed.
*/
@@ -2047,3 +2085,235 @@ single_thread_clear(struct proc *p, int flag)
SCHED_UNLOCK(s);
}
}
+
+void
+sigio_del(struct sigiolst *rmlist)
+{
+ struct sigio *sigio;
+
+ while ((sigio = LIST_FIRST(rmlist)) != NULL) {
+ LIST_REMOVE(sigio, sio_pgsigio);
+ crfree(sigio->sio_ucred);
+ free(sigio, M_SIGIO, sizeof(*sigio));
+ }
+}
+
+void
+sigio_unlink(struct sigio_ref *sir, struct sigiolst *rmlist)
+{
+ struct sigio *sigio;
+
+ MUTEX_ASSERT_LOCKED(&sigio_lock);
+
+ sigio = sir->sir_sigio;
+ if (sigio != NULL) {
+ KASSERT(sigio->sio_myref == sir);
+ sir->sir_sigio = NULL;
+
+ if (sigio->sio_pgid > 0)
+ sigio->sio_proc = NULL;
+ else
+ sigio->sio_pgrp = NULL;
+ LIST_REMOVE(sigio, sio_pgsigio);
+
+ LIST_INSERT_HEAD(rmlist, sigio, sio_pgsigio);
+ }
+}
+
+void
+sigio_free(struct sigio_ref *sir)
+{
+ struct sigiolst rmlist;
+
+ if (sir->sir_sigio == NULL)
+ return;
+
+ LIST_INIT(&rmlist);
+
+ mtx_enter(&sigio_lock);
+ sigio_unlink(sir, &rmlist);
+ mtx_leave(&sigio_lock);
+
+ sigio_del(&rmlist);
+}
+
+void
+sigio_freelist(struct sigiolst *sigiolst)
+{
+ struct sigiolst rmlist;
+ struct sigio *sigio;
+
+ if (LIST_EMPTY(sigiolst))
+ return;
+
+ LIST_INIT(&rmlist);
+
+ mtx_enter(&sigio_lock);
+ while ((sigio = LIST_FIRST(sigiolst)) != NULL)
+ sigio_unlink(sigio->sio_myref, &rmlist);
+ mtx_leave(&sigio_lock);
+
+ sigio_del(&rmlist);
+}
+
+int
+sigio_setown(struct sigio_ref *sir, pid_t pgid)
+{
+ struct sigiolst rmlist;
+ struct proc *p = curproc;
+ struct pgrp *pgrp = NULL;
+ struct process *pr = NULL;
+ struct sigio *sigio;
+ int error;
+
+ if (pgid == 0) {
+ sigio_free(sir);
+ return (0);
+ }
+
+ sigio = malloc(sizeof(*sigio), M_SIGIO, M_WAITOK);
+ sigio->sio_pgid = pgid;
+ sigio->sio_ucred = crhold(p->p_ucred);
+ sigio->sio_myref = sir;
+
+ LIST_INIT(&rmlist);
+
+ /*
+ * The kernel lock, and not sleeping between prfind()/pgfind() and
+ * linking of the sigio ensure that the process or process group does
+ * not disappear unexpectedly.
+ */
+ KERNEL_LOCK();
+ mtx_enter(&sigio_lock);
+
+ if (pgid > 0) {
+ pr = prfind(pgid);
+ if (pr == NULL) {
+ error = ESRCH;
+ goto fail;
+ }
+
+ /*
+ * Policy - Don't allow a process to FSETOWN a process
+ * in another session.
+ *
+ * Remove this test to allow maximum flexibility or
+ * restrict FSETOWN to the current process or process
+ * group for maximum safety.
+ */
+ if (pr->ps_session != p->p_p->ps_session) {
+ error = EPERM;
+ goto fail;
+ }
+
+ if ((pr->ps_flags & PS_EXITING) != 0) {
+ error = ESRCH;
+ goto fail;
+ }
+ } else /* if (pgid < 0) */ {
+ pgrp = pgfind(-pgid);
+ if (pgrp == NULL) {
+ error = ESRCH;
+ goto fail;
+ }
+
+ /*
+ * Policy - Don't allow a process to FSETOWN a process
+ * in another session.
+ *
+ * Remove this test to allow maximum flexibility or
+ * restrict FSETOWN to the current process or process
+ * group for maximum safety.
+ */
+ if (pgrp->pg_session != p->p_p->ps_session) {
+ error = EPERM;
+ goto fail;
+ }
+ }
+
+ if (pgid > 0) {
+ sigio->sio_proc = pr;
+ LIST_INSERT_HEAD(&pr->ps_sigiolst, sigio, sio_pgsigio);
+ } else {
+ sigio->sio_pgrp = pgrp;
+ LIST_INSERT_HEAD(&pgrp->pg_sigiolst, sigio, sio_pgsigio);
+ }
+
+ sigio_unlink(sir, &rmlist);
+ sir->sir_sigio = sigio;
+
+ mtx_leave(&sigio_lock);
+ KERNEL_UNLOCK();
+
+ sigio_del(&rmlist);
+
+ return (0);
+
+fail:
+ mtx_leave(&sigio_lock);
+ KERNEL_UNLOCK();
+
+ crfree(sigio->sio_ucred);
+ free(sigio, M_SIGIO, sizeof(*sigio));
+
+ return (error);
+}
+
+pid_t
+sigio_getown(struct sigio_ref *sir)
+{
+ struct sigio *sigio;
+ pid_t pgid = 0;
+
+ mtx_enter(&sigio_lock);
+ sigio = sir->sir_sigio;
+ if (sigio != NULL)
+ pgid = sigio->sio_pgid;
+ mtx_leave(&sigio_lock);
+
+ return (pgid);
+}
+
+void
+sigio_copy(struct sigio_ref *dst, struct sigio_ref *src)
+{
+ struct sigiolst rmlist;
+ struct sigio *newsigio, *sigio;
+
+ sigio_free(dst);
+
+ if (src->sir_sigio == NULL)
+ return;
+
+ newsigio = malloc(sizeof(*newsigio), M_SIGIO, M_WAITOK);
+ LIST_INIT(&rmlist);
+
+ mtx_enter(&sigio_lock);
+
+ sigio = src->sir_sigio;
+ if (sigio == NULL) {
+ mtx_leave(&sigio_lock);
+ free(newsigio, M_SIGIO, sizeof(*newsigio));
+ return;
+ }
+
+ newsigio->sio_pgid = sigio->sio_pgid;
+ newsigio->sio_ucred = crhold(sigio->sio_ucred);
+ newsigio->sio_myref = dst;
+ if (newsigio->sio_pgid > 0) {
+ newsigio->sio_proc = sigio->sio_proc;
+ LIST_INSERT_HEAD(&newsigio->sio_proc->ps_sigiolst, newsigio,
+ sio_pgsigio);
+ } else {
+ newsigio->sio_pgrp = sigio->sio_pgrp;
+ LIST_INSERT_HEAD(&newsigio->sio_pgrp->pg_sigiolst, newsigio,
+ sio_pgsigio);
+ }
+
+ sigio_unlink(dst, &rmlist);
+ dst->sir_sigio = newsigio;
+
+ mtx_leave(&sigio_lock);
+
+ sigio_del(&rmlist);
+}