summaryrefslogtreecommitdiff
path: root/sys/kern/kern_sig.c
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/kern_sig.c
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/kern_sig.c')
-rw-r--r--sys/kern/kern_sig.c272
1 files changed, 271 insertions, 1 deletions
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);
+}