#include <sys/vm.h>
#include <sys/user.h> /* for coredump */
#include <kern/ast.h> /* for APC support */
-#include <kern/lock.h>
#include <kern/task.h> /* extern void *get_bsdtask_info(task_t); */
#include <kern/thread.h>
#include <kern/sched_prim.h>
#include <libkern/OSAtomic.h>
#include <sys/sdt.h>
+#include <sys/codesign.h>
/*
* Missing prototypes that Mach should export
extern thread_t port_name_to_thread(mach_port_name_t port_name);
extern kern_return_t get_signalact(task_t , thread_t *, int);
extern unsigned int get_useraddr(void);
-extern kern_return_t task_suspend_internal(task_t);
-extern kern_return_t task_resume_internal(task_t);
/*
* ---
static void stop(proc_t, proc_t);
int cansignal(proc_t, kauth_cred_t, proc_t, int, int);
int killpg1(proc_t, int, int, int, int);
-int setsigvec(proc_t, thread_t, int, struct __kern_sigaction *, boolean_t in_sigstart);
static void psignal_uthread(thread_t, int);
+static void psignal_try_thread(proc_t, thread_t, int signum);
kern_return_t do_bsdexception(int, int, int);
void __posix_sem_syscall_return(kern_return_t);
+char *proc_name_address(void *p);
/* implementations in osfmk/kern/sync_sema.c. We do not want port.h in this scope, so void * them */
kern_return_t semaphore_timedwait_signal_trap_internal(mach_port_name_t, mach_port_name_t, unsigned int, clock_res_t, void (*)(kern_return_t));
static int filt_sigattach(struct knote *kn);
static void filt_sigdetach(struct knote *kn);
static int filt_signal(struct knote *kn, long hint);
-static void filt_signaltouch(struct knote *kn, struct kevent64_s *kev,
+static void filt_signaltouch(struct knote *kn, struct kevent_internal_s *kev,
long type);
struct filterops sig_filtops = {
#define PSIG_LOCKED 0x1
#define PSIG_VFORK 0x2
#define PSIG_THREAD 0x4
+#define PSIG_TRY_THREAD 0x8
static void psignal_internal(proc_t p, task_t task, thread_t thread, int flavor, int signum);
if (p == q)
return(1);
+ /* you can't send launchd SIGKILL, even if root */
+ if (signum == SIGKILL && q == initproc)
+ return(0);
+
if (!suser(uc, NULL))
return (1); /* root can always signal */
return (0);
}
+/*
+ * <rdar://problem/21952708> Some signals can be restricted from being handled,
+ * forcing the default action for that signal. This behavior applies only to
+ * non-root (EUID != 0) processes, and is configured with the "sigrestrict=x"
+ * bootarg:
+ *
+ * 0 (default): Disallow use of restricted signals. Trying to register a handler
+ * returns ENOTSUP, which userspace may use to take special action (e.g. abort).
+ * 1: As above, but return EINVAL. Restricted signals behave similarly to SIGKILL.
+ * 2: Usual POSIX semantics.
+ */
+unsigned sigrestrict_arg = 0;
+
+#if PLATFORM_WatchOS
+static int
+sigrestrictmask(void)
+{
+ if (kauth_getuid() != 0 && sigrestrict_arg != 2) {
+ return SIGRESTRICTMASK;
+ }
+ return 0;
+}
+
+static int
+signal_is_restricted(proc_t p, int signum)
+{
+ if (sigmask(signum) & sigrestrictmask()) {
+ if (sigrestrict_arg == 0 &&
+ task_get_apptype(p->task) == TASK_APPTYPE_APP_DEFAULT) {
+ return ENOTSUP;
+ } else {
+ return EINVAL;
+ }
+ }
+ return 0;
+}
+
+#else
+
+static inline int
+signal_is_restricted(proc_t p, int signum)
+{
+ (void)p;
+ (void)signum;
+ return 0;
+}
+#endif /* !PLATFORM_WatchOS */
/*
* Returns: 0 Success
signum = uap->signum;
if (signum <= 0 || signum >= NSIG ||
- signum == SIGKILL || signum == SIGSTOP)
+ signum == SIGKILL || signum == SIGSTOP)
return (EINVAL);
+ if (uap->nsa) {
+ if (IS_64BIT_PROCESS(p)) {
+ struct __user64_sigaction __vec64;
+ error = copyin(uap->nsa, &__vec64, sizeof(__vec64));
+ __sigaction_user64_to_kern(&__vec64, &__vec);
+ } else {
+ struct __user32_sigaction __vec32;
+ error = copyin(uap->nsa, &__vec32, sizeof(__vec32));
+ __sigaction_user32_to_kern(&__vec32, &__vec);
+ }
+ if (error)
+ return (error);
+ __vec.sa_flags &= SA_USERSPACE_MASK; /* Only pass on valid sa_flags */
+
+ if ((__vec.sa_flags & SA_SIGINFO) || __vec.sa_handler != SIG_DFL) {
+ if ((error = signal_is_restricted(p, signum))) {
+ if (error == ENOTSUP) {
+ printf("%s(%d): denied attempt to register action for signal %d\n",
+ proc_name_address(p), proc_pid(p), signum);
+ }
+ return error;
+ }
+ }
+ }
+
if (uap->osa) {
sa->sa_handler = ps->ps_sigact[signum];
sa->sa_mask = ps->ps_catchmask[signum];
if (IS_64BIT_PROCESS(p)) {
struct user64_sigaction vec64;
-
sigaction_kern_to_user64(sa, &vec64);
error = copyout(&vec64, uap->osa, sizeof(vec64));
} else {
struct user32_sigaction vec32;
-
sigaction_kern_to_user32(sa, &vec32);
error = copyout(&vec32, uap->osa, sizeof(vec32));
}
if (error)
return (error);
}
+
if (uap->nsa) {
- if (IS_64BIT_PROCESS(p)) {
- struct __user64_sigaction __vec64;
-
- error = copyin(uap->nsa, &__vec64, sizeof(__vec64));
- __sigaction_user64_to_kern(&__vec64, &__vec);
- } else {
- struct __user32_sigaction __vec32;
-
- error = copyin(uap->nsa, &__vec32, sizeof(__vec32));
- __sigaction_user32_to_kern(&__vec32, &__vec);
- }
- if (error)
- return (error);
- __vec.sa_flags &= SA_USERSPACE_MASK; /* Only pass on valid sa_flags */
error = setsigvec(p, current_thread(), signum, &__vec, FALSE);
}
+
return (error);
}
signal_setast(sig_actthread);
}
+/*
+ * get_signalthread
+ *
+ * Picks an appropriate thread from a process to target with a signal.
+ *
+ * Called with proc locked.
+ * Returns thread with BSD ast set.
+ *
+ * We attempt to deliver a proc-wide signal to the first thread in the task.
+ * This allows single threaded applications which use signals to
+ * be able to be linked with multithreaded libraries.
+ */
static kern_return_t
get_signalthread(proc_t p, int signum, thread_t * thr)
{
thread_t sig_thread;
struct task * sig_task = p->task;
kern_return_t kret;
-
+
*thr = THREAD_NULL;
if ((p->p_lflag & P_LINVFORK) && p->p_vforkact) {
return(KERN_SUCCESS);
}else
return(KERN_FAILURE);
- }
+ }
- proc_lock(p);
TAILQ_FOREACH(uth, &p->p_uthlist, uu_list) {
if(((uth->uu_flag & UT_NO_SIGMASK)== 0) &&
(((uth->uu_sigmask & mask) == 0) || (uth->uu_sigwait & mask))) {
if (check_actforsig(p->task, uth->uu_context.vc_thread, 1) == KERN_SUCCESS) {
*thr = uth->uu_context.vc_thread;
- proc_unlock(p);
return(KERN_SUCCESS);
}
}
}
- proc_unlock(p);
if (get_signalact(p->task, thr, 1) == KERN_SUCCESS) {
return(KERN_SUCCESS);
}
psignal_internal(proc_t p, task_t task, thread_t thread, int flavor, int signum)
{
int prop;
- sig_t action = NULL;
+ user_addr_t action = USER_ADDR_NULL;
proc_t sig_proc;
thread_t sig_thread;
- register task_t sig_task;
+ task_t sig_task;
int mask;
struct uthread *uth;
kern_return_t kret;
kauth_cred_t my_cred;
if ((u_int)signum >= NSIG || signum == 0)
- panic("psignal signal number");
+ panic("psignal: bad signal number %d", signum);
+
mask = sigmask(signum);
prop = sigprop[signum];
}
#endif /* SIGNAL_DEBUG */
+ /* catch unexpected initproc kills early for easier debuggging */
+ if (signum == SIGKILL && p == initproc)
+ panic_plain("unexpected SIGKILL of %s %s",
+ (p->p_name[0] != '\0' ? p->p_name : "initproc"),
+ ((p->p_csflags & CS_KILLED) ? "(CS_KILLED)" : ""));
+
/*
* We will need the task pointer later. Grab it now to
* check for a zombie process. Also don't send signals
sig_task = get_threadtask(thread);
sig_thread = thread;
sig_proc = (proc_t)get_bsdtask_info(sig_task);
+ } else if (flavor & PSIG_TRY_THREAD) {
+ assert((thread == current_thread()) && (p == current_proc()));
+ sig_task = p->task;
+ sig_thread = thread;
+ sig_proc = p;
} else {
sig_task = p->task;
- sig_thread = (struct thread *)0;
+ sig_thread = THREAD_NULL;
sig_proc = p;
}
* also no need to send a signal to a process that is in the middle
* of being torn down.
*/
- if (ISSET(sig_proc->p_flag, P_REBOOT) ||
- ISSET(sig_proc->p_lflag, P_LEXIT))
+ if (ISSET(sig_proc->p_flag, P_REBOOT) || ISSET(sig_proc->p_lflag, P_LEXIT)) {
+ DTRACE_PROC3(signal__discard, thread_t, sig_thread, proc_t, sig_proc, int, signum);
return;
+ }
if( (flavor & (PSIG_VFORK | PSIG_THREAD)) == 0) {
proc_knote(sig_proc, NOTE_SIGNAL | signum);
if ((flavor & PSIG_LOCKED)== 0)
proc_signalstart(sig_proc, 0);
- /*
- * Deliver the signal to the first thread in the task. This
- * allows single threaded applications which use signals to
- * be able to be linked with multithreaded libraries. We have
- * an implicit reference to the current thread, but need
- * an explicit one otherwise. The thread reference keeps
- * the corresponding task data structures around too. This
- * reference is released by thread_deallocate.
- */
-
-
+ /* Don't send signals to a process that has ignored them. */
if (((flavor & PSIG_VFORK) == 0) && ((sig_proc->p_lflag & P_LTRACED) == 0) && (sig_proc->p_sigignore & mask)) {
DTRACE_PROC3(signal__discard, thread_t, sig_thread, proc_t, sig_proc, int, signum);
- goto psigout;
+ goto sigout_unlocked;
}
+ /*
+ * The proc_lock prevents the targeted thread from being deallocated
+ * or handling the signal until we're done signaling it.
+ *
+ * Once the proc_lock is dropped, we have no guarantee the thread or uthread exists anymore.
+ *
+ * XXX: What if the thread goes inactive after the thread passes bsd ast point?
+ */
+ proc_lock(sig_proc);
+
if (flavor & PSIG_VFORK) {
action = SIG_DFL;
act_set_astbsd(sig_thread);
kret = KERN_SUCCESS;
+ } else if (flavor & PSIG_TRY_THREAD) {
+ uth = get_bsdthread_info(sig_thread);
+ if (((uth->uu_flag & UT_NO_SIGMASK) == 0) &&
+ (((uth->uu_sigmask & mask) == 0) || (uth->uu_sigwait & mask)) &&
+ ((kret = check_actforsig(sig_proc->task, sig_thread, 1)) == KERN_SUCCESS)) {
+ /* deliver to specified thread */
+ } else {
+ /* deliver to any willing thread */
+ kret = get_signalthread(sig_proc, signum, &sig_thread);
+ }
} else if (flavor & PSIG_THREAD) {
/* If successful return with ast set */
kret = check_actforsig(sig_task, sig_thread, 1);
/* If successful return with ast set */
kret = get_signalthread(sig_proc, signum, &sig_thread);
}
+
if (kret != KERN_SUCCESS) {
-#if SIGNAL_DEBUG
- ram_printf(1);
-#endif /* SIGNAL_DEBUG */
- goto psigout;
+ DTRACE_PROC3(signal__discard, thread_t, sig_thread, proc_t, sig_proc, int, signum);
+ proc_unlock(sig_proc);
+ goto sigout_unlocked;
}
-
uth = get_bsdthread_info(sig_thread);
/*
* action will be SIG_DFL here.)
*/
if (sig_proc->p_sigignore & mask)
- goto psigout;
+ goto sigout_locked;
+
if (uth->uu_sigwait & mask)
action = KERN_SIG_WAIT;
else if (uth->uu_sigmask & mask)
}
}
-
- proc_lock(sig_proc);
-
+ /* TODO: p_nice isn't hooked up to the scheduler... */
if (sig_proc->p_nice > NZERO && action == SIG_DFL && (prop & SA_KILL) &&
(sig_proc->p_lflag & P_LTRACED) == 0)
sig_proc->p_nice = NZERO;
* is default; don't stop the process below if sleeping,
* and don't clear any pending SIGCONT.
*/
- proc_unlock(sig_proc);
pg = proc_pgrp(sig_proc);
if (prop & SA_TTYSTOP && pg->pg_jobc == 0 &&
action == SIG_DFL) {
pg_rele(pg);
- goto psigout;
+ goto sigout_locked;
}
pg_rele(pg);
- proc_lock(sig_proc);
uth->uu_siglist &= ~contsigmask;
}
uth->uu_siglist |= mask;
- /*
- * Repost AST incase sigthread has processed
- * ast and missed signal post.
- */
- if (action == KERN_SIG_CATCH)
- act_set_astbsd(sig_thread);
-
/*
* Defer further processing for signals which are held,
* except that stopped processes must be continued by SIGCONT.
*/
/* vfork will not go thru as action is SIG_DFL */
- if ((action == KERN_SIG_HOLD) && ((prop & SA_CONT) == 0 || sig_proc->p_stat != SSTOP)) {
- proc_unlock(sig_proc);
- goto psigout;
- }
+ if ((action == KERN_SIG_HOLD) && ((prop & SA_CONT) == 0 || sig_proc->p_stat != SSTOP))
+ goto sigout_locked;
+
/*
* SIGKILL priority twiddling moved here from above because
* it needs sig_thread. Could merge it into large switch
* below if we didn't care about priority for tracing
* as SIGKILL's action is always SIG_DFL.
+ *
+ * TODO: p_nice isn't hooked up to the scheduler...
*/
if ((signum == SIGKILL) && (sig_proc->p_nice > NZERO)) {
sig_proc->p_nice = NZERO;
if (sig_proc->p_lflag & P_LTRACED) {
if (sig_proc->p_stat != SSTOP)
goto runlocked;
- else {
- proc_unlock(sig_proc);
- goto psigout;
- }
+ else
+ goto sigout_locked;
}
+
if ((flavor & PSIG_VFORK) != 0)
goto runlocked;
if (prop & SA_CONT) {
OSBitOrAtomic(P_CONTINUED, &sig_proc->p_flag);
sig_proc->p_contproc = current_proc()->p_pid;
-
- proc_unlock(sig_proc);
(void) task_resume_internal(sig_task);
- goto psigout;
}
- proc_unlock(sig_proc);
- goto psigout;
+ goto sigout_locked;
}
if (action != SIG_DFL) {
*/
if (prop & SA_CONT) {
OSBitOrAtomic(P_CONTINUED, &sig_proc->p_flag);
- proc_unlock(sig_proc);
(void) task_resume_internal(sig_task);
- proc_lock(sig_proc);
sig_proc->p_stat = SRUN;
} else if (sig_proc->p_stat == SSTOP) {
- proc_unlock(sig_proc);
- goto psigout;
+ goto sigout_locked;
}
/*
* Fill out siginfo structure information to pass to the
* Note: Avoid the SIGCHLD recursion case!
*/
if (signum != SIGCHLD) {
- proc_unlock(sig_proc);
r_uid = kauth_getruid();
- proc_lock(sig_proc);
sig_proc->si_pid = current_proc()->p_pid;
sig_proc->si_status = W_EXITCODE(signum, 0);
* stopped from the keyboard.
*/
if (!(prop & SA_STOP) && sig_proc->p_pptr == initproc) {
- proc_unlock(sig_proc);
- psignal_locked(sig_proc, SIGKILL);
- proc_lock(sig_proc);
uth->uu_siglist &= ~mask;
proc_unlock(sig_proc);
- goto psigout;
+ /* siglock still locked, proc_lock not locked */
+ psignal_locked(sig_proc, SIGKILL);
+ goto sigout_unlocked;
}
-
+
/*
* Stop the task
* if task hasn't already been stopped by
psignal(pp, SIGCHLD);
}
- if (pp != PROC_NULL)
+ if (pp != PROC_NULL) {
proc_parentdropref(pp, 0);
- } else
- proc_unlock(sig_proc);
- goto psigout;
+ }
+
+ goto sigout_unlocked;
+ }
+
+ goto sigout_locked;
}
DTRACE_PROC3(signal__send, thread_t, sig_thread, proc_t, p, int, signum);
- /*
- * enters switch with sig_proc lock held but dropped when
- * gets out of switch
- */
switch (signum) {
/*
* Signals ignored by default have been dealt
*/
act_set_astbsd(sig_thread);
thread_abort(sig_thread);
- proc_unlock(sig_proc);
- goto psigout;
+ goto sigout_locked;
case SIGCONT:
/*
OSBitOrAtomic(P_CONTINUED, &sig_proc->p_flag);
sig_proc->p_contproc = sig_proc->p_pid;
- proc_unlock(sig_proc);
(void) task_resume_internal(sig_task);
- proc_lock(sig_proc);
+
/*
* When processing a SIGCONT, we need to check
* to see if there are signals pending that
uth->uu_siglist &= ~mask;
sig_proc->p_stat = SRUN;
- proc_unlock(sig_proc);
- goto psigout;
+ goto sigout_locked;
default:
/*
*/
if (((flavor & (PSIG_VFORK|PSIG_THREAD)) == 0) && (action == SIG_DFL) && (prop & SA_KILL)) {
sig_proc->p_stat = SRUN;
- proc_unlock(sig_proc);
thread_abort(sig_thread);
- goto psigout;
+ goto sigout_locked;
}
/*
* resume it.
*/
if (sig_proc->p_stat == SSTOP) {
- proc_unlock(sig_proc);
- goto psigout;
+ goto sigout_locked;
}
goto runlocked;
}
*/
if (sig_proc->p_stat == SSTOP) {
if ((sig_proc->p_lflag & P_LTRACED) != 0 && sig_proc->p_xstat != 0)
- uth->uu_siglist |= sigmask(sig_proc->p_xstat);
+ uth->uu_siglist |= sigmask(sig_proc->p_xstat);
+
if ((flavor & PSIG_VFORK) != 0) {
sig_proc->p_stat = SRUN;
}
- proc_unlock(sig_proc);
} else {
/*
* setrunnable(p) in BSD and
* Wake up the thread if it is interruptible.
*/
sig_proc->p_stat = SRUN;
- proc_unlock(sig_proc);
if ((flavor & PSIG_VFORK) == 0)
thread_abort_safely(sig_thread);
}
-psigout:
+
+sigout_locked:
+ proc_unlock(sig_proc);
+
+sigout_unlocked:
if ((flavor & PSIG_LOCKED)== 0) {
proc_signalend(sig_proc, 0);
}
psignal_internal(PROC_NULL, TASK_NULL, thread, PSIG_THREAD, signum);
}
+/* same as psignal(), but prefer delivery to 'thread' if possible */
+static void
+psignal_try_thread(proc_t p, thread_t thread, int signum)
+{
+ psignal_internal(p, NULL, thread, PSIG_TRY_THREAD, signum);
+}
/*
* If the current process has received a signal (should be caught or cause
switch ((long)p->p_sigacts->ps_sigact[signum]) {
case (long)SIG_DFL:
- /*
- * Don't take default actions on system processes.
- */
- if (p->p_ppid == 0) {
-#if DIAGNOSTIC
- /*
- * Are you sure you want to ignore SIGSEGV
- * in init? XXX
- */
- printf("Process (pid %d) got signal %d\n",
- p->p_pid, signum);
-#endif
- break; /* == ignore */
- }
-
/*
* If there is a pending stop signal to process
* with default action, stop here,
switch ((long)p->p_sigacts->ps_sigact[signum]) {
case (long)SIG_DFL:
- /*
- * Don't take default actions on system processes.
- */
- if (p->p_ppid == 0) {
-#if DIAGNOSTIC
- /*
- * Are you sure you want to ignore SIGSEGV
- * in init? XXX
- */
- printf("Process (pid %d) got signal %d\n",
- p->p_pid, signum);
-#endif
- break; /* == ignore */
- }
-
/*
* If there is a pending stop signal to process
* with default action, stop here,
}
static void
-filt_signaltouch(struct knote *kn, struct kevent64_s *kev, long type)
+filt_signaltouch(struct knote *kn, struct kevent_internal_s *kev, long type)
{
proc_klist_lock();
switch (type) {
}
break;
default:
- panic("filt_machporttouch() - invalid type (%ld)", type);
+ panic("filt_signaltouch() - invalid type (%ld)", type);
break;
}
proc_klist_unlock();
else
task_vtimer_clear(p->task, TASK_VTIMER_USER);
- psignal(p, SIGVTALRM);
+ psignal_try_thread(p, thread, SIGVTALRM);
}
}
else
task_vtimer_clear(p->task, TASK_VTIMER_PROF);
- psignal(p, SIGPROF);
+ psignal_try_thread(p, thread, SIGPROF);
}
}
task_vtimer_clear(p->task, TASK_VTIMER_RLIM);
- psignal(p, SIGXCPU);
+ psignal_try_thread(p, thread, SIGXCPU);
}
}
{
if (!locked)
proc_lock(p);
+
+ if(p->p_signalholder == current_thread())
+ panic("proc_signalstart: thread attempting to signal a process for which it holds the signal lock");
+
p->p_sigwaitcnt++;
while ((p->p_lflag & P_LINSIGNAL) == P_LINSIGNAL)
msleep(&p->p_sigmask, &p->p_mlock, 0, "proc_signstart", NULL);