Print this page
OS-4437 lxbrand ptrace turns harmless signals deadly
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-3742 lxbrand add support for signalfd
OS-4382 remove obsolete brand hooks added during lx development
OS-4306 lxbrand setsockopt(IP_MULTICAST_TTL) handles optlen poorly
OS-4303 lxbrand ltp ptrace05 fails
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
OS-3820 lxbrand ptrace(2): the next generation
OS-3685 lxbrand PTRACE_O_TRACEFORK race condition
OS-3834 lxbrand 64-bit strace(1) reports 64-bit process as using x32 ABI
OS-3794 lxbrand panic on init signal death
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Bryan Cantrill <bryan@joyent.com>
OS-3642 failed assert when exiting zlogin due to OS-3140 fix
OS-3140 In LX zone 'ps fax' does not show all processes
OS-2844 lx brand should support 64-bit user-land
@@ -20,11 +20,11 @@
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
@@ -59,10 +59,11 @@
#include <sys/contract/process_impl.h>
#include <sys/cyclic.h>
#include <sys/dtrace.h>
#include <sys/sdt.h>
#include <sys/signalfd.h>
+#include <sys/brand.h>
const k_sigset_t nullsmask = {0, 0, 0};
const k_sigset_t fillset = /* MUST be contiguous */
{FILLSET0, FILLSET1, FILLSET2};
@@ -146,10 +147,25 @@
return (sigismember(&t->t_hold, sig) ||
(schedctl_sigblock(t) && !sigismember(&cantmask, sig)));
}
/*
+ * Return true if the signal can safely be ignored.
+ * That is, if the signal is included in the p_ignore mask and doing so is not
+ * forbidden by any process branding.
+ */
+static int
+sig_ignorable(proc_t *p, klwp_t *lwp, int sig)
+{
+ return (sigismember(&p->p_ignore, sig) && /* sig in ignore mask */
+ !(PROC_IS_BRANDED(p) && /* allowed by brand */
+ BROP(p)->b_sig_ignorable != NULL &&
+ BROP(p)->b_sig_ignorable(p, lwp, sig) == B_FALSE));
+
+}
+
+/*
* Return true if the signal can safely be discarded on generation.
* That is, if there is no need for the signal on the receiving end.
* The answer is true if the process is a zombie or
* if all of these conditions are true:
* the signal is being ignored
@@ -157,16 +173,17 @@
* the signal is not being traced by /proc
* the signal is not blocked by the process
* the signal is not being accepted via sigwait()
*/
static int
-sig_discardable(proc_t *p, int sig)
+sig_discardable(proc_t *p, kthread_t *tp, int sig)
{
kthread_t *t = p->p_tlist;
+ klwp_t *lwp = (tp == NULL) ? NULL : tp->t_lwp;
return (t == NULL || /* if zombie or ... */
- (sigismember(&p->p_ignore, sig) && /* signal is ignored */
+ (sig_ignorable(p, lwp, sig) && /* signal is ignored */
t->t_forw == t && /* and single-threaded */
!tracing(p, sig) && /* and no /proc tracing */
!signal_is_blocked(t, sig) && /* and signal not blocked */
!sigismember(&t->t_sigwait, sig))); /* and not being accepted */
}
@@ -198,11 +215,11 @@
rval = 1;
} else if (t->t_state == TS_STOPPED && sig == SIGKILL &&
!(ttoproc(t)->p_proc_flag & P_PR_LOCK)) {
ttoproc(t)->p_stopsig = 0;
t->t_dtrace_stop = 0;
- t->t_schedflag |= TS_XSTART | TS_PSTART;
+ t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
setrun_locked(t);
} else if (t != curthread && t->t_state == TS_ONPROC) {
aston(t); /* make it do issig promptly */
if (t->t_cpu != CPU)
poke_cpu(t->t_cpu->cpu_id);
@@ -295,11 +312,11 @@
sigdelset(&tt->t_extsig, SIGCONT);
} while ((tt = tt->t_forw) != p->p_tlist);
}
}
- if (sig_discardable(p, sig)) {
+ if (sig_discardable(p, t, sig)) {
DTRACE_PROC3(signal__discard, kthread_t *, p->p_tlist,
proc_t *, p, int, sig);
return;
}
@@ -495,11 +512,11 @@
for (sig = 1; sig < NSIG; sig++) {
if (sigismember(&set, sig) &&
(tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sigismember(&p->p_ignore, sig))) {
+ !sig_ignorable(p, lwp, sig))) {
/*
* Don't promote a signal that will stop
* the process when lwp_nostop is set.
*/
if (!lwp->lwp_nostop ||
@@ -621,10 +638,25 @@
stop(PR_SUSPENDED, SUSPEND_NORMAL);
continue;
}
/*
+ * Allow the brand the chance to alter (or suppress) delivery
+ * of this signal.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_issig_stop != NULL) {
+ /*
+ * The brand hook will return 0 if it would like
+ * us to drive on, or -1 if we should restart
+ * the loop to check other conditions.
+ */
+ if (BROP(p)->b_issig_stop(p, lwp) != 0) {
+ continue;
+ }
+ }
+
+ /*
* Honor requested stop before dealing with the
* current signal; a debugger may change it.
* Do not want to go back to loop here since this is a special
* stop that means: make incremental progress before the next
* stop. The danger is that returning to top of loop would most
@@ -654,11 +686,11 @@
if ((sig = lwp->lwp_cursig) != 0) {
ext = lwp->lwp_extsig;
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
if (sigismember(&t->t_sigwait, sig) ||
- (!sigismember(&p->p_ignore, sig) &&
+ (!sig_ignorable(p, lwp, sig) &&
!isjobstop(sig))) {
if (p->p_flag & (SEXITLWPS|SKILLED)) {
sig = SIGKILL;
ext = (p->p_flag & SEXTKILLED) != 0;
}
@@ -706,11 +738,11 @@
for (;;) {
if ((sig = fsig(&t->t_sig, t)) != 0) {
toproc = 0;
if (tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sigismember(&p->p_ignore, sig)) {
+ !sig_ignorable(p, lwp, sig)) {
if (sigismember(&t->t_extsig, sig))
ext = 1;
break;
}
sigdelset(&t->t_sig, sig);
@@ -720,11 +752,11 @@
if (sig == SIGCLD)
sigcld_found = 1;
toproc = 1;
if (tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sigismember(&p->p_ignore, sig)) {
+ !sig_ignorable(p, lwp, sig)) {
if (sigismember(&p->p_extsig, sig))
ext = 1;
break;
}
sigdelset(&p->p_sig, sig);
@@ -952,10 +984,20 @@
}
flags &= ~TS_CSTART;
}
break;
+ case PR_BRAND:
+ /*
+ * We have been stopped by the brand code for a brand-private
+ * reason. This is an asynchronous stop affecting only this
+ * LWP.
+ */
+ VERIFY(PROC_IS_BRANDED(p));
+ flags &= ~TS_BSTART;
+ break;
+
default: /* /proc stop */
flags &= ~TS_PSTART;
/*
* Do synchronous stop unless the async-stop flag is set.
* If why is PR_REQUESTED and t->t_dtrace_stop flag is set,
@@ -1063,11 +1105,11 @@
*/
cv_broadcast(&p->p_holdlwps);
}
}
- if (why != PR_JOBCONTROL && why != PR_CHECKPOINT) {
+ if (why != PR_JOBCONTROL && why != PR_CHECKPOINT && why != PR_BRAND) {
/*
* Do process-level notification when all lwps are
* either stopped on events of interest to /proc
* or are stopped showing PR_SUSPENDED or are zombies.
*/
@@ -1169,10 +1211,17 @@
* stopped) because we can't call mutex_enter from a stopped thread.
*/
if (why == PR_CHECKPOINT)
del_one_utstop();
+ /*
+ * Allow the brand to post notification of this stop condition.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_stop_notify != NULL) {
+ BROP(p)->b_stop_notify(p, lwp, why, what);
+ }
+
thread_lock(t);
ASSERT((t->t_schedflag & TS_ALLSTART) == 0);
t->t_schedflag |= flags;
t->t_whystop = (short)why;
t->t_whatstop = (short)what;
@@ -1190,11 +1239,11 @@
sigismember(&p->p_sig, SIGKILL) ||
(t->t_proc_flag & TP_LWPEXIT) ||
(p->p_flag & (SEXITLWPS|SKILLED))) {
p->p_stopsig = 0;
thread_lock(t);
- t->t_schedflag |= TS_XSTART | TS_PSTART;
+ t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
setrun_locked(t);
thread_unlock_nopreempt(t);
} else if (why == PR_JOBCONTROL) {
if (p->p_flag & SSCONT) {
/*
@@ -1325,11 +1374,11 @@
/*
* The signal disposition could have changed since we promoted
* this signal from pending to current (we dropped p->p_lock).
* This can happen only in a multi-threaded process.
*/
- if (sigismember(&p->p_ignore, sig) ||
+ if (sig_ignorable(p, lwp, sig) ||
(func == SIG_DFL && sigismember(&stopdefault, sig))) {
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
if (lwp->lwp_curinfo) {
siginfofree(lwp->lwp_curinfo);
@@ -1769,13 +1818,16 @@
cp->p_pidflag &= ~CLDPEND;
if (sqp == NULL) {
/*
* This can only happen when the parent is init.
* (See call to sigcld(q, NULL) in exit().)
- * Use KM_NOSLEEP to avoid deadlock.
+ * Use KM_NOSLEEP to avoid deadlock. The child procs
+ * initpid can be 1 for zlogin.
*/
- ASSERT(pp == proc_init);
+ ASSERT(pp->p_pidp->pid_id ==
+ cp->p_zone->zone_proc_initpid ||
+ pp->p_pidp->pid_id == 1);
winfo(cp, &info, 0);
sigaddq(pp, NULL, &info, KM_NOSLEEP);
} else {
winfo(cp, &sqp->sq_info, 0);
sigaddqa(pp, NULL, sqp);
@@ -1802,10 +1854,19 @@
proc_t *cp;
sigqueue_t *sqp;
sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
mutex_enter(&pidlock);
+ if (PROC_IS_BRANDED(pp) && BROP(pp)->b_sigcld_repost != NULL) {
+ /*
+ * Allow the brand to inject synthetic SIGCLD signals.
+ */
+ if (BROP(pp)->b_sigcld_repost(pp, sqp) == 0) {
+ mutex_exit(&pidlock);
+ return;
+ }
+ }
for (cp = pp->p_child; cp; cp = cp->p_sibling) {
if (cp->p_pidflag & CLDPEND) {
post_sigcld(cp, sqp);
mutex_exit(&pidlock);
return;
@@ -2113,11 +2174,11 @@
int sig = sigqp->sq_info.si_signo;
ASSERT(MUTEX_HELD(&p->p_lock));
ASSERT(sig >= 1 && sig < NSIG);
- if (sig_discardable(p, sig))
+ if (sig_discardable(p, t, sig))
siginfofree(sigqp);
else
sigaddqins(p, t, sigqp);
sigtoproc(p, t, sig);
@@ -2139,11 +2200,11 @@
* If the signal will be discarded by sigtoproc() or
* if the process isn't requesting siginfo and it isn't
* blocking the signal (it *could* change it's mind while
* the signal is pending) then don't bother creating one.
*/
- if (!sig_discardable(p, sig) &&
+ if (!sig_discardable(p, t, sig) &&
(sigismember(&p->p_siginfo, sig) ||
(curproc->p_ct_process != p->p_ct_process) ||
(sig == SIGCLD && SI_FROMKERNEL(infop))) &&
((sqp = kmem_alloc(sizeof (sigqueue_t), km_flags)) != NULL)) {
bcopy(infop, &sqp->sq_info, sizeof (k_siginfo_t));