X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/0c530ab8987f0ae6a1a3d9284f40182b88852816..c910b4d9d2451126ae3917b931cd4390c11e1d52:/bsd/kern/kern_sig.c diff --git a/bsd/kern/kern_sig.c b/bsd/kern/kern_sig.c index 479b1f5e1..b9b14ffb5 100644 --- a/bsd/kern/kern_sig.c +++ b/bsd/kern/kern_sig.c @@ -1,25 +1,30 @@ /* - * Copyright (c) 2000-2001 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1995-2007 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -/* Copyright (c) 1995-1998 Apple Computer, Inc. All Rights Reserved */ /* * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -59,6 +64,12 @@ * * @(#)kern_sig.c 8.7 (Berkeley) 4/18/94 */ +/* + * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + */ #define SIGPROP /* include signal properties table */ #include @@ -73,9 +84,6 @@ #include #include #include -#if KTRACE -#include -#endif #include #include #include @@ -101,6 +109,9 @@ #include #include #include +#include + +#include /* * Missing prototypes that Mach should export @@ -108,9 +119,7 @@ * +++ */ extern int thread_enable_fpe(thread_t act, int onoff); -extern void unix_syscall_return(int error); extern thread_t port_name_to_thread(mach_port_name_t port_name); -extern kern_return_t check_actforsig(task_t task, thread_t thread, int setast); extern kern_return_t get_signalact(task_t , thread_t *, int); extern boolean_t thread_should_abort(thread_t); extern unsigned int get_useraddr(void); @@ -119,23 +128,22 @@ extern unsigned int get_useraddr(void); * --- */ -extern void doexception(int exc, int code, int sub); +extern void doexception(int exc, mach_exception_code_t code, + mach_exception_subcode_t sub); -void stop(struct proc *p); -int cansignal(struct proc *, kauth_cred_t, struct proc *, int); -int killpg1(struct proc *, int, int, int); -void sigexit_locked(struct proc *, int); -int setsigvec(struct proc *, int, struct __user_sigaction *); -void exit1(struct proc *, int, int *); -void psignal_uthread(thread_t, int); +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, int, struct __user_sigaction *); +static void psignal_uthread(thread_t, int); kern_return_t do_bsdexception(int, int, int); void __posix_sem_syscall_return(kern_return_t); /* 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(void *, void *,time_t, int32_t, void (*)(int)); -kern_return_t semaphore_timedwait_trap_internal(void *, time_t, int32_t, void (*)(int)); -kern_return_t semaphore_wait_signal_trap_internal(void *, void *, void (*)(int)); -kern_return_t semaphore_wait_trap_internal(void *, void (*)(int)); +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)); +kern_return_t semaphore_timedwait_trap_internal(mach_port_name_t, unsigned int, clock_res_t, void (*)(kern_return_t)); +kern_return_t semaphore_wait_signal_trap_internal(mach_port_name_t, mach_port_name_t, void (*)(kern_return_t)); +kern_return_t semaphore_wait_trap_internal(mach_port_name_t, void (*)(kern_return_t)); static int filt_sigattach(struct knote *kn); static void filt_sigdetach(struct knote *kn); @@ -144,6 +152,35 @@ static int filt_signal(struct knote *kn, long hint); struct filterops sig_filtops = { 0, filt_sigattach, filt_sigdetach, filt_signal }; +/* structures and fns for killpg1 iterartion callback and filters */ +struct killpg1_filtargs { + int posix; + proc_t cp; +}; + +struct killpg1_iterargs { + proc_t cp; + kauth_cred_t uc; + int signum; + int * nfoundp; +}; + +static int killpg1_filt(proc_t p, void * arg); +static int killpg1_pgrpfilt(proc_t p, __unused void * arg); +static int killpg1_callback(proc_t p, void * arg); + +static int pgsignal_filt(proc_t p, void * arg); +static int pgsignal_callback(proc_t p, void * arg); +static kern_return_t get_signalthread(proc_t, int, thread_t *); + + +/* flags for psignal_internal */ +#define PSIG_LOCKED 0x1 +#define PSIG_VFORK 0x2 +#define PSIG_THREAD 0x4 + + +static void psignal_internal(proc_t p, task_t task, thread_t thread, int flavor, int signum); /* * NOTE: Source and target may *NOT* overlap! (target is smaller) @@ -200,81 +237,32 @@ ram_printf(int x) } #endif /* SIGNAL_DEBUG */ -int -signal_lock(struct proc *p) -{ -int error = 0; -#if DIAGNOSTIC -#if SIGNAL_DEBUG -#ifdef __ppc__ - { - int register sp, *fp, numsaved; - - __asm__ volatile("mr %0,r1" : "=r" (sp)); - - fp = (int *)*((int *)sp); - for (numsaved = 0; numsaved < 3; numsaved++) { - p->lockpc[numsaved] = fp[2]; - if ((int)fp <= 0) - break; - fp = (int *)*fp; - } - } -#endif /* __ppc__ */ -#endif /* SIGNAL_DEBUG */ -#endif /* DIAGNOSTIC */ - -siglock_retry: - error = lockmgr((struct lock__bsd__ *)&p->signal_lock[0], LK_EXCLUSIVE, 0, (struct proc *)0); - if (error == EINTR) - goto siglock_retry; - return(error); -} - -int -signal_unlock(struct proc *p) -{ -#if DIAGNOSTIC -#if SIGNAL_DEBUG -#ifdef __ppc__ - { - int register sp, *fp, numsaved; - - __asm__ volatile("mr %0,r1" : "=r" (sp)); - - fp = (int *)*((int *)sp); - for (numsaved = 0; numsaved < 3; numsaved++) { - p->unlockpc[numsaved] = fp[2]; - if ((int)fp <= 0) - break; - fp = (int *)*fp; - } - } -#endif /* __ppc__ */ -#endif /* SIGNAL_DEBUG */ -#endif /* DIAGNOSTIC */ - - /* TBD: check p last arg */ - return(lockmgr((struct lock__bsd__ *)&p->signal_lock[0], LK_RELEASE, (simple_lock_t)0, (struct proc *)0)); -} void -signal_setast(sig_actthread) -thread_t sig_actthread; +signal_setast(thread_t sig_actthread) { act_set_astbsd(sig_actthread); } /* * Can process p, with ucred uc, send the signal signum to process q? + * uc is refcounted by the caller so internal fileds can be used safely + * when called with zombie arg, list lock is held */ int -cansignal(p, uc, q, signum) - struct proc *p; - kauth_cred_t uc; - struct proc *q; - int signum; +cansignal(proc_t p, kauth_cred_t uc, proc_t q, int signum, int zombie) { + kauth_cred_t my_cred; + struct session * p_sessp = SESSION_NULL; + struct session * q_sessp = SESSION_NULL; +#if CONFIG_MACF + int error; + + error = mac_proc_check_signal(p, q, signum); + if (error) + return (0); +#endif + /* you can signal yourself */ if (p == q) return(1); @@ -282,61 +270,67 @@ cansignal(p, uc, q, signum) if (!suser(uc, NULL)) return (1); /* root can always signal */ - if (signum == SIGCONT && q->p_session == p->p_session) + if (zombie == 0) + proc_list_lock(); + if (p->p_pgrp != PGRP_NULL) + p_sessp = p->p_pgrp->pg_session; + if (q->p_pgrp != PGRP_NULL) + q_sessp = q->p_pgrp->pg_session; + + if (signum == SIGCONT && q_sessp == p_sessp) { + if (zombie == 0) + proc_list_unlock(); return (1); /* SIGCONT in session */ + } + + if (zombie == 0) + proc_list_unlock(); /* - * Using kill(), only certain signals can be sent to setugid - * child processes + * If the real or effective UID of the sender matches the real, + * effective, or ssaved UID of the target, permit the signal to + * be sent. */ - if (q->p_flag & P_SUGID) { - switch (signum) { - case 0: - case SIGKILL: - case SIGINT: - case SIGTERM: - case SIGSTOP: - case SIGTTIN: - case SIGTTOU: - case SIGTSTP: - case SIGHUP: - case SIGUSR1: - case SIGUSR2: - if (uc->cr_ruid == q->p_ucred->cr_ruid || - kauth_cred_getuid(uc) == q->p_ucred->cr_ruid || - uc->cr_ruid == kauth_cred_getuid(q->p_ucred) || - kauth_cred_getuid(uc) == kauth_cred_getuid(q->p_ucred)) - return (1); - } - return (0); + if (zombie == 0) + my_cred = kauth_cred_proc_ref(q); + else + my_cred = proc_ucred(q); + + if (uc->cr_ruid == my_cred->cr_ruid || + uc->cr_ruid == my_cred->cr_svuid || + kauth_cred_getuid(uc) == my_cred->cr_ruid || + kauth_cred_getuid(uc) == my_cred->cr_svuid || + uc->cr_ruid == kauth_cred_getuid(my_cred) || + kauth_cred_getuid(uc) == kauth_cred_getuid(my_cred)) { + if (zombie == 0) + kauth_cred_unref(&my_cred); + return (1); } - /* XXX - * because the P_SUGID test exists, this has extra tests which - * could be removed. - */ - if (uc->cr_ruid == q->p_ucred->cr_ruid || - uc->cr_ruid == q->p_ucred->cr_svuid || - kauth_cred_getuid(uc) == q->p_ucred->cr_ruid || - kauth_cred_getuid(uc) == q->p_ucred->cr_svuid || - uc->cr_ruid == kauth_cred_getuid(q->p_ucred) || - kauth_cred_getuid(uc) == kauth_cred_getuid(q->p_ucred)) - return (1); + if (zombie == 0) + kauth_cred_unref(&my_cred); + return (0); } +/* + * Returns: 0 Success + * EINVAL + * copyout:EFAULT + * copyin:EFAULT + */ /* ARGSUSED */ int -sigaction(struct proc *p, register struct sigaction_args *uap, __unused register_t *retval) +sigaction(proc_t p, struct sigaction_args *uap, __unused register_t *retval) { struct user_sigaction vec; struct __user_sigaction __vec; struct user_sigaction *sa = &vec; - register struct sigacts *ps = p->p_sigacts; + struct sigacts *ps = p->p_sigacts; - register int signum; + int signum; int bit, error=0; signum = uap->signum; @@ -384,6 +378,7 @@ sigaction(struct proc *p, register struct sigaction_args *uap, __unused register } if (error) return (error); + __vec.sa_flags &= SA_USERSPACE_MASK; /* Only pass on valid sa_flags */ error = setsigvec(p, signum, &__vec); } return (error); @@ -391,115 +386,132 @@ sigaction(struct proc *p, register struct sigaction_args *uap, __unused register /* Routines to manipulate bits on all threads */ int -clear_procsiglist(struct proc *p, int bit) +clear_procsiglist(proc_t p, int bit) { struct uthread * uth; thread_t thact; - signal_lock(p); + proc_lock(p); + proc_signalstart(p, 1); - if ((p->p_flag & P_INVFORK) && p->p_vforkact) { + if ((p->p_lflag & P_LINVFORK) && p->p_vforkact) { thact = p->p_vforkact; uth = (struct uthread *)get_bsdthread_info(thact); if (uth) { uth->uu_siglist &= ~bit; } - p->p_siglist &= ~bit; - signal_unlock(p); + proc_signalend(p, 1); + proc_unlock(p); return(0); } TAILQ_FOREACH(uth, &p->p_uthlist, uu_list) { uth->uu_siglist &= ~bit; } - p->p_siglist &= ~bit; - signal_unlock(p); + + proc_signalend(p, 1); + proc_unlock(p); + return(0); } static int -unblock_procsigmask(struct proc *p, int bit) +unblock_procsigmask(proc_t p, int bit) { struct uthread * uth; thread_t thact; - signal_lock(p); - if ((p->p_flag & P_INVFORK) && p->p_vforkact) { + proc_lock(p); + proc_signalstart(p, 1); + + if ((p->p_lflag & P_LINVFORK) && p->p_vforkact) { thact = p->p_vforkact; uth = (struct uthread *)get_bsdthread_info(thact); if (uth) { uth->uu_sigmask &= ~bit; } p->p_sigmask &= ~bit; - signal_unlock(p); + proc_signalend(p, 1); + proc_unlock(p); return(0); } TAILQ_FOREACH(uth, &p->p_uthlist, uu_list) { uth->uu_sigmask &= ~bit; } p->p_sigmask &= ~bit; - signal_unlock(p); + + proc_signalend(p, 1); + proc_unlock(p); return(0); } - static int -block_procsigmask(struct proc *p, int bit) +block_procsigmask(proc_t p, int bit) { struct uthread * uth; thread_t thact; - signal_lock(p); - if ((p->p_flag & P_INVFORK) && p->p_vforkact) { + proc_lock(p); + proc_signalstart(p, 1); + + if ((p->p_lflag & P_LINVFORK) && p->p_vforkact) { thact = p->p_vforkact; uth = (struct uthread *)get_bsdthread_info(thact); if (uth) { uth->uu_sigmask |= bit; } p->p_sigmask |= bit; - signal_unlock(p); + proc_signalend(p, 1); + proc_unlock(p); return(0); } TAILQ_FOREACH(uth, &p->p_uthlist, uu_list) { uth->uu_sigmask |= bit; } p->p_sigmask |= bit; - signal_unlock(p); + + proc_signalend(p, 1); + proc_unlock(p); return(0); } int -set_procsigmask(struct proc *p, int bit) +set_procsigmask(proc_t p, int bit) { struct uthread * uth; thread_t thact; - signal_lock(p); - if ((p->p_flag & P_INVFORK) && p->p_vforkact) { + proc_lock(p); + proc_signalstart(p, 1); + + if ((p->p_lflag & P_LINVFORK) && p->p_vforkact) { thact = p->p_vforkact; uth = (struct uthread *)get_bsdthread_info(thact); if (uth) { uth->uu_sigmask = bit; } p->p_sigmask = bit; - signal_unlock(p); + proc_signalend(p, 1); + proc_unlock(p); return(0); } TAILQ_FOREACH(uth, &p->p_uthlist, uu_list) { uth->uu_sigmask = bit; } p->p_sigmask = bit; - signal_unlock(p); + proc_signalend(p, 1); + proc_unlock(p); + return(0); } /* XXX should be static? */ int -setsigvec(struct proc *p, int signum, struct __user_sigaction *sa) +setsigvec(proc_t p, int signum, struct __user_sigaction *sa) { - register struct sigacts *ps = p->p_sigacts; - register int bit; + struct sigacts *ps = p->p_sigacts; + int bit; if ((signum == SIGKILL || signum == SIGSTOP) && sa->sa_handler != SIG_DFL) @@ -541,13 +553,13 @@ setsigvec(struct proc *p, int signum, struct __user_sigaction *sa) ps->ps_signodefer &= ~bit; if (signum == SIGCHLD) { if (sa->sa_flags & SA_NOCLDSTOP) - p->p_flag |= P_NOCLDSTOP; + OSBitOrAtomic(P_NOCLDSTOP, (UInt32 *)&p->p_flag); else - p->p_flag &= ~P_NOCLDSTOP; + OSBitAndAtomic(~((uint32_t)P_NOCLDSTOP), (UInt32 *)&p->p_flag); if ((sa->sa_flags & SA_NOCLDWAIT) || (sa->sa_handler == SIG_IGN)) - p->p_flag |= P_NOCLDWAIT; + OSBitOrAtomic(P_NOCLDWAIT, (UInt32 *)&p->p_flag); else - p->p_flag &= ~P_NOCLDWAIT; + OSBitAndAtomic(~((uint32_t)P_NOCLDWAIT), (UInt32 *)&p->p_flag); } #ifdef __ppc__ @@ -586,10 +598,9 @@ setsigvec(struct proc *p, int signum, struct __user_sigaction *sa) * set to ignore signals that are ignored by default. */ void -siginit(p) - struct proc *p; +siginit(proc_t p) { - register int i; + int i; for (i = 0; i < NSIG; i++) if (sigprop[i] & SA_IGNORE && i != SIGCONT) @@ -600,17 +611,13 @@ siginit(p) * Reset signals for an exec of the specified process. */ void -execsigs(p, thr_act) - register struct proc *p; - register thread_t thr_act; +execsigs(proc_t p, thread_t thread) { - register struct sigacts *ps = p->p_sigacts; - register int nc, mask; - struct uthread *ut = (struct uthread *)0; + struct sigacts *ps = p->p_sigacts; + int nc, mask; + struct uthread *ut; - if (thr_act){ - ut = (struct uthread *)get_bsdthread_info(thr_act); - } + ut = (struct uthread *)get_bsdthread_info(thread); /* * Reset caught signals. Held signals remain held * through p_sigmask (unless they were caught, @@ -623,9 +630,8 @@ execsigs(p, thr_act) if (sigprop[nc] & SA_IGNORE) { if (nc != SIGCONT) p->p_sigignore |= mask; - if (thr_act){ + if (thread){ ut->uu_siglist &= ~mask; - p->p_siglist &= ~mask; } else clear_procsiglist(p, mask); } @@ -635,16 +641,12 @@ execsigs(p, thr_act) * Reset stack state to the user stack. * Clear set of signals caught on the signal stack. */ - ps->ps_sigstk.ss_flags = SA_DISABLE; - ps->ps_sigstk.ss_size = 0; - ps->ps_sigstk.ss_sp = USER_ADDR_NULL; - ps->ps_flags = 0; - if (thr_act) { - ut->uu_sigstk.ss_flags = SA_DISABLE; - ut->uu_sigstk.ss_size = 0; - ut->uu_sigstk.ss_sp = USER_ADDR_NULL; - ut->uu_flag &= ~UT_ALTSTACK; - } + /* thread */ + ut->uu_sigstk.ss_flags = SA_DISABLE; + ut->uu_sigstk.ss_size = 0; + ut->uu_sigstk.ss_sp = USER_ADDR_NULL; + ut->uu_flag &= ~UT_ALTSTACK; + /* process */ ps->ps_sigonstack = 0; } @@ -655,7 +657,7 @@ execsigs(p, thr_act) * the library stub does the rest. */ int -sigprocmask(register struct proc *p, struct sigprocmask_args *uap, __unused register_t *retval) +sigprocmask(proc_t p, struct sigprocmask_args *uap, __unused register_t *retval) { int error = 0; sigset_t oldmask, nmask; @@ -700,7 +702,7 @@ out: } int -sigpending(__unused struct proc *p, register struct sigpending_args *uap, __unused register_t *retval) +sigpending(__unused proc_t p, struct sigpending_args *uap, __unused register_t *retval) { struct uthread *ut; sigset_t pendlist; @@ -713,7 +715,6 @@ sigpending(__unused struct proc *p, register struct sigpending_args *uap, __unus return(0); } - /* * Suspend process until signal, providing mask to be set * in the meantime. Note nonstandard calling convention: @@ -724,11 +725,18 @@ static int sigcontinue(__unused int error) { // struct uthread *ut = get_bsdthread_info(current_thread()); - unix_syscall_return(EINTR); + unix_syscall_return(EINTR); +} + +int +sigsuspend(proc_t p, struct sigsuspend_args *uap, register_t *retval) +{ + __pthread_testcancel(1); + return(sigsuspend_nocancel(p, (struct sigsuspend_nocancel_args *)uap, retval)); } int -sigsuspend(register struct proc *p, struct sigsuspend_args *uap, __unused register_t *retval) +sigsuspend_nocancel(proc_t p, struct sigsuspend_nocancel_args *uap, __unused register_t *retval) { struct uthread *ut; @@ -751,29 +759,47 @@ sigsuspend(register struct proc *p, struct sigsuspend_args *uap, __unused regist int -__disable_threadsignal(struct proc *p, - __unused register struct __disable_threadsignal_args *uap, - __unused register_t *retval) +__disable_threadsignal(__unused proc_t p, + __unused struct __disable_threadsignal_args *uap, + __unused register_t *retval) { struct uthread *uth; uth = (struct uthread *)get_bsdthread_info(current_thread()); /* No longer valid to have any signal delivered */ - signal_lock(p); - uth->uu_flag |= UT_NO_SIGMASK; - signal_unlock(p); + uth->uu_flag |= (UT_NO_SIGMASK | UT_CANCELDISABLE); return(0); } +void +__pthread_testcancel(int presyscall) +{ + + thread_t self = current_thread(); + struct uthread * uthread; + + uthread = (struct uthread *)get_bsdthread_info(self); + + + uthread->uu_flag &= ~UT_NOTCANCELPT; + + if ((uthread->uu_flag & (UT_CANCELDISABLE | UT_CANCEL | UT_CANCELED)) == UT_CANCEL) { + if(presyscall != 0) { + unix_syscall_return(EINTR); + /* NOTREACHED */ + } else + thread_abort_safely(self); + } +} + + int -__pthread_markcancel(p, uap, retval) - struct proc *p; - register struct __pthread_markcancel_args *uap; - register_t *retval; +__pthread_markcancel(__unused proc_t p, + struct __pthread_markcancel_args *uap, __unused register_t *retval) { thread_act_t target_act; int error = 0; @@ -787,7 +813,7 @@ __pthread_markcancel(p, uap, retval) uth = (struct uthread *)get_bsdthread_info(target_act); /* if the thread is in vfork do not cancel */ - if ((uth->uu_flag & (P_VFORK | UT_CANCEL | UT_CANCELED )) == 0) { + if ((uth->uu_flag & (UT_VFORK | UT_CANCEL | UT_CANCELED )) == 0) { uth->uu_flag |= (UT_CANCEL | UT_NO_SIGMASK); if (((uth->uu_flag & UT_NOTCANCELPT) == 0) && ((uth->uu_flag & UT_CANCELDISABLE) == 0)) @@ -804,17 +830,15 @@ __pthread_markcancel(p, uap, retval) * if action = 2; Disable the cancel handling */ int -__pthread_canceled(p, uap, retval) - struct proc *p; - register struct __pthread_canceled_args *uap; - register_t *retval; +__pthread_canceled(__unused proc_t p, + struct __pthread_canceled_args *uap, __unused register_t *retval) { - thread_act_t thr_act; + thread_act_t thread; struct uthread *uth; int action = uap->action; - thr_act = current_act(); - uth = (struct uthread *)get_bsdthread_info(thr_act); + thread = current_thread(); + uth = (struct uthread *)get_bsdthread_info(thread); switch (action) { case 1: @@ -853,12 +877,23 @@ __posix_sem_syscall_return(kern_return_t kern_result) /* does not return */ } +/* + * Returns: 0 Success + * EINTR + * ETIMEDOUT + * EINVAL + */ +int +__semwait_signal(__unused proc_t p, struct __semwait_signal_args *uap, + register_t *retval) +{ + __pthread_testcancel(0); + return(__semwait_signal_nocancel(p, (struct __semwait_signal_nocancel_args *)uap, retval)); +} int -__semwait_signal(p, uap, retval) - struct proc *p; - register struct __semwait_signal_args *uap; - register_t *retval; +__semwait_signal_nocancel(__unused proc_t p, struct __semwait_signal_nocancel_args *uap, + __unused register_t *retval) { kern_return_t kern_result; @@ -878,23 +913,27 @@ __semwait_signal(p, uap, retval) then.tv_nsec += NSEC_PER_SEC; then.tv_sec--; } + /* if time has elapsed, set time to null timepsec to bailout rightaway */ + if ((int)then.tv_sec < 0) { + then.tv_sec = 0; + then.tv_nsec = 0; + } } - if (uap->mutex_sem == (void *)NULL) - kern_result = semaphore_timedwait_trap_internal(uap->cond_sem, then.tv_sec, then.tv_nsec, __posix_sem_syscall_return); + if (uap->mutex_sem == 0) + kern_result = semaphore_timedwait_trap_internal((mach_port_name_t)uap->cond_sem, then.tv_sec, then.tv_nsec, __posix_sem_syscall_return); else kern_result = semaphore_timedwait_signal_trap_internal(uap->cond_sem, uap->mutex_sem, then.tv_sec, then.tv_nsec, __posix_sem_syscall_return); } else { - if (uap->mutex_sem == (void *)NULL) + if (uap->mutex_sem == 0) kern_result = semaphore_wait_trap_internal(uap->cond_sem, __posix_sem_syscall_return); else kern_result = semaphore_wait_signal_trap_internal(uap->cond_sem, uap->mutex_sem, __posix_sem_syscall_return); } -out: if (kern_result == KERN_SUCCESS) return(0); else if (kern_result == KERN_ABORTED) @@ -905,11 +944,9 @@ out: return(EINVAL); } - int -__pthread_kill(__unused struct proc *p, - register struct __pthread_kill_args *uap, - __unused register_t *retval) +__pthread_kill(__unused proc_t p, struct __pthread_kill_args *uap, + __unused register_t *retval) { thread_t target_act; int error = 0; @@ -941,9 +978,8 @@ out: int -pthread_sigmask(__unused register struct proc *p, - register struct pthread_sigmask_args *uap, - __unused register_t *retval) +__pthread_sigmask(__unused proc_t p, struct __pthread_sigmask_args *uap, + __unused register_t *retval) { user_addr_t set = uap->set; user_addr_t oset = uap->oset; @@ -990,9 +1026,21 @@ out: return(error); } +/* + * Returns: 0 Success + * EINVAL + * copyin:EFAULT + * copyout:EFAULT + */ +int +__sigwait(proc_t p, struct __sigwait_args *uap, register_t *retval) +{ + __pthread_testcancel(1); + return(__sigwait_nocancel(p, (struct __sigwait_nocancel_args *)uap, retval)); +} int -sigwait(register struct proc *p, register struct sigwait_args *uap, __unused register_t *retval) +__sigwait_nocancel(proc_t p, struct __sigwait_nocancel_args *uap, __unused register_t *retval) { struct uthread *ut; struct uthread *uth; @@ -1016,18 +1064,20 @@ sigwait(register struct proc *p, register struct sigwait_args *uap, __unused reg if (siglist == 0) return(EINVAL); - signal_lock(p); - if ((p->p_flag & P_INVFORK) && p->p_vforkact) { - signal_unlock(p); + proc_lock(p); + if ((p->p_lflag & P_LINVFORK) && p->p_vforkact) { + proc_unlock(p); return(EINVAL); } else { + proc_signalstart(p, 1); TAILQ_FOREACH(uth, &p->p_uthlist, uu_list) { if ( (sigw = uth->uu_siglist & siglist) ) { break; } } + proc_signalend(p, 1); } - signal_unlock(p); + if (sigw) { /* The signal was pending on a thread */ goto sigwait1; @@ -1039,15 +1089,19 @@ sigwait(register struct proc *p, register struct sigwait_args *uap, __unused reg * save it here and mark the sigacts structure * to indicate this. */ + uth = ut; /* wait for it to be delivered to us */ ut->uu_oldmask = ut->uu_sigmask; ut->uu_flag |= UT_SAS_OLDMASK; - if (siglist == (sigset_t)0) + if (siglist == (sigset_t)0) { + proc_unlock(p); return(EINVAL); + } /* SIGKILL and SIGSTOP are not maskable as well */ ut->uu_sigmask = ~(siglist|sigcantmask); ut->uu_sigwait = siglist; + /* No Continuations for now */ - error = tsleep((caddr_t)&ut->uu_sigwait, PPAUSE|PCATCH, "pause", 0); + error = msleep((caddr_t)&ut->uu_sigwait, &p->p_mlock, PPAUSE|PCATCH, "pause", 0); if ((error == EINTR) || (error == ERESTART)) error = 0; @@ -1062,39 +1116,33 @@ sigwait1: signum = ffs((unsigned int)sigw); if (!signum) panic("sigwait with no signal wakeup"); - ut->uu_siglist &= ~(sigmask(signum)); + /* Clear the pending signal in the thread it was delivered */ + uth->uu_siglist &= ~(sigmask(signum)); + proc_unlock(p); if (uap->sig != USER_ADDR_NULL) error = copyout(&signum, uap->sig, sizeof(int)); - } + } else + proc_unlock(p); return(error); } - int -sigaltstack(struct proc *p, register struct sigaltstack_args *uap, __unused register_t *retval) +sigaltstack(__unused proc_t p, struct sigaltstack_args *uap, __unused register_t *retval) { - struct sigacts *psp; - struct user_sigaltstack *pstk; struct user_sigaltstack ss; - struct uthread *uth; - int uthsigaltstack = 0; + struct user_sigaltstack *pstk; int error; + struct uthread *uth; + int onstack; uth = (struct uthread *)get_bsdthread_info(current_thread()); - uthsigaltstack = p->p_lflag & P_LTHSIGSTACK; - psp = p->p_sigacts; - if (uthsigaltstack != 0) { - pstk = &uth->uu_sigstk; - if ((uth->uu_flag & UT_ALTSTACK) == 0) - uth->uu_sigstk.ss_flags |= SA_DISABLE; - } else { - pstk = &psp->ps_sigstk; - if ((psp->ps_flags & SAS_ALTSTACK) == 0) - psp->ps_sigstk.ss_flags |= SA_DISABLE; - } + pstk = &uth->uu_sigstk; + if ((uth->uu_flag & UT_ALTSTACK) == 0) + uth->uu_sigstk.ss_flags |= SA_DISABLE; + onstack = pstk->ss_flags & SA_ONSTACK; if (uap->oss) { if (IS_64BIT_PROCESS(p)) { error = copyout(pstk, uap->oss, sizeof(struct user_sigaltstack)); @@ -1122,40 +1170,30 @@ sigaltstack(struct proc *p, register struct sigaltstack_args *uap, __unused regi } if (ss.ss_flags & SA_DISABLE) { - if (uthsigaltstack != 0) { - /* if we are here we are not in the signal handler ;so no need to check */ - if (uth->uu_sigstk.ss_flags & SA_ONSTACK) - return (EINVAL); - uth->uu_flag &= ~UT_ALTSTACK; - uth->uu_sigstk.ss_flags = ss.ss_flags; - } else { - if (psp->ps_sigstk.ss_flags & SA_ONSTACK) - return (EINVAL); - psp->ps_flags &= ~SAS_ALTSTACK; - psp->ps_sigstk.ss_flags = ss.ss_flags; - } - + /* if we are here we are not in the signal handler ;so no need to check */ + if (uth->uu_sigstk.ss_flags & SA_ONSTACK) + return (EINVAL); + uth->uu_flag &= ~UT_ALTSTACK; + uth->uu_sigstk.ss_flags = ss.ss_flags; return (0); } + if (onstack) + return (EPERM); /* The older stacksize was 8K, enforce that one so no compat problems */ #define OLDMINSIGSTKSZ 8*1024 if (ss.ss_size < OLDMINSIGSTKSZ) return (ENOMEM); - if (uthsigaltstack != 0) { - uth->uu_flag |= UT_ALTSTACK; - uth->uu_sigstk= ss; - } else { - psp->ps_flags |= SAS_ALTSTACK; - psp->ps_sigstk= ss; - } + uth->uu_flag |= UT_ALTSTACK; + uth->uu_sigstk= ss; return (0); } int -kill(struct proc *cp, struct kill_args *uap, __unused register_t *retval) +kill(proc_t cp, struct kill_args *uap, __unused register_t *retval) { - register struct proc *p; + proc_t p; kauth_cred_t uc = kauth_cred_get(); + int posix = uap->posix; /* !0 if posix behaviour desired */ AUDIT_ARG(pid, uap->pid); AUDIT_ARG(signum, uap->signum); @@ -1164,7 +1202,7 @@ kill(struct proc *cp, struct kill_args *uap, __unused register_t *retval) return (EINVAL); if (uap->pid > 0) { /* kill single process */ - if ((p = proc_findref(uap->pid)) == NULL) { + if ((p = proc_find(uap->pid)) == NULL) { if ((p = pzfind(uap->pid)) != NULL) { /* * IEEE Std 1003.1-2001: return success @@ -1175,139 +1213,226 @@ kill(struct proc *cp, struct kill_args *uap, __unused register_t *retval) return (ESRCH); } AUDIT_ARG(process, p); - if (!cansignal(cp, uc, p, uap->signum)) { - proc_dropref(p); + if (!cansignal(cp, uc, p, uap->signum, 0)) { + proc_rele(p); return(EPERM); } if (uap->signum) psignal(p, uap->signum); - proc_dropref(p); + proc_rele(p); return (0); } switch (uap->pid) { case -1: /* broadcast signal */ - return (killpg1(cp, uap->signum, 0, 1)); + return (killpg1(cp, uap->signum, 0, 1, posix)); case 0: /* signal own process group */ - return (killpg1(cp, uap->signum, 0, 0)); + return (killpg1(cp, uap->signum, 0, 0, posix)); default: /* negative explicit process group */ - return (killpg1(cp, uap->signum, -(uap->pid), 0)); + return (killpg1(cp, uap->signum, -(uap->pid), 0, posix)); } /* NOTREACHED */ } +static int +killpg1_filt(proc_t p, void * arg) +{ + struct killpg1_filtargs * kfargp = (struct killpg1_filtargs *)arg; + proc_t cp = kfargp->cp; + int posix = kfargp->posix; + + + if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || + (!posix && p == cp)) + return(0); + else + return(1); +} + + +static int +killpg1_pgrpfilt(proc_t p, __unused void * arg) +{ + if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || + (p->p_stat == SZOMB)) + return(0); + else + return(1); +} + + + +static int +killpg1_callback(proc_t p, void * arg) +{ + struct killpg1_iterargs * kargp = (struct killpg1_iterargs *)arg; + proc_t cp = kargp->cp; + kauth_cred_t uc = kargp->uc; /* refcounted by the caller safe to use internal fields */ + int signum = kargp->signum; + int * nfoundp = kargp->nfoundp; + int n; + + + if (cansignal(cp, uc, p, signum, 0) == 0) + return(PROC_RETURNED); + + if (nfoundp != NULL) { + n = *nfoundp; + *nfoundp = n+1; + } + if (signum != 0) + psignal(p, signum); + + return(PROC_RETURNED); + +} /* * Common code for kill process group/broadcast kill. * cp is calling process. */ int -killpg1(cp, signum, pgid, all) - register struct proc *cp; - int signum, pgid, all; +killpg1(proc_t cp, int signum, int pgid, int all, int posix) { - register struct proc *p; - kauth_cred_t uc = cp->p_ucred; + proc_t p; + kauth_cred_t uc; struct pgrp *pgrp; int nfound = 0; + struct killpg1_iterargs karg; + struct killpg1_filtargs kfarg; + int error = 0; + uc = kauth_cred_proc_ref(cp); if (all) { /* * broadcast */ - for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { + kfarg.posix = posix; + kfarg.cp = cp; + + karg.cp = cp; + karg.uc = uc; + karg.nfoundp = &nfound; + karg.signum = signum; + + proc_iterate(PROC_ALLPROCLIST, killpg1_callback, &karg, killpg1_filt, (void *)&kfarg); + /* + * Signalling zombies is a no-op, but they must be counted + * among those processes which have been signalled, since + * they are still members of the process group. + */ + + proc_list_lock(); + + for (p = zombproc.lh_first; p != 0; p = p->p_list.le_next) { if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || - p == cp || !cansignal(cp, uc, p, signum)) + (!posix && p == cp) || !cansignal(cp, uc, p, signum, 1)) continue; nfound++; - if (signum) - psignal(p, signum); } + + proc_list_unlock(); + } else { - if (pgid == 0) + if (pgid == 0) { /* * zero pgid means send to my process group. */ - pgrp = cp->p_pgrp; - else { + pgrp = proc_pgrp(cp); + } else { pgrp = pgfind(pgid); - if (pgrp == NULL) - return (ESRCH); - } - for (p = pgrp->pg_members.lh_first; p != 0; - p = p->p_pglist.le_next) { - if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || - p->p_stat == SZOMB || - !cansignal(cp, uc, p, signum)) - continue; - nfound++; - if (signum) - psignal(p, signum); + if (pgrp == NULL) { + error = ESRCH; + goto out; + } } + + karg.nfoundp = &nfound; + karg.uc = uc; + karg.signum = signum; + karg.cp = cp; + + + /* PGRP_DROPREF drops the pgrp refernce */ + pgrp_iterate(pgrp, PGRP_BLOCKITERATE | PGRP_DROPREF, killpg1_callback, &karg, + killpg1_pgrpfilt, NULL); } - return (nfound ? 0 : ESRCH); + error = (nfound ? 0 : (posix ? EPERM : ESRCH)); +out: + kauth_cred_unref(&uc); + return (error); } + /* * Send a signal to a process group. */ void -gsignal(pgid, signum) - int pgid, signum; +gsignal(int pgid, int signum) { struct pgrp *pgrp; - if (pgid && (pgrp = pgfind(pgid))) + if (pgid && (pgrp = pgfind(pgid))) { pgsignal(pgrp, signum, 0); + pg_rele(pgrp); + } } /* - * Send a signal to a process group. If checktty is 1, + * Send a signal to a process group. If checkctty is 1, * limit to members which have a controlling terminal. */ -void -pgsignal(pgrp, signum, checkctty) - struct pgrp *pgrp; - int signum, checkctty; + +static int +pgsignal_filt(proc_t p, void * arg) { - register struct proc *p; + int checkctty = (int)arg; - if (pgrp) - for (p = pgrp->pg_members.lh_first; p != 0; - p = p->p_pglist.le_next) - if (checkctty == 0 || p->p_flag & P_CONTROLT) - psignal(p, signum); + if ((checkctty == 0) || p->p_flag & P_CONTROLT) + return(1); + else + return(0); } -/* - * Send signal to a backgrounded process blocked due to tty access - * In FreeBSD, the backgrounded process wakes up every second and - * discovers whether it is foregounded or not. In our case, we block - * the thread in tsleep as we want to avoid storm of processes as well - * as the suspend is only at AST level - */ -void -tty_pgsignal(pgrp, signum) - struct pgrp *pgrp; - int signum; + +static int +pgsignal_callback(proc_t p, void * arg) { - register struct proc *p; + int signum = (int)arg; - if (pgrp) - for (p = pgrp->pg_members.lh_first; p != 0; - p = p->p_pglist.le_next) - if ((p->p_flag & P_TTYSLEEP) && (p->p_flag & P_CONTROLT)) - psignal(p, signum); + psignal(p, signum); + return(PROC_RETURNED); +} + + +void +pgsignal(struct pgrp *pgrp, int signum, int checkctty) +{ + if (pgrp != PGRP_NULL) { + pgrp_iterate(pgrp, PGRP_BLOCKITERATE, pgsignal_callback, (void *)signum, pgsignal_filt, (void *)checkctty); + } } + +void +tty_pgsignal(struct tty *tp, int signum, int checkctty) +{ + struct pgrp * pg; + + pg = tty_pgrp(tp); + if (pg != PGRP_NULL) { + pgrp_iterate(pg, PGRP_BLOCKITERATE, pgsignal_callback, (void *)signum, pgsignal_filt, (void *)checkctty); + pg_rele(pg); + } +} /* * Send a signal caused by a trap to a specific thread. */ void -threadsignal(thread_t sig_actthread, int signum, u_long code) +threadsignal(thread_t sig_actthread, int signum, mach_exception_code_t code) { - register struct uthread *uth; - register struct task * sig_task; - register struct proc *p ; + struct uthread *uth; + struct task * sig_task; + proc_t p; int mask; if ((u_int)signum >= NSIG || signum == 0) @@ -1317,38 +1442,93 @@ threadsignal(thread_t sig_actthread, int signum, u_long code) if ((mask & threadmask) == 0) return; sig_task = get_threadtask(sig_actthread); - p = (struct proc *)(get_bsdtask_info(sig_task)); + p = (proc_t)(get_bsdtask_info(sig_task)); uth = get_bsdthread_info(sig_actthread); if (uth && (uth->uu_flag & UT_VFORK)) p = uth->uu_proc; - if (!(p->p_flag & P_TRACED) && (p->p_sigignore & mask)) + proc_lock(p); + if (!(p->p_lflag & P_LTRACED) && (p->p_sigignore & mask)) { + proc_unlock(p); return; + } uth->uu_siglist |= mask; - p->p_siglist |= mask; /* just for lame ones looking here */ uth->uu_code = code; + proc_unlock(p); + /* mark on process as well */ signal_setast(sig_actthread); } - -void -psignal(p, signum) - register struct proc *p; - register int signum; +static kern_return_t +get_signalthread(proc_t p, int signum, thread_t * thr) { - psignal_lock(p, signum, 1); + struct uthread *uth; + sigset_t mask = sigmask(signum); + 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) { + sig_thread = p->p_vforkact; + kret = check_actforsig(sig_task, sig_thread, 1); + if (kret == KERN_SUCCESS) { + *thr = sig_thread; + 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); + } + + return(KERN_FAILURE); } -void -psignal_vfork(struct proc *p, task_t new_task, thread_t thr_act, int signum) +/* + * Send the signal to the process. If the signal has an action, the action + * is usually performed by the target process rather than the caller; we add + * the signal to the set of pending signals for the process. + * + * Exceptions: + * o When a stop signal is sent to a sleeping process that takes the + * default action, the process is stopped without awakening it. + * o SIGCONT restarts stopped processes (or puts them back to sleep) + * regardless of the signal action (eg, blocked or ignored). + * + * Other ignored signals are discarded immediately. + */ +static void +psignal_internal(proc_t p, task_t task, thread_t thread, int flavor, int signum) { - register int prop; - register sig_t action; + int prop; + sig_t action = NULL; + proc_t sig_proc; + thread_t sig_thread; + register task_t sig_task; int mask; struct uthread *uth; + kern_return_t kret; + uid_t r_uid; + proc_t pp; + kauth_cred_t my_cred; if ((u_int)signum >= NSIG || signum == 0) panic("psignal signal number"); @@ -1356,195 +1536,49 @@ psignal_vfork(struct proc *p, task_t new_task, thread_t thr_act, int signum) prop = sigprop[signum]; #if SIGNAL_DEBUG - if(rdebug_proc && (p == rdebug_proc)) { + if(rdebug_proc && (p != PROC_NULL) && (p == rdebug_proc)) { ram_printf(3); } #endif /* SIGNAL_DEBUG */ - if ((new_task == TASK_NULL) || (thr_act == (thread_t)NULL) || is_kerneltask(new_task)) - return; - - - uth = get_bsdthread_info(thr_act); - signal_lock(p); - - /* - * proc is traced, always give parent a chance. - */ - action = SIG_DFL; - - if (p->p_nice > NZERO && action == SIG_DFL && (prop & SA_KILL) && - (p->p_flag & P_TRACED) == 0) - p->p_nice = NZERO; - - if (prop & SA_CONT) { - p->p_siglist &= ~stopsigmask; - uth->uu_siglist &= ~stopsigmask; - } - - if (prop & SA_STOP) { - /* - * If sending a tty stop signal to a member of an orphaned - * process group, discard the signal here if the action - * is default; don't stop the process below if sleeping, - * and don't clear any pending SIGCONT. - */ - if (prop & SA_TTYSTOP && p->p_pgrp->pg_jobc == 0 && - action == SIG_DFL) - goto psigout; - uth->uu_siglist &= ~contsigmask; - p->p_siglist &= ~contsigmask; - } - uth->uu_siglist |= mask; - p->p_siglist |= mask; /* just for lame ones looking here */ - - /* Deliver signal to the activation passed in */ - act_set_astbsd(thr_act); - - /* - * 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. - */ - if ((signum == SIGKILL) && (p->p_nice > NZERO)) { - p->p_nice = NZERO; - } - - /* - * This Process is traced - wake it up (if not already - * stopped) so that it can discover the signal in - * issig() and stop for the parent. - */ - if (p->p_flag & P_TRACED) { - if (p->p_stat != SSTOP) - goto run; - else - goto psigout; - } -run: - /* - * If we're being traced (possibly because someone attached us - * while we were stopped), check for a signal from the debugger. - */ - if (p->p_stat == SSTOP) { - if ((p->p_flag & P_TRACED) != 0 && p->p_xstat != 0) { - uth->uu_siglist |= sigmask(p->p_xstat); - p->p_siglist |= mask; /* just for lame ones looking here */ - } - } - - /* - * setrunnable(p) in BSD - */ - p->p_stat = SRUN; - -psigout: - signal_unlock(p); -} - -static thread_t -get_signalthread(struct proc *p, int signum) -{ - struct uthread *uth; - thread_t thr_act; - sigset_t mask = sigmask(signum); - thread_t sig_thread_act; - struct task * sig_task = p->task; - kern_return_t kret; - - if ((p->p_flag & P_INVFORK) && p->p_vforkact) { - sig_thread_act = p->p_vforkact; - kret = check_actforsig(sig_task, sig_thread_act, 1); - if (kret == KERN_SUCCESS) - return(sig_thread_act); - else - return(THREAD_NULL); - } - - 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_act, 1) == KERN_SUCCESS) - return(uth->uu_act); - } - } - if (get_signalact(p->task, &thr_act, 1) == KERN_SUCCESS) { - return(thr_act); - } - - return(THREAD_NULL); -} - -/* - * Send the signal to the process. If the signal has an action, the action - * is usually performed by the target process rather than the caller; we add - * the signal to the set of pending signals for the process. - * - * Exceptions: - * o When a stop signal is sent to a sleeping process that takes the - * default action, the process is stopped without awakening it. - * o SIGCONT restarts stopped processes (or puts them back to sleep) - * regardless of the signal action (eg, blocked or ignored). - * - * Other ignored signals are discarded immediately. - */ -void -psignal_lock(p, signum, withlock) - register struct proc *p; - register int signum; - register int withlock; -{ - register int prop; - register sig_t action; - thread_t sig_thread_act; - register task_t sig_task; - int mask; - struct uthread *uth; - boolean_t funnel_state = FALSE; - int sw_funnel = 0; - - if ((u_int)signum >= NSIG || signum == 0) - panic("psignal signal number"); - mask = sigmask(signum); - prop = sigprop[signum]; - -#if SIGNAL_DEBUG - if(rdebug_proc && (p == rdebug_proc)) { - ram_printf(3); - } -#endif /* SIGNAL_DEBUG */ - - if (thread_funnel_get() == (funnel_t *)0) { - sw_funnel = 1; - funnel_state = thread_funnel_set(kernel_flock, TRUE); - } /* * We will need the task pointer later. Grab it now to * check for a zombie process. Also don't send signals * to kernel internal tasks. */ - if (((sig_task = p->task) == TASK_NULL) || is_kerneltask(sig_task)) { - if (sw_funnel) - thread_funnel_set(kernel_flock, funnel_state); + if (flavor & PSIG_VFORK) { + sig_task = task; + sig_thread = thread; + sig_proc= p; + } else if (flavor & PSIG_THREAD) { + sig_task = get_threadtask(thread); + sig_thread = thread; + sig_proc = (proc_t)get_bsdtask_info(sig_task); + } else { + sig_task = p->task; + sig_proc = p; + sig_thread = (struct thread *)0; + } + if (((sig_task == TASK_NULL) || is_kerneltask(sig_task))) { return; } - KNOTE(&p->p_klist, NOTE_SIGNAL | signum); - /* * do not send signals to the process that has the thread * doing a reboot(). Not doing so will mark that thread aborted * and can cause IO failures wich will cause data loss. */ - if (ISSET(p->p_flag, P_REBOOT)) { - if (sw_funnel) - thread_funnel_set(kernel_flock, funnel_state); + if (ISSET(sig_proc->p_flag, P_REBOOT)) { return; } - if (withlock) - signal_lock(p); + 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 @@ -1556,82 +1590,107 @@ psignal_lock(p, signum, withlock) * reference is released by thread_deallocate. */ - if (((p->p_flag & P_TRACED) == 0) && (p->p_sigignore & mask)) - goto psigout; - /* If successful return with ast set */ - sig_thread_act = get_signalthread(p, signum); + 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; + } - if (sig_thread_act == THREAD_NULL) { - /* XXXX FIXME - * if it is sigkill, may be we should - * inject a thread to terminate - */ + if (flavor & PSIG_VFORK) { + action = SIG_DFL; + act_set_astbsd(sig_thread); + kret = KERN_SUCCESS; + } else if (flavor & PSIG_THREAD) { + /* If successful return with ast set */ + kret = check_actforsig(sig_task, sig_thread, 1); + } else { + /* 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; } - uth = get_bsdthread_info(sig_thread_act); + + uth = get_bsdthread_info(sig_thread); /* * If proc is traced, always give parent a chance. */ - if (p->p_flag & P_TRACED) - action = SIG_DFL; - else { - /* - * If the signal is being ignored, - * then we forget about it immediately. - * (Note: we don't set SIGCONT in p_sigignore, - * and if it is set to SIG_IGN, - * action will be SIG_DFL here.) - */ - if (p->p_sigignore & mask) - goto psigout; - /* sigwait takes precedence */ - if (uth->uu_sigwait & mask) - action = KERN_SIG_WAIT; - else if (uth->uu_sigmask & mask) - action = KERN_SIG_HOLD; - else if (p->p_sigcatch & mask) - action = KERN_SIG_CATCH; - else + + if ((flavor & PSIG_VFORK) == 0) { + if (sig_proc->p_lflag & P_LTRACED) action = SIG_DFL; + else { + /* + * If the signal is being ignored, + * then we forget about it immediately. + * (Note: we don't set SIGCONT in p_sigignore, + * and if it is set to SIG_IGN, + * action will be SIG_DFL here.) + */ + if (sig_proc->p_sigignore & mask) + goto psigout; + if (uth->uu_sigwait & mask) + action = KERN_SIG_WAIT; + else if (uth->uu_sigmask & mask) + action = KERN_SIG_HOLD; + else if (sig_proc->p_sigcatch & mask) + action = KERN_SIG_CATCH; + else + action = SIG_DFL; + } } - if (p->p_nice > NZERO && action == SIG_DFL && (prop & SA_KILL) && - (p->p_flag & P_TRACED) == 0) - p->p_nice = NZERO; - if (prop & SA_CONT) { + proc_lock(sig_proc); + + if (sig_proc->p_nice > NZERO && action == SIG_DFL && (prop & SA_KILL) && + (sig_proc->p_lflag & P_LTRACED) == 0) + sig_proc->p_nice = NZERO; + + if (prop & SA_CONT) uth->uu_siglist &= ~stopsigmask; - p->p_siglist &= ~stopsigmask; - } if (prop & SA_STOP) { + struct pgrp *pg; /* * If sending a tty stop signal to a member of an orphaned * process group, discard the signal here if the action * is default; don't stop the process below if sleeping, * and don't clear any pending SIGCONT. */ - if (prop & SA_TTYSTOP && p->p_pgrp->pg_jobc == 0 && - action == SIG_DFL) + 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; + } + pg_rele(pg); + proc_lock(sig_proc); uth->uu_siglist &= ~contsigmask; - p->p_siglist &= ~contsigmask; } + uth->uu_siglist |= mask; - p->p_siglist |= mask; /* just for lame ones looking here */ + /* + * 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. */ - if (action == KERN_SIG_HOLD && ((prop & SA_CONT) == 0 || p->p_stat != SSTOP)) { + /* 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; } /* @@ -1640,8 +1699,8 @@ psignal_lock(p, signum, withlock) * below if we didn't care about priority for tracing * as SIGKILL's action is always SIG_DFL. */ - if ((signum == SIGKILL) && (p->p_nice > NZERO)) { - p->p_nice = NZERO; + if ((signum == SIGKILL) && (sig_proc->p_nice > NZERO)) { + sig_proc->p_nice = NZERO; } /* @@ -1649,23 +1708,31 @@ psignal_lock(p, signum, withlock) * stopped) so that it can discover the signal in * issig() and stop for the parent. */ - if (p->p_flag & P_TRACED) { - if (p->p_stat != SSTOP) - goto run; - else + if (sig_proc->p_lflag & P_LTRACED) { + if (sig_proc->p_stat != SSTOP) + goto runlocked; + else { + proc_unlock(sig_proc); goto psigout; + } } + if ((flavor & PSIG_VFORK) != 0) + goto runlocked; if (action == KERN_SIG_WAIT) { uth->uu_sigwait = mask; uth->uu_siglist &= ~mask; - p->p_siglist &= ~mask; wakeup(&uth->uu_sigwait); /* if it is SIGCONT resume whole process */ if (prop & SA_CONT) { - p->p_flag |= P_CONTINUED; + OSBitOrAtomic(P_CONTINUED, (UInt32 *)&sig_proc->p_flag); + sig_proc->p_contproc = current_proc()->p_pid; + + proc_unlock(sig_proc); (void) task_resume(sig_task); + goto psigout; } + proc_unlock(sig_proc); goto psigout; } @@ -1676,307 +1743,39 @@ psignal_lock(p, signum, withlock) * (except for SIGCONT). */ if (prop & SA_CONT) { - if (p->p_flag & P_TTYSLEEP) { - p->p_flag &= ~P_TTYSLEEP; - wakeup(&p->p_siglist); - } else { - p->p_flag |= P_CONTINUED; - (void) task_resume(sig_task); - } - p->p_stat = SRUN; - } else if (p->p_stat == SSTOP) - goto psigout; - goto run; - } else { - /* Default action - varies */ - if (mask & stopsigmask) { - /* - * These are the signals which by default - * stop a process. - * - * Don't clog system with children of init - * stopped from the keyboard. - */ - if (!(prop & SA_STOP) && p->p_pptr == initproc) { - psignal_lock(p, SIGKILL, 0); - uth->uu_siglist &= ~mask; - p->p_siglist &= ~mask; - goto psigout; - } - - /* - * Stop the task - * if task hasn't already been stopped by - * a signal. - */ - uth->uu_siglist &= ~mask; - p->p_siglist &= ~mask; - if (p->p_stat != SSTOP) { - p->p_xstat = signum; - stop(p); - if ((p->p_pptr->p_flag & P_NOCLDSTOP) == 0) { - struct proc *pp = p->p_pptr; - - pp->si_pid = p->p_pid; - pp->si_status = p->p_xstat; - pp->si_code = CLD_STOPPED; - pp->si_uid = p->p_ucred->cr_ruid; - psignal(pp, SIGCHLD); - } - } - goto psigout; - } - - switch (signum) { - /* - * Signals ignored by default have been dealt - * with already, since their bits are on in - * p_sigignore. - */ - - case SIGKILL: - /* - * Kill signal always sets process running and - * unsuspends it. - */ - /* - * Process will be running after 'run' - */ - p->p_stat = SRUN; - - thread_abort(sig_thread_act); - - goto psigout; - - case SIGCONT: - /* - * Let the process run. If it's sleeping on an - * event, it remains so. - */ - if (p->p_flag & P_TTYSLEEP) { - p->p_flag &= ~P_TTYSLEEP; - wakeup(&p->p_siglist); - } else { - p->p_flag |= P_CONTINUED; - (void) task_resume(sig_task); - } - uth->uu_siglist &= ~mask; - p->p_siglist &= ~mask; - p->p_stat = SRUN; - + OSBitOrAtomic(P_CONTINUED, (UInt32 *)&sig_proc->p_flag); + proc_unlock(sig_proc); + (void) task_resume(sig_task); + proc_lock(sig_proc); + sig_proc->p_stat = SRUN; + } else if (sig_proc->p_stat == SSTOP) { + proc_unlock(sig_proc); goto psigout; - - default: - /* - * All other signals wake up the process, but don't - * resume it. - */ - if (p->p_stat == SSTOP) - goto psigout; - goto run; } - } - /*NOTREACHED*/ -run: - /* - * If we're being traced (possibly because someone attached us - * while we were stopped), check for a signal from the debugger. - */ - if (p->p_stat == SSTOP) { - if ((p->p_flag & P_TRACED) != 0 && p->p_xstat != 0) - uth->uu_siglist |= sigmask(p->p_xstat); - } else { - /* - * setrunnable(p) in BSD and - * Wake up the thread if it is interruptible. - */ - p->p_stat = SRUN; - thread_abort_safely(sig_thread_act); - } -psigout: - if (withlock) - signal_unlock(p); - if (sw_funnel) - thread_funnel_set(kernel_flock, funnel_state); -} - - -/* psignal_lock(p, signum, withlock ) */ -void -psignal_uthread(thr_act, signum) - thread_t thr_act; - int signum; -{ - struct proc *p; - register int prop; - register sig_t action; - thread_t sig_thread_act; - register task_t sig_task; - int mask; - struct uthread *uth; - kern_return_t kret; - int error = 0; - - p = (struct proc *)get_bsdtask_info(get_threadtask(thr_act)); - if ((u_int)signum >= NSIG || signum == 0) - panic("Invalid signal number in psignal_uthread"); - mask = sigmask(signum); - prop = sigprop[signum]; - -#if SIGNAL_DEBUG - if(rdebug_proc && (p == rdebug_proc)) { - ram_printf(3); - } -#endif /* SIGNAL_DEBUG */ - - /* - * We will need the task pointer later. Grab it now to - * check for a zombie process. Also don't send signals - * to kernel internal tasks. - */ - if (((sig_task = p->task) == TASK_NULL) || is_kerneltask(sig_task)) { - return; - } - - sig_thread_act = thr_act; - /* - * do not send signals to the process that has the thread - * doing a reboot(). Not doing so will mark that thread aborted - * and can cause IO failures wich will cause data loss. - */ - if (ISSET(p->p_flag, P_REBOOT)) { - return; - } - - signal_lock(p); - - /* - * 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. - */ - - if (((p->p_flag & P_TRACED) == 0) && (p->p_sigignore & mask)) - goto puthout; - - kret = check_actforsig(sig_task, sig_thread_act, 1); - - if (kret != KERN_SUCCESS) { - error = EINVAL; - goto puthout; - } - - - uth = get_bsdthread_info(sig_thread_act); - - /* - * If proc is traced, always give parent a chance. - */ - if (p->p_flag & P_TRACED) - action = SIG_DFL; - else { /* - * If the signal is being ignored, - * then we forget about it immediately. - * (Note: we don't set SIGCONT in p_sigignore, - * and if it is set to SIG_IGN, - * action will be SIG_DFL here.) - */ - if (p->p_sigignore & mask) - goto puthout; - /* sigwait takes precedence */ - if (uth->uu_sigwait & mask) - action = KERN_SIG_WAIT; - else if (uth->uu_sigmask & mask) - action = KERN_SIG_HOLD; - else if (p->p_sigcatch & mask) - action = KERN_SIG_CATCH; - else - action = SIG_DFL; - } - - if (p->p_nice > NZERO && action == SIG_DFL && (prop & SA_KILL) && - (p->p_flag & P_TRACED) == 0) - p->p_nice = NZERO; - - if (prop & SA_CONT) { - uth->uu_siglist &= ~stopsigmask; - p->p_siglist &= ~stopsigmask; - } - - if (prop & SA_STOP) { - /* - * If sending a tty stop signal to a member of an orphaned - * process group, discard the signal here if the action - * is default; don't stop the process below if sleeping, - * and don't clear any pending SIGCONT. + * Fill out siginfo structure information to pass to the + * signalled process/thread sigaction handler, when it + * wakes up. si_code is 0 because this is an ordinary + * signal, not a SIGCHLD, and so si_status is the signal + * number itself, instead of the child process exit status. + * We shift this left because it will be shifted right before + * it is passed to user space. kind of ugly to use W_EXITCODE + * this way, but it beats defining a new macro. + * + * Note: Avoid the SIGCHLD recursion case! */ - if (prop & SA_TTYSTOP && p->p_pgrp->pg_jobc == 0 && - action == SIG_DFL) - goto puthout; - uth->uu_siglist &= ~contsigmask; - p->p_siglist &= ~contsigmask; - } - uth->uu_siglist |= mask; - p->p_siglist |= mask; /* just for lame ones looking here */ - - /* - * Defer further processing for signals which are held, - * except that stopped processes must be continued by SIGCONT. - */ - if (action == KERN_SIG_HOLD && ((prop & SA_CONT) == 0 || p->p_stat != SSTOP)) - goto puthout; - - /* - * 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. - */ - if ((signum == SIGKILL) && (p->p_nice > NZERO)) { - p->p_nice = NZERO; - } - - /* - * Process is traced - wake it up (if not already - * stopped) so that it can discover the signal in - * issig() and stop for the parent. - */ - if (p->p_flag & P_TRACED) { - if (p->p_stat != SSTOP) - goto psurun; - else - goto puthout; - } - - if (action == KERN_SIG_WAIT) { - uth->uu_sigwait = mask; - uth->uu_siglist &= ~mask; - p->p_siglist &= ~mask; - wakeup(&uth->uu_sigwait); - /* if it is SIGCONT resume whole process */ - if (prop & SA_CONT) { - p->p_flag |= P_CONTINUED; - (void) task_resume(sig_task); + 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); + sig_proc->si_uid = r_uid; + sig_proc->si_code = 0; } - goto puthout; - } - if (action != SIG_DFL) { - /* - * User wants to catch the signal. - * Wake up the thread, but don't un-suspend it - * (except for SIGCONT). - */ - if (prop & SA_CONT) { - p->p_flag |= P_CONTINUED; - (void) task_resume(sig_task); - } - goto psurun; + goto runlocked; } else { /* Default action - varies */ if (mask & stopsigmask) { @@ -1987,11 +1786,13 @@ psignal_uthread(thr_act, signum) * Don't clog system with children of init * stopped from the keyboard. */ - if (!(prop & SA_STOP) && p->p_pptr == initproc) { - psignal_lock(p, SIGKILL, 0); + 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; - p->p_siglist &= ~mask; - goto puthout; + proc_unlock(sig_proc); + goto psigout; } /* @@ -2000,23 +1801,51 @@ psignal_uthread(thr_act, signum) * a signal. */ uth->uu_siglist &= ~mask; - p->p_siglist &= ~mask; - if (p->p_stat != SSTOP) { - p->p_xstat = signum; - if ((p->p_pptr->p_flag & P_NOCLDSTOP) == 0) { - struct proc *pp = p->p_pptr; - - pp->si_pid = p->p_pid; - pp->si_status = p->p_xstat; + if (sig_proc->p_stat != SSTOP) { + sig_proc->p_xstat = signum; + sig_proc->p_stat = SSTOP; + OSBitAndAtomic(~((uint32_t)P_CONTINUED), (UInt32 *)&sig_proc->p_flag); + sig_proc->p_lflag &= ~P_LWAITED; + proc_unlock(sig_proc); + + pp = proc_parentholdref(sig_proc); + stop(sig_proc, pp); + if (( pp != PROC_NULL) && ((pp->p_flag & P_NOCLDSTOP) == 0)) { + + my_cred = kauth_cred_proc_ref(sig_proc); + r_uid = my_cred->cr_ruid; + kauth_cred_unref(&my_cred); + + proc_lock(sig_proc); + pp->si_pid = sig_proc->p_pid; + /* + * POSIX: sigaction for a stopped child + * when sent to the parent must set the + * child's signal number into si_status. + */ + if (signum != SIGSTOP) + pp->si_status = WEXITSTATUS(sig_proc->p_xstat); + else + pp->si_status = W_EXITCODE(signum, signum); pp->si_code = CLD_STOPPED; - pp->si_uid = p->p_ucred->cr_ruid; + pp->si_uid = r_uid; + proc_unlock(sig_proc); + psignal(pp, SIGCHLD); } - stop(p); - } - goto puthout; + if (pp != PROC_NULL) + proc_parentdropref(pp, 0); + } else + proc_unlock(sig_proc); + goto psigout; } + 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 @@ -2032,103 +1861,123 @@ psignal_uthread(thr_act, signum) /* * Process will be running after 'run' */ - p->p_stat = SRUN; - - thread_abort(sig_thread_act); + sig_proc->p_stat = SRUN; + proc_unlock(sig_proc); + thread_abort(sig_thread); - goto puthout; + goto psigout; case SIGCONT: /* * Let the process run. If it's sleeping on an * event, it remains so. */ - if (p->p_flag & P_TTYSLEEP) { - p->p_flag &= ~P_TTYSLEEP; - wakeup(&p->p_siglist); - } else { - p->p_flag |= P_CONTINUED; - (void) task_resume(sig_task); + OSBitOrAtomic(P_CONTINUED, (UInt32 *)&sig_proc->p_flag); + sig_proc->p_contproc = sig_proc->p_pid; + + proc_unlock(sig_proc); + (void) task_resume(sig_task); + proc_lock(sig_proc); + /* + * When processing a SIGCONT, we need to check + * to see if there are signals pending that + * were not delivered because we had been + * previously stopped. If that's the case, + * we need to thread_abort_safely() to trigger + * interruption of the current system call to + * cause their handlers to fire. If it's only + * the SIGCONT, then don't wake up. + */ + if (((flavor & (PSIG_VFORK|PSIG_THREAD)) == 0) && (((uth->uu_siglist & ~uth->uu_sigmask) & ~sig_proc->p_sigignore) & ~mask)) { + uth->uu_siglist &= ~mask; + sig_proc->p_stat = SRUN; + goto runlocked; } + uth->uu_siglist &= ~mask; - p->p_siglist &= ~mask; - p->p_stat = SRUN; - goto puthout; + sig_proc->p_stat = SRUN; + proc_unlock(sig_proc); + goto psigout; default: + /* + * A signal which has a default action of killing + * the process, and for which there is no handler, + * needs to act like SIGKILL + */ + 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; + } + /* * All other signals wake up the process, but don't * resume it. */ - goto psurun; + if (sig_proc->p_stat == SSTOP) { + proc_unlock(sig_proc); + goto psigout; + } + goto runlocked; } } /*NOTREACHED*/ -psurun: + +runlocked: /* * If we're being traced (possibly because someone attached us * while we were stopped), check for a signal from the debugger. */ - if (p->p_stat == SSTOP) { - if ((p->p_flag & P_TRACED) != 0 && p->p_xstat != 0) { - uth->uu_siglist |= sigmask(p->p_xstat); - p->p_siglist |= sigmask(p->p_xstat); + 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); + 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. */ - p->p_stat = SRUN; - thread_abort_safely(sig_thread_act); + sig_proc->p_stat = SRUN; + proc_unlock(sig_proc); + if ((flavor & PSIG_VFORK) == 0) + thread_abort_safely(sig_thread); + } +psigout: + if ((flavor & PSIG_LOCKED)== 0) { + proc_signalend(sig_proc, 0); } - -puthout: - signal_unlock(p); } - -__inline__ void -sig_lock_to_exit(struct proc *p) +void +psignal(proc_t p, int signum) { - thread_t self = current_thread(); + psignal_internal(p, NULL, NULL, 0, signum); +} - p->exit_thread = self; - (void) task_suspend(p->task); +void +psignal_locked(proc_t p, int signum) +{ + psignal_internal(p, NULL, NULL, PSIG_LOCKED, signum); } -__inline__ int -sig_try_locked(struct proc *p) +void +psignal_vfork(proc_t p, task_t new_task, thread_t thread, int signum) { - thread_t self = current_thread(); + psignal_internal(p, new_task, thread, PSIG_VFORK, signum); +} - while (p->sigwait || p->exit_thread) { - if (p->exit_thread) { - if (p->exit_thread != self) { - /* - * Already exiting - no signals. - */ - thread_abort(self); - } - return(0); - } - if(assert_wait_possible()) { - assert_wait((caddr_t)&p->sigwait_thread, - (THREAD_INTERRUPTIBLE)); - } - signal_unlock(p); - thread_block(THREAD_CONTINUE_NULL); - signal_lock(p); - if (thread_should_abort(self)) { - /* - * Terminate request - clean up. - */ - return -1; - } - } - return 1; +static void +psignal_uthread(thread_t thread, int signum) +{ + psignal_internal(PROC_NULL, TASK_NULL, thread, PSIG_THREAD, signum); } + /* * If the current process has received a signal (should be caught or cause * termination, should interrupt current syscall), return the signal number. @@ -2142,13 +1991,15 @@ sig_try_locked(struct proc *p) * postsig(signum); */ int -issignal(p) - register struct proc *p; +issignal(proc_t p) { - register int signum, mask, prop, sigbits; + int signum, mask, prop, sigbits; thread_t cur_act; struct uthread * ut; - struct proc *pp; + proc_t pp; + kauth_cred_t my_cred; + int retval = 0; + uid_t r_uid; cur_act = current_thread(); @@ -2157,65 +2008,80 @@ issignal(p) ram_printf(3); } #endif /* SIGNAL_DEBUG */ - signal_lock(p); + proc_lock(p); /* * Try to grab the signal lock. */ if (sig_try_locked(p) <= 0) { - signal_unlock(p); - return (0); + proc_unlock(p); + return(0); } + proc_signalstart(p, 1); + ut = get_bsdthread_info(cur_act); for(;;) { sigbits = ut->uu_siglist & ~ut->uu_sigmask; - if (p->p_flag & P_PPWAIT) + if (p->p_lflag & P_LPPWAIT) sigbits &= ~stopsigmask; if (sigbits == 0) { /* no signal to send */ - signal_unlock(p); - return (0); + retval = 0; + goto out; } + signum = ffs((long)sigbits); mask = sigmask(signum); prop = sigprop[signum]; /* * We should see pending but ignored signals - * only if P_TRACED was on when they were posted. + * only if P_LTRACED was on when they were posted. */ - if (mask & p->p_sigignore && (p->p_flag & P_TRACED) == 0) { + if (mask & p->p_sigignore && (p->p_lflag & P_LTRACED) == 0) { ut->uu_siglist &= ~mask; /* take the signal! */ - p->p_siglist &= ~mask; /* take the signal! */ continue; } - if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) { - register task_t task; + if (p->p_lflag & P_LTRACED && (p->p_lflag & P_LPPWAIT) == 0) { + task_t task; /* * If traced, always stop, and stay * stopped until released by the debugger. */ /* ptrace debugging */ p->p_xstat = signum; - pp = p->p_pptr; - if (p->p_flag & P_SIGEXC) { + + if (p->p_lflag & P_LSIGEXC) { p->sigwait = TRUE; p->sigwait_thread = cur_act; p->p_stat = SSTOP; - p->p_flag &= ~(P_WAITED|P_CONTINUED); + OSBitAndAtomic(~((uint32_t)P_CONTINUED), (UInt32 *)&p->p_flag); + p->p_lflag &= ~P_LWAITED; ut->uu_siglist &= ~mask; /* clear the old signal */ - p->p_siglist &= ~mask; /* clear the old signal */ - signal_unlock(p); + proc_signalend(p, 1); + proc_unlock(p); do_bsdexception(EXC_SOFTWARE, EXC_SOFT_SIGNAL, signum); - signal_lock(p); + proc_lock(p); + proc_signalstart(p, 1); } else { -// panic("Unsupportef gdb option \n");; - pp->si_pid = p->p_pid; - pp->si_status = p->p_xstat; - pp->si_code = CLD_TRAPPED; - pp->si_uid = p->p_ucred->cr_ruid; - psignal(pp, SIGCHLD); + proc_unlock(p); + my_cred = kauth_cred_proc_ref(p); + r_uid = my_cred->cr_ruid; + kauth_cred_unref(&my_cred); + + pp = proc_parentholdref(p); + if (pp != PROC_NULL) { + proc_lock(pp); + + pp->si_pid = p->p_pid; + pp->si_status = p->p_xstat; + pp->si_code = CLD_TRAPPED; + pp->si_uid = r_uid; + + proc_unlock(pp); + } + /* * XXX Have to really stop for debuggers; * XXX stop() doesn't do the right thing. @@ -2224,19 +2090,31 @@ issignal(p) * XXX middle of it. */ task = p->task; - task_hold(task); + task_suspend(task); + + proc_lock(p); p->sigwait = TRUE; p->sigwait_thread = cur_act; p->p_stat = SSTOP; - p->p_flag &= ~(P_WAITED|P_CONTINUED); + OSBitAndAtomic(~((uint32_t)P_CONTINUED), (UInt32 *)&p->p_flag); + p->p_lflag &= ~P_LWAITED; ut->uu_siglist &= ~mask; /* clear the old signal */ - p->p_siglist &= ~mask; /* clear the old signal */ - wakeup((caddr_t)p->p_pptr); - signal_unlock(p); + proc_signalend(p, 1); + proc_unlock(p); + + if (pp != PROC_NULL) { + psignal(pp, SIGCHLD); + proc_list_lock(); + wakeup((caddr_t)pp); + proc_parentdropref(pp, 1); + proc_list_unlock(); + } + assert_wait((caddr_t)&p->sigwait, (THREAD_INTERRUPTIBLE)); thread_block(THREAD_CONTINUE_NULL); - signal_lock(p); + proc_lock(p); + proc_signalstart(p, 1); } p->sigwait = FALSE; @@ -2264,8 +2142,9 @@ issignal(p) * before calling exit(). (Since exit() * calls closef() which can trash u_qsave.) */ - signal_unlock(p); - exit1(p,signum, (int *)NULL); + proc_signalend(p, 1); + proc_unlock(p); + exit1(p, W_EXITCODE(0, SIGKILL), (int *)NULL); return(0); } @@ -2273,8 +2152,8 @@ issignal(p) * We may have to quit */ if (thread_should_abort(current_thread())) { - signal_unlock(p); - return(0); + retval = 0; + goto out; } /* * If parent wants us to take the signal, @@ -2290,7 +2169,6 @@ issignal(p) */ mask = sigmask(signum); ut->uu_siglist |= mask; - p->p_siglist |= mask; /* just for lame ones looking here */ if (ut->uu_sigmask & mask) continue; } @@ -2307,7 +2185,7 @@ issignal(p) /* * Don't take default actions on system processes. */ - if (p->p_pptr->p_pid == 0) { + if (p->p_ppid == 0) { #if DIAGNOSTIC /* * Are you sure you want to ignore SIGSEGV @@ -2327,22 +2205,46 @@ issignal(p) * process group, ignore tty stop signals. */ if (prop & SA_STOP) { - if (p->p_flag & P_TRACED || - (p->p_pgrp->pg_jobc == 0 && - prop & SA_TTYSTOP)) + struct pgrp * pg; + + proc_unlock(p); + pg = proc_pgrp(p); + if (p->p_lflag & P_LTRACED || + (pg->pg_jobc == 0 && + prop & SA_TTYSTOP)) { + proc_lock(p); + pg_rele(pg); break; /* == ignore */ + } + pg_rele(pg); if (p->p_stat != SSTOP) { + proc_lock(p); p->p_xstat = signum; - stop(p); - if ((p->p_pptr->p_flag & P_NOCLDSTOP) == 0) { - pp = p->p_pptr; + + p->p_stat = SSTOP; + p->p_lflag &= ~P_LWAITED; + proc_unlock(p); + + pp = proc_parentholdref(p); + stop(p, pp); + if ((pp != PROC_NULL) && ((pp->p_flag & P_NOCLDSTOP) == 0)) { + my_cred = kauth_cred_proc_ref(p); + r_uid = my_cred->cr_ruid; + kauth_cred_unref(&my_cred); + + proc_lock(pp); pp->si_pid = p->p_pid; - pp->si_status = p->p_xstat; + pp->si_status = WEXITSTATUS(p->p_xstat); pp->si_code = CLD_STOPPED; - pp->si_uid = p->p_ucred->cr_ruid; + pp->si_uid = r_uid; + proc_unlock(pp); + psignal(pp, SIGCHLD); } + if (pp != PROC_NULL) + proc_parentdropref(pp, 0); } + proc_lock(p); break; } else if (prop & SA_IGNORE) { /* @@ -2352,11 +2254,12 @@ issignal(p) break; /* == ignore */ } else { ut->uu_siglist &= ~mask; /* take the signal! */ - p->p_siglist &= ~mask; /* take the signal! */ - signal_unlock(p); - return (signum); + retval = signum; + goto out; } + /*NOTREACHED*/ + break; case (long)SIG_IGN: /* @@ -2365,7 +2268,7 @@ issignal(p) * than SIGCONT, unless process is traced. */ if ((prop & SA_CONT) == 0 && - (p->p_flag & P_TRACED) == 0) + (p->p_lflag & P_LTRACED) == 0) printf("issignal\n"); break; /* == ignore */ @@ -2375,22 +2278,23 @@ issignal(p) * postsig() process it. */ ut->uu_siglist &= ~mask; /* take the signal! */ - p->p_siglist &= ~mask; /* take the signal! */ - signal_unlock(p); - return (signum); + retval = signum; + goto out; } ut->uu_siglist &= ~mask; /* take the signal! */ - p->p_siglist &= ~mask; /* take the signal! */ } /* NOTREACHED */ +out: + proc_signalend(p,1); + proc_unlock(p); + return(retval); } /* called from _sleep */ int -CURSIG(p) - register struct proc *p; +CURSIG(proc_t p) { - register int signum, mask, prop, sigbits; + int signum, mask, prop, sigbits; thread_t cur_act; struct uthread * ut; int retnum = 0; @@ -2403,13 +2307,13 @@ CURSIG(p) if (ut->uu_siglist == 0) return (0); - if (((ut->uu_siglist & ~ut->uu_sigmask) == 0) && ((p->p_flag & P_TRACED) == 0)) + if (((ut->uu_siglist & ~ut->uu_sigmask) == 0) && ((p->p_lflag & P_LTRACED) == 0)) return (0); sigbits = ut->uu_siglist & ~ut->uu_sigmask; for(;;) { - if (p->p_flag & P_PPWAIT) + if (p->p_lflag & P_LPPWAIT) sigbits &= ~stopsigmask; if (sigbits == 0) { /* no signal to send */ return (retnum); @@ -2421,12 +2325,12 @@ CURSIG(p) /* * We should see pending but ignored signals - * only if P_TRACED was on when they were posted. + * only if P_LTRACED was on when they were posted. */ - if (mask & p->p_sigignore && (p->p_flag & P_TRACED) == 0) { + if (mask & p->p_sigignore && (p->p_lflag & P_LTRACED) == 0) { continue; } - if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) { + if (p->p_lflag & P_LTRACED && (p->p_lflag & P_LPPWAIT) == 0) { /* * Put the new signal into p_siglist. If the * signal is being masked, look for other signals. @@ -2449,7 +2353,7 @@ CURSIG(p) /* * Don't take default actions on system processes. */ - if (p->p_pptr->p_pid == 0) { + if (p->p_ppid == 0) { #if DIAGNOSTIC /* * Are you sure you want to ignore SIGSEGV @@ -2469,10 +2373,17 @@ CURSIG(p) * process group, ignore tty stop signals. */ if (prop & SA_STOP) { - if (p->p_flag & P_TRACED || - (p->p_pgrp->pg_jobc == 0 && - prop & SA_TTYSTOP)) + struct pgrp *pg; + + pg = proc_pgrp(p); + + if (p->p_lflag & P_LTRACED || + (pg->pg_jobc == 0 && + prop & SA_TTYSTOP)) { + pg_rele(pg); break; /* == ignore */ + } + pg_rele(pg); retnum = signum; break; } else if (prop & SA_IGNORE) { @@ -2493,7 +2404,7 @@ CURSIG(p) * than SIGCONT, unless process is traced. */ if ((prop & SA_CONT) == 0 && - (p->p_flag & P_TRACED) == 0) + (p->p_lflag & P_LTRACED) == 0) printf("issignal\n"); break; /* == ignore */ @@ -2514,14 +2425,15 @@ CURSIG(p) * via wakeup. Signals are handled elsewhere. The process must not be * on the run queue. */ -void -stop(p) - register struct proc *p; +static void +stop(proc_t p, proc_t parent) { - p->p_stat = SSTOP; - p->p_flag &= ~(P_WAITED|P_CONTINUED); - if (p->p_pptr->p_stat != SSTOP) - wakeup((caddr_t)p->p_pptr); + OSBitAndAtomic(~((uint32_t)P_CONTINUED), (UInt32 *)&p->p_flag); + if ((parent != PROC_NULL) && (parent->p_stat != SSTOP)) { + proc_list_lock(); + wakeup((caddr_t)parent); + proc_list_unlock(); + } (void) task_suspend(p->task); /*XXX*/ } @@ -2532,7 +2444,7 @@ stop(p) void postsig(int signum) { - struct proc *p = current_proc(); + proc_t p = current_proc(); struct sigacts *ps = p->p_sigacts; user_addr_t catcher; u_long code; @@ -2549,36 +2461,52 @@ postsig(int signum) panic("psig not on master"); #endif - signal_lock(p); + proc_lock(p); /* * Try to grab the signal lock. */ if (sig_try_locked(p) <= 0) { - signal_unlock(p); + proc_unlock(p); return; } + proc_signalstart(p, 1); + ut = (struct uthread *)get_bsdthread_info(current_thread()); mask = sigmask(signum); ut->uu_siglist &= ~mask; - p->p_siglist &= ~mask; catcher = ps->ps_sigact[signum]; -#if KTRACE - //LP64: catcher argument is a 64 bit user space handler address - if (KTRPOINT(p, KTR_PSIG)) - ktrpsig(p->p_tracep, - signum, CAST_DOWN(void *,catcher), ut->uu_flag & UT_SAS_OLDMASK ? - &ut->uu_oldmask : &ut->uu_sigmask, 0); -#endif if (catcher == SIG_DFL) { /* * Default catcher, where the default is to kill * the process. (Other cases were ignored above.) */ - /* called with signal_lock() held */ - sigexit_locked(p, signum); + siginfo_t sinfo; + bzero((caddr_t)&sinfo, sizeof(siginfo_t)); + + sig_lock_to_exit(p); + p->p_acflag |= AXSIG; + if (sigprop[signum] & SA_CORE) { + p->p_sigacts->ps_sig = signum; + proc_signalend(p, 1); + proc_unlock(p); + if (coredump(p) == 0) + signum |= WCOREFLAG; + } else { + proc_signalend(p, 1); + proc_unlock(p); + } + + sinfo.si_signo = signum; + sinfo.si_pid = p->si_pid; + sinfo.si_uid = p->si_uid; + sinfo.si_status = WEXITSTATUS(p->si_status); + + DTRACE_PROC3(signal__handle, int, signum, siginfo_t *, &sinfo, + void (*)(void), SIG_DFL); + + exit1(p, W_EXITCODE(0, signum), (int *)NULL); return; - /* NOTREACHED */ } else { /* * If we get here, the signal must be caught. @@ -2588,6 +2516,7 @@ postsig(int signum) log(LOG_WARNING, "postsig: processing masked or ignored signal\n"); #endif + /* * Set the new mask value and also defer further * occurences of this signal. @@ -2626,75 +2555,62 @@ postsig(int signum) code = ps->ps_code; ps->ps_code = 0; } - p->p_stats->p_ru.ru_nsignals++; + OSIncrementAtomic(&p->p_stats->p_ru.ru_nsignals); sendsig(p, catcher, signum, returnmask, code); } - signal_unlock(p); + proc_signalend(p, 1); + proc_unlock(p); } /* - * Force the current process to exit with the specified signal, dumping core - * if appropriate. We bypass the normal tests for masked and caught signals, - * allowing unrecoverable failures to terminate the process without changing - * signal state. Mark the accounting record with the signal termination. - * If dumping core, save the signal number for the debugger. Calls exit and - * does not return. + * Attach a signal knote to the list of knotes for this process. + * + * Signal knotes share the knote list with proc knotes. This + * could be avoided by using a signal-specific knote list, but + * probably isn't worth the trouble. */ - /* called with signal lock */ -void -sigexit_locked(p, signum) - register struct proc *p; - int signum; -{ - - sig_lock_to_exit(p); - p->p_acflag |= AXSIG; - if (sigprop[signum] & SA_CORE) { - p->p_sigacts->ps_sig = signum; - signal_unlock(p); - if (coredump(p) == 0) - signum |= WCOREFLAG; - } else - signal_unlock(p); - - exit1(p, W_EXITCODE(0, signum), (int *)NULL); - /* NOTREACHED */ -} - static int filt_sigattach(struct knote *kn) { - struct proc *p = current_proc(); - boolean_t funnel_state; + proc_t p = current_proc(); /* can attach only to oneself */ + + proc_klist_lock(); kn->kn_ptr.p_proc = p; kn->kn_flags |= EV_CLEAR; /* automatically set */ - /* Take the funnel to protect the proc while adding to the list */ - funnel_state = thread_funnel_set(kernel_flock, TRUE); KNOTE_ATTACH(&p->p_klist, kn); - thread_funnel_set(kernel_flock, funnel_state); + + proc_klist_unlock(); return (0); } +/* + * remove the knote from the process list, if it hasn't already + * been removed by exit processing. + */ + static void filt_sigdetach(struct knote *kn) { - struct proc *p = kn->kn_ptr.p_proc; - boolean_t funnel_state; + proc_t p = kn->kn_ptr.p_proc; - funnel_state = thread_funnel_set(kernel_flock, TRUE); + proc_klist_lock(); + kn->kn_ptr.p_proc = NULL; KNOTE_DETACH(&p->p_klist, kn); - thread_funnel_set(kernel_flock, funnel_state); + proc_klist_unlock(); } /* - * signal knotes are shared with proc knotes, so we apply a mask to - * the hint in order to differentiate them from process hints. This - * could be avoided by using a signal-specific knote list, but probably - * isn't worth the trouble. + * Post an event to the signal filter. Because we share the same list + * as process knotes, we have to filter out and handle only signal events. + * + * We assume that we process fdfree() before we post the NOTE_EXIT for + * a process during exit. Therefore, since signal filters can only be + * set up "in-process", we should have already torn down the kqueue + * hosting the EVFILT_SIGNAL knote and should never see NOTE_EXIT. */ static int filt_signal(struct knote *kn, long hint) @@ -2705,109 +2621,122 @@ filt_signal(struct knote *kn, long hint) if (kn->kn_id == (unsigned int)hint) kn->kn_data++; + } else if (hint & NOTE_EXIT) { + panic("filt_signal: detected NOTE_EXIT event"); } + return (kn->kn_data != 0); } - void -bsd_ast(thread_t thr_act) +bsd_ast(thread_t thread) { - struct proc *p = current_proc(); - struct uthread *ut = get_bsdthread_info(thr_act); + proc_t p = current_proc(); + struct uthread *ut = get_bsdthread_info(thread); int signum; user_addr_t pc; - boolean_t funnel_state; static int bsd_init_done = 0; if (p == NULL) return; - funnel_state = thread_funnel_set(kernel_flock, TRUE); - if ((p->p_flag & P_OWEUPC) && (p->p_flag & P_PROFIL)) { pc = get_useraddr(); addupc_task(p, pc, 1); - p->p_flag &= ~P_OWEUPC; + OSBitAndAtomic(~((uint32_t)P_OWEUPC), (UInt32 *)&p->p_flag); } - if (CHECK_SIGNALS(p, current_thread(), ut)) { - while ( (signum = issignal(p)) ) - postsig(signum); + if (timerisset(&p->p_vtimer_user.it_value)) { + uint32_t microsecs; + + task_vtimer_update(p->task, TASK_VTIMER_USER, µsecs); + + if (!itimerdecr(p, &p->p_vtimer_user, microsecs)) { + if (timerisset(&p->p_vtimer_user.it_value)) + task_vtimer_set(p->task, TASK_VTIMER_USER); + else + task_vtimer_clear(p->task, TASK_VTIMER_USER); + + psignal(p, SIGVTALRM); } - if (!bsd_init_done) { - bsd_init_done = 1; - bsdinit_task(); } - (void) thread_funnel_set(kernel_flock, FALSE); -} + if (timerisset(&p->p_vtimer_prof.it_value)) { + uint32_t microsecs; -/* - * Follwing routines are called using callout from bsd_hardclock - * so that psignals are called in a thread context and are funneled - */ -void -psignal_vtalarm(struct proc *p) -{ - boolean_t funnel_state; + task_vtimer_update(p->task, TASK_VTIMER_PROF, µsecs); - if (p == NULL) - return; - funnel_state = thread_funnel_set(kernel_flock, TRUE); - psignal_lock(p, SIGVTALRM, 1); - (void) thread_funnel_set(kernel_flock, FALSE); + if (!itimerdecr(p, &p->p_vtimer_prof, microsecs)) { + if (timerisset(&p->p_vtimer_prof.it_value)) + task_vtimer_set(p->task, TASK_VTIMER_PROF); + else + task_vtimer_clear(p->task, TASK_VTIMER_PROF); + + psignal(p, SIGPROF); + } } -void -psignal_xcpu(struct proc *p) -{ - boolean_t funnel_state; + if (timerisset(&p->p_rlim_cpu)) { + struct timeval tv; - if (p == NULL) - return; - funnel_state = thread_funnel_set(kernel_flock, TRUE); - psignal_lock(p, SIGXCPU, 1); - (void) thread_funnel_set(kernel_flock, FALSE); -} + task_vtimer_update(p->task, TASK_VTIMER_RLIM, (uint32_t *) &tv.tv_usec); -void -psignal_sigprof(struct proc *p) -{ - boolean_t funnel_state; + proc_spinlock(p); + if (p->p_rlim_cpu.tv_sec > 0 || p->p_rlim_cpu.tv_usec > tv.tv_usec) { + tv.tv_sec = 0; + timersub(&p->p_rlim_cpu, &tv, &p->p_rlim_cpu); + proc_spinunlock(p); + } else { + + timerclear(&p->p_rlim_cpu); + proc_spinunlock(p); + + task_vtimer_clear(p->task, TASK_VTIMER_RLIM); + + psignal(p, SIGXCPU); + } + } + + if (CHECK_SIGNALS(p, current_thread(), ut)) { + while ( (signum = issignal(p)) ) + postsig(signum); + } + + if (!bsd_init_done) { + bsd_init_done = 1; + bsdinit_task(); + } - if (p == NULL) - return; - funnel_state = thread_funnel_set(kernel_flock, TRUE); - psignal_lock(p, SIGPROF, 1); - (void) thread_funnel_set(kernel_flock, FALSE); } -/* ptrace set runnalbe */ +/* ptrace set runnable */ void -pt_setrunnable(struct proc *p) +pt_setrunnable(proc_t p) { -task_t task; + task_t task; task = p->task; - if (p->p_flag & P_TRACED) { + if (p->p_lflag & P_LTRACED) { + proc_lock(p); p->p_stat = SRUN; + proc_unlock(p); if (p->sigwait) { wakeup((caddr_t)&(p->sigwait)); - task_release(task); + if ((p->p_lflag & P_LSIGEXC) == 0) { // 5878479 + task_release(task); + } } } } - kern_return_t do_bsdexception( int exc, int code, int sub) { - exception_data_type_t codes[EXCEPTION_CODE_MAX]; + mach_exception_data_type_t codes[EXCEPTION_CODE_MAX]; codes[0] = code; codes[1] = sub; @@ -2815,28 +2744,19 @@ do_bsdexception( } int -proc_pendingsignals(struct proc *p, sigset_t mask) +proc_pendingsignals(proc_t p, sigset_t mask) { struct uthread * uth; thread_t th; sigset_t bits = 0; - int error; + proc_lock(p); /* If the process is in proc exit return no signal info */ - if (p->p_lflag & P_LPEXIT) - return(0); - - /* duplicate the signal lock code to enable recursion; as exit - * holds the lock too long. All this code is being reworked - * this is just a workaround for regressions till new code - * arrives. - */ -ppend_retry: - error = lockmgr((struct lock__bsd__ *)&p->signal_lock[0], (LK_EXCLUSIVE | LK_CANRECURSE), 0, (struct proc *)0); - if (error == EINTR) - goto ppend_retry; + if (p->p_lflag & P_LPEXIT) { + goto out; + } - if ((p->p_flag & P_INVFORK) && p->p_vforkact) { + if ((p->p_lflag & P_LINVFORK) && p->p_vforkact) { th = p->p_vforkact; uth = (struct uthread *)get_bsdthread_info(th); if (uth) { @@ -2850,7 +2770,7 @@ ppend_retry: bits |= (((uth->uu_siglist & ~uth->uu_sigmask) & ~p->p_sigignore) & mask); } out: - signal_unlock(p); + proc_unlock(p); return(bits); } @@ -2860,11 +2780,140 @@ thread_issignal(proc_t p, thread_t th, sigset_t mask) struct uthread * uth; sigset_t bits=0; + proc_lock(p); + uth = (struct uthread *)get_bsdthread_info(th); + if (uth) { + bits = (((uth->uu_siglist & ~uth->uu_sigmask) & ~p->p_sigignore) & mask); + } + proc_unlock(p); + return(bits); +} - uth = (struct uthread *)get_bsdthread_info(th); - if (uth) { - bits = (((uth->uu_siglist & ~uth->uu_sigmask) & ~p->p_sigignore) & mask); - } - return(bits); +/* + * Allow external reads of the sigprop array. + */ +int +hassigprop(int sig, int prop) +{ + return (sigprop[sig] & prop); +} + +void +pgsigio(pid_t pgid, int sig) +{ + proc_t p = PROC_NULL; + + if (pgid < 0) + gsignal(-(pgid), sig); + + else if (pgid > 0 && (p = proc_find(pgid)) != 0) + psignal(p, sig); + if (p != PROC_NULL) + proc_rele(p); +} + + +void +proc_signalstart(proc_t p, int locked) +{ + if (locked == 0) + proc_lock(p); + while ((p->p_lflag & P_LINSIGNAL) == P_LINSIGNAL) { + p->p_lflag |= P_LSIGNALWAIT; + msleep(&p->p_sigmask, &p->p_mlock, 0, "proc_signstart", NULL); + } + p->p_lflag |= P_LINSIGNAL; +#if DIAGNOSTIC +#if SIGNAL_DEBUG +#ifdef __ppc__ + { + int sp, *fp, numsaved; + + __asm__ volatile("mr %0,r1" : "=r" (sp)); + + fp = (int *)*((int *)sp); + for (numsaved = 0; numsaved < 3; numsaved++) { + p->lockpc[numsaved] = fp[2]; + if ((int)fp <= 0) + break; + fp = (int *)*fp; + } + } +#endif /* __ppc__ */ +#endif /* SIGNAL_DEBUG */ +#endif /* DIAGNOSTIC */ + p->p_signalholder = current_thread(); + if (locked == 0) + proc_unlock(p); + +} + +void +proc_signalend(proc_t p, int locked) +{ + if (locked == 0) + proc_lock(p); + p->p_lflag &= ~P_LINSIGNAL; + +#if DIAGNOSTIC +#if SIGNAL_DEBUG +#ifdef __ppc__ + { + int sp, *fp, numsaved; + + __asm__ volatile("mr %0,r1" : "=r" (sp)); + + fp = (int *)*((int *)sp); + for (numsaved = 0; numsaved < 3; numsaved++) { + p->unlockpc[numsaved] = fp[2]; + if ((int)fp <= 0) + break; + fp = (int *)*fp; + } + } +#endif /* __ppc__ */ +#endif /* SIGNAL_DEBUG */ +#endif /* DIAGNOSTIC */ + + if ((p->p_lflag & P_LSIGNALWAIT) == P_LSIGNALWAIT) { + p->p_lflag &= ~P_LSIGNALWAIT; + wakeup(&p->p_sigmask); + } + p->p_signalholder = NULL; + if (locked == 0) + proc_unlock(p); +} + + +void +sig_lock_to_exit(proc_t p) +{ + thread_t self = current_thread(); + + p->exit_thread = self; + proc_unlock(p); + (void) task_suspend(p->task); + proc_lock(p); } +int +sig_try_locked(proc_t p) +{ + thread_t self = current_thread(); + + while (p->sigwait || p->exit_thread) { + if (p->exit_thread) { + return(0); + } + msleep((caddr_t)&p->sigwait_thread, &p->p_mlock, PCATCH | PDROP, 0, 0); + if (thread_should_abort(self)) { + /* + * Terminate request - clean up. + */ + proc_lock(p); + return -1; + } + proc_lock(p); + } + return 1; +}