Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F152554623
D39830.id121076.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
D39830.id121076.diff
View Options
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -3013,13 +3013,6 @@
prison_deref(pr, drflags);
}
-static void
-prison_kill_processes_cb(struct proc *p, void *arg __unused)
-{
-
- kern_psignal(p, SIGKILL);
-}
-
/*
* Note the iteration does not guarantee acting on all processes.
* Most notably there may be fork or jail_attach in progress.
@@ -3191,7 +3184,7 @@
/* Kill any processes attached to a killed prison. */
if (killpr != NULL)
- prison_proc_iterate(killpr, prison_kill_processes_cb, NULL);
+ kern_prsignal(curthread, killpr, SIGKILL);
/*
* Finish removing any unreferenced prisons, which couldn't happen
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -43,6 +43,7 @@
#include "opt_ktrace.h"
#include <sys/param.h>
+#include <sys/_unrhdr.h>
#include <sys/capsicum.h>
#include <sys/ctype.h>
#include <sys/systm.h>
@@ -83,6 +84,7 @@
#include <sys/sysent.h>
#include <sys/syslog.h>
#include <sys/sysproto.h>
+#include <sys/taskqueue.h>
#include <sys/timers.h>
#include <sys/unistd.h>
#include <sys/vmmeter.h>
@@ -108,7 +110,7 @@
"struct thread *", "struct proc *", "int");
static int coredump(struct thread *);
-static int killpg1(struct thread *td, int sig, int pgid, int all,
+static int killpg1(struct thread *td, int sig, int pgid, struct prison *pr,
ksiginfo_t *ksi);
static int issignal(struct thread *td);
static void reschedule_signals(struct proc *p, sigset_t block, int flags);
@@ -1765,27 +1767,105 @@
struct killpg1_ctx {
struct thread *td;
+ struct proc *target;
+ struct ucred *cr;
+ struct sx *lock;
ksiginfo_t *ksi;
int sig;
bool sent;
bool found;
+ int count;
int ret;
+ struct unrhdr pids;
+ struct task t;
};
static void
-killpg1_sendsig_locked(struct proc *p, struct killpg1_ctx *arg)
+killpg1_kill_proc_locked(struct killpg1_ctx *arg)
{
+ struct proc *p;
+ bool need_stop;
int err;
- err = p_cansignal(arg->td, p, arg->sig);
- if (err == 0 && arg->sig != 0)
- pksignal(p, arg->sig, arg->ksi);
- if (err != ESRCH)
+ p = arg->target;
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ PROC_ASSERT_HELD(p);
+
+ err = cr_cansignal(arg->cr, p, arg->sig);
+ if (err != ESRCH) {
arg->found = true;
+ arg->count++;
+ }
if (err == 0)
arg->sent = true;
else if (arg->ret == 0 && err != ESRCH && err != EPERM)
arg->ret = err;
+ if (err != 0 || arg->sig == 0)
+ return;
+
+ /*
+ * The need_stop indicates if the target process needs to be
+ * suspended before being signalled. This is needed when we
+ * guarantee that all processes in subtree are signalled,
+ * avoiding the race with some process not yet fully linked
+ * into all structures during fork, ignored by iterator, and
+ * then escaping signalling.
+ *
+ * The thread cannot usefully stop itself anyway, and if other
+ * thread of the current process forks while the current
+ * thread signals the whole subtree, it is an application
+ * race.
+ */
+
+ if ((p->p_flag & (P_KPROC | P_SYSTEM | P_STOPPED)) == 0)
+ need_stop = thread_single(p, SINGLE_ALLPROC) == 0;
+ else
+ need_stop = false;
+ (void)pksignal(p, arg->sig, arg->ksi);
+ if (need_stop)
+ thread_single_end(p, SINGLE_ALLPROC);
+}
+
+
+static void
+killpg1_proc_work(void *arg, int pending __unused)
+{
+ struct killpg1_ctx *w;
+
+ w = arg;
+ PROC_LOCK(w->target);
+ if ((w->target->p_flag2 & P2_WEXIT) == 0)
+ killpg1_kill_proc_locked(w);
+ PROC_UNLOCK(w->target);
+
+ sx_xlock(w->lock);
+ w->target = NULL;
+ wakeup(&w->target);
+ sx_xunlock(w->lock);
+}
+
+
+static void
+killpg1_sendsig_locked(struct proc *p, struct killpg1_ctx *arg, struct sx *lock)
+{
+
+ sx_assert(lock, SX_SLOCKED);
+
+ if ((p->p_flag2 & P2_WEXIT) != 0)
+ return;
+ _PHOLD_LITE(p);
+ PROC_UNLOCK(p);
+ if (alloc_unr_specific(&arg->pids, p->p_pid) != p->p_pid)
+ goto out;
+ arg->target = p;
+ arg->lock = lock;
+ taskqueue_enqueue(taskqueue_thread, &arg->t);
+ while (arg->target != NULL) {
+ sx_sleep(&arg->target, lock, PWAIT, "killpg", 0);
+ }
+out:
+ PROC_LOCK(p);
+ _PRELE(p);
}
static void
@@ -1797,7 +1877,7 @@
return;
PROC_LOCK(p);
- killpg1_sendsig_locked(p, arg);
+ killpg1_sendsig_locked(p, arg, &proctree_lock);
PROC_UNLOCK(p);
}
@@ -1810,7 +1890,7 @@
(p == ctx->td->td_proc) || p->p_state == PRS_NEW)
return;
- killpg1_sendsig_locked(p, ctx);
+ killpg1_sendsig_locked(p, ctx, &allproc_lock);
}
/*
@@ -1818,51 +1898,86 @@
* cp is calling process.
*/
static int
-killpg1(struct thread *td, int sig, int pgid, int all, ksiginfo_t *ksi)
+killpg1(struct thread *td, int sig, int pgid, struct prison *pr,
+ ksiginfo_t *ksi)
{
- struct proc *p;
+ struct proc *p, *ptmp;
struct pgrp *pgrp;
struct killpg1_ctx arg;
+ arg.cr = crhold(td->td_ucred);
arg.td = td;
arg.ksi = ksi;
arg.sig = sig;
arg.sent = false;
- arg.found = false;
+ arg.count = 0;
arg.ret = 0;
- if (all) {
- /*
- * broadcast
- */
- prison_proc_iterate(td->td_ucred->cr_prison,
- kill_processes_prison_cb, &arg);
- } else {
- sx_slock(&proctree_lock);
- if (pgid == 0) {
+ init_unrhdr(&arg.pids, 1, PID_MAX, UNR_NO_MTX);
+ TASK_INIT(&arg.t, 0, killpg1_proc_work, &arg);
+
+ /*
+ * Prevent swapout, since ctx, ksi are
+ * allocated on the stack. We sleep in
+ * killpg1_sendsig_locked() waiting for
+ * task to complete single-threading.
+ */
+ PHOLD(td->td_proc);
+
+ do {
+ arg.found = false;
+ if (pr != NULL) {
/*
- * zero pgid means send to my process group.
+ * broadcast
*/
- pgrp = td->td_proc->p_pgrp;
- PGRP_LOCK(pgrp);
+ prison_proc_iterate(pr, kill_processes_prison_cb, &arg);
} else {
- pgrp = pgfind(pgid);
- if (pgrp == NULL) {
- sx_sunlock(&proctree_lock);
- return (ESRCH);
+ sx_slock(&proctree_lock);
+ if (pgid == 0) {
+ /*
+ * zero pgid means send to my process group.
+ */
+ pgrp = td->td_proc->p_pgrp;
+ PGRP_LOCK(pgrp);
+ } else {
+ pgrp = pgfind(pgid);
+ if (pgrp == NULL) {
+ sx_sunlock(&proctree_lock);
+ arg.ret = ESRCH;
+ goto out;
+ }
}
+ PGRP_UNLOCK(pgrp);
+ LIST_FOREACH_SAFE(p, &pgrp->pg_members, p_pglist, ptmp)
+ killpg1_sendsig(p, false, &arg);
+ sx_sunlock(&proctree_lock);
}
- sx_sunlock(&proctree_lock);
- LIST_FOREACH(p, &pgrp->pg_members, p_pglist) {
- killpg1_sendsig(p, false, &arg);
- }
- PGRP_UNLOCK(pgrp);
- }
- MPASS(arg.ret != 0 || arg.found || !arg.sent);
+ } while (arg.found);
+out:
+ PRELE(td->td_proc);
+ crfree(arg.cr);
+ clean_unrhdr(&arg.pids);
+ clear_unrhdr(&arg.pids);
+
+ MPASS(arg.ret != 0 || arg.count != 0 || !arg.sent);
if (arg.ret == 0 && !arg.sent)
- arg.ret = arg.found ? EPERM : ESRCH;
+ arg.ret = arg.count != 0 ? EPERM : ESRCH;
return (arg.ret);
}
+int
+kern_prsignal(struct thread *td, struct prison *pr, int sig)
+{
+ ksiginfo_t ksi;
+
+ ksiginfo_init(&ksi);
+ ksi.ksi_signo = sig;
+ ksi.ksi_code = SI_USER;
+ ksi.ksi_pid = td->td_proc->p_pid;
+ ksi.ksi_uid = td->td_ucred->cr_ruid;
+
+ return (killpg1(td, sig, 0, pr, &ksi));
+}
+
#ifndef _SYS_SYSPROTO_H_
struct kill_args {
int pid;
@@ -1916,11 +2031,11 @@
}
switch (pid) {
case -1: /* broadcast signal */
- return (killpg1(td, signum, 0, 1, &ksi));
+ return (killpg1(td, signum, 0, td->td_ucred->cr_prison, &ksi));
case 0: /* signal own process group */
- return (killpg1(td, signum, 0, 0, &ksi));
+ return (killpg1(td, signum, 0, NULL, &ksi));
default: /* negative explicit process group */
- return (killpg1(td, signum, -pid, 0, &ksi));
+ return (killpg1(td, signum, -pid, NULL, &ksi));
}
/* NOTREACHED */
}
diff --git a/sys/sys/signalvar.h b/sys/sys/signalvar.h
--- a/sys/sys/signalvar.h
+++ b/sys/sys/signalvar.h
@@ -324,6 +324,7 @@
}
struct pgrp;
+struct prison;
struct proc;
struct sigio;
struct thread;
@@ -390,6 +391,7 @@
void pgsigio(struct sigio **sigiop, int sig, int checkctty);
void pgsignal(struct pgrp *pgrp, int sig, int checkctty, ksiginfo_t *ksi);
int postsig(int sig);
+int kern_prsignal(struct thread *td, struct prison *pr, int sig);
void kern_psignal(struct proc *p, int sig);
int ptracestop(struct thread *td, int sig, ksiginfo_t *si);
void sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *retmask);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Apr 16, 4:02 PM (35 m, 32 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31538645
Default Alt Text
D39830.id121076.diff (8 KB)
Attached To
Mode
D39830: killpg1: Fix a race between fork(2) and killpg1()
Attached
Detach File
Event Timeline
Log In to Comment