X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/c331a0bec715536613c8dd5f34a4e115d5b15824..8a3053a07cee346dca737a5670e546fd26a7c9d6:/bsd/kern/kern_exit.c diff --git a/bsd/kern/kern_exit.c b/bsd/kern/kern_exit.c index 811b4fb7d..1ef04170d 100644 --- a/bsd/kern/kern_exit.c +++ b/bsd/kern/kern_exit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2007 Apple Inc. All rights reserved. + * Copyright (c) 2000-2011 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -119,6 +119,14 @@ #include #include +#if VM_PRESSURE_EVENTS +#include +#endif + +#if CONFIG_MEMORYSTATUS +#include +#endif + #if CONFIG_DTRACE /* Do not include dtrace.h, it redefines kmem_[alloc/free] */ extern void (*dtrace_fasttrap_exit_ptr)(proc_t); @@ -140,12 +148,12 @@ extern void dtrace_lazy_dofs_destroy(proc_t); #include extern char init_task_failure_data[]; -void proc_prepareexit(proc_t p, int rv); +void proc_prepareexit(proc_t p, int rv, boolean_t perf_notify); void vfork_exit(proc_t p, int rv); void vproc_exit(proc_t p); __private_extern__ void munge_user64_rusage(struct rusage *a_rusage_p, struct user64_rusage *a_user_rusage_p); __private_extern__ void munge_user32_rusage(struct rusage *a_rusage_p, struct user32_rusage *a_user_rusage_p); -static int reap_child_locked(proc_t parent, proc_t child, int deadparent, int locked, int droplock); +static int reap_child_locked(proc_t parent, proc_t child, int deadparent, int reparentedtoinit, int locked, int droplock); /* * Things which should have prototypes in headers, but don't @@ -156,16 +164,16 @@ int wait1continue(int result); int waitidcontinue(int result); int *get_bsduthreadrval(thread_t); kern_return_t sys_perf_notify(thread_t thread, int pid); -kern_return_t abnormal_exit_notify(mach_exception_data_type_t code, - mach_exception_data_type_t subcode); -void workqueue_exit(struct proc *); +kern_return_t task_exception_notify(exception_type_t exception, + mach_exception_data_type_t code, mach_exception_data_type_t subcode); void delay(int); - +void gather_rusage_info_v2(proc_t p, struct rusage_info_v2 *ru, int flavor); + /* * NOTE: Source and target may *NOT* overlap! * XXX Should share code with bsd/dev/ppc/unix_signal.c */ -static void +void siginfo_user_to_user32(user_siginfo_t *in, user32_siginfo_t *out) { out->si_signo = in->si_signo; @@ -180,7 +188,7 @@ siginfo_user_to_user32(user_siginfo_t *in, user32_siginfo_t *out) out->si_band = in->si_band; /* range reduction */ } -static void +void siginfo_user_to_user64(user_siginfo_t *in, user64_siginfo_t *out) { out->si_signo = in->si_signo; @@ -195,6 +203,24 @@ siginfo_user_to_user64(user_siginfo_t *in, user64_siginfo_t *out) out->si_band = in->si_band; /* range reduction */ } +static int +copyoutsiginfo(user_siginfo_t *native, boolean_t is64, user_addr_t uaddr) +{ + if (is64) { + user64_siginfo_t sinfo64; + + bzero(&sinfo64, sizeof (sinfo64)); + siginfo_user_to_user64(native, &sinfo64); + return (copyout(&sinfo64, uaddr, sizeof (sinfo64))); + } else { + user32_siginfo_t sinfo32; + + bzero(&sinfo32, sizeof (sinfo32)); + siginfo_user_to_user32(native, &sinfo32); + return (copyout(&sinfo32, uaddr, sizeof (sinfo32))); + } +} + /* * exit -- * Death of process. @@ -219,10 +245,18 @@ exit(proc_t p, struct exit_args *uap, int *retval) */ int exit1(proc_t p, int rv, int *retval) +{ + return exit1_internal(p, rv, retval, TRUE, TRUE, 0); +} + +int +exit1_internal(proc_t p, int rv, int *retval, boolean_t thread_can_terminate, boolean_t perf_notify, + int jetsam_flags) { thread_t self = current_thread(); struct task *task = p->task; struct uthread *ut; + int error = 0; /* * If a thread in this task has already @@ -232,10 +266,14 @@ exit1(proc_t p, int rv, int *retval) ut = get_bsdthread_info(self); if (ut->uu_flag & UT_VFORK) { - vfork_exit(p, rv); - vfork_return(p , retval, p->p_pid); - unix_syscall_return(0); - /* NOT REACHED */ + if (!thread_can_terminate) { + return EINVAL; + } + + vfork_exit(p, rv); + vfork_return(p , retval, p->p_pid); + unix_syscall_return(0); + /* NOT REACHED */ } /* @@ -255,15 +293,45 @@ exit1(proc_t p, int rv, int *retval) DTRACE_PROC1(exit, int, CLD_EXITED); + /* mark process is going to exit and pull out of DBG/disk throttle */ + /* TODO: This should be done after becoming exit thread */ + proc_set_task_policy(p->task, THREAD_NULL, TASK_POLICY_ATTRIBUTE, + TASK_POLICY_TERMINATED, TASK_POLICY_ENABLE); + proc_lock(p); + error = proc_transstart(p, 1); + if (error == EDEADLK) { + /* Temp: If deadlock error, then it implies multithreaded exec is + * in progress. Instread of letting exit continue and + * corrupting the freed memory, let the exit thread + * return. This will save corruption in remote case. + */ + proc_unlock(p); + if (current_proc() == p){ + if (p->exit_thread == self) + printf("exit_thread failed to exit, leaving process %s[%d] in unkillable limbo\n", + p->p_comm, p->p_pid); + thread_exception_return(); + } else { + /* external termination like jetsam */ + return(error); + } + } + while (p->exit_thread != self) { if (sig_try_locked(p) <= 0) { + proc_transend(p, 1); if (get_threadtask(self) != task) { proc_unlock(p); return(0); } proc_unlock(p); + thread_terminate(self); + if (!thread_can_terminate) { + return 0; + } + thread_exception_return(); /* NOTREACHED */ } @@ -282,24 +350,27 @@ exit1(proc_t p, int rv, int *retval) p->p_lflag |= P_LEXIT; p->p_xstat = rv; + p->p_lflag |= jetsam_flags; + proc_transend(p, 1); proc_unlock(p); - proc_prepareexit(p, rv); + proc_prepareexit(p, rv, perf_notify); - /* task terminate will call proc_terminate and that cleans it up */ + /* Last thread to terminate will call proc_exit() */ task_terminate_internal(task); return(0); } void -proc_prepareexit(proc_t p, int rv) +proc_prepareexit(proc_t p, int rv, boolean_t perf_notify) { mach_exception_data_type_t code, subcode; struct uthread *ut; thread_t self = current_thread(); ut = get_bsdthread_info(self); + struct rusage_superset *rup; /* If a core should be generated, notify crash reporter */ if (hassigprop(WTERMSIG(rv), SA_CORE) || ((p->p_csflags & CS_KILLED) != 0)) { @@ -321,12 +392,35 @@ proc_prepareexit(proc_t p, int rv) ((ut->uu_exception & 0x0f) << 20) | ((int)ut->uu_code & 0xfffff); subcode = ut->uu_subcode; - (void) abnormal_exit_notify(code, subcode); + (void) task_exception_notify(EXC_CRASH, code, subcode); } skipcheck: - /* Notify the perf server */ - (void)sys_perf_notify(self, p->p_pid); + /* Notify the perf server? */ + if (perf_notify) { + (void)sys_perf_notify(self, p->p_pid); + } + + /* + * Before this process becomes a zombie, stash resource usage + * stats in the proc for external observers to query + * via proc_pid_rusage(). + * + * If the zombie allocation fails, just punt the stats. + */ + MALLOC_ZONE(rup, struct rusage_superset *, + sizeof (*rup), M_ZOMBIE, M_WAITOK); + if (rup != NULL) { + gather_rusage_info_v2(p, &rup->ri, RUSAGE_INFO_V2); + rup->ri.ri_phys_footprint = 0; + rup->ri.ri_proc_exit_abstime = mach_absolute_time(); + + /* + * Make the rusage_info visible to external observers + * only after it has been completely filled in. + */ + p->p_ru = rup; + } /* * Remove proc from allproc queue and from pidhash chain. @@ -337,6 +431,10 @@ skipcheck: proc_list_lock(); +#if CONFIG_MEMORYSTATUS + memorystatus_remove(p, TRUE); +#endif + LIST_REMOVE(p, p_list); LIST_INSERT_HEAD(&zombproc, p, p_list); /* Place onto zombproc. */ /* will not be visible via proc_find */ @@ -371,27 +469,47 @@ proc_exit(proc_t p) struct uthread * uth; pid_t pid; int exitval; - - /* This can happen if thread_terminate of the single thread - * process - */ + int knote_hint; uth = (struct uthread *)get_bsdthread_info(current_thread()); proc_lock(p); + proc_transstart(p, 1); if( !(p->p_lflag & P_LEXIT)) { + /* + * This can happen if a thread_terminate() occurs + * in a single-threaded process. + */ p->p_lflag |= P_LEXIT; + proc_transend(p, 1); proc_unlock(p); - proc_prepareexit(p, 0); + proc_prepareexit(p, 0, TRUE); + (void) task_terminate_internal(task); proc_lock(p); + } else { + proc_transend(p, 1); } p->p_lflag |= P_LPEXIT; + + /* + * Other kernel threads may be in the middle of signalling this process. + * Wait for those threads to wrap it up before making the process + * disappear on them. + */ + if ((p->p_lflag & P_LINSIGNAL) || (p->p_sigwaitcnt > 0)) { + p->p_sigwaitcnt++; + while ((p->p_lflag & P_LINSIGNAL) || (p->p_sigwaitcnt > 1)) + msleep(&p->p_sigmask, &p->p_mlock, PWAIT, "proc_sigdrain", NULL); + p->p_sigwaitcnt--; + } + proc_unlock(p); pid = p->p_pid; exitval = p->p_xstat; - KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXIT) | DBG_FUNC_START, - pid, exitval, 0, 0, 0); + KERNEL_DEBUG_CONSTANT_IST(KDEBUG_COMMON, + BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXIT) | DBG_FUNC_START, + pid, exitval, 0, 0, 0); #if CONFIG_DTRACE /* @@ -425,9 +543,11 @@ proc_exit(proc_t p) proc_unlock(p); #endif - /* XXX Zombie allocation may fail, in which case stats get lost */ - MALLOC_ZONE(p->p_ru, struct rusage *, - sizeof (*p->p_ru), M_ZOMBIE, M_WAITOK); + nspace_proc_exit(p); + +#if VM_PRESSURE_EVENTS + vm_pressure_proc_cleanup(p); +#endif /* * need to cancel async IO requests that can be cancelled and wait for those @@ -436,6 +556,10 @@ proc_exit(proc_t p) proc_refdrain(p); + /* if any pending cpu limits action, clear it */ + task_clear_cpuusage(p->task, TRUE); + + workqueue_mark_exiting(p); workqueue_exit(p); _aio_exit( p ); @@ -454,7 +578,7 @@ proc_exit(proc_t p) * no need to throttle this thread since its going away * but we do need to update our bookeeping w/r to throttled threads */ - throttle_lowpri_io(FALSE); + throttle_lowpri_io(0); } #if SYSV_SHM @@ -477,9 +601,9 @@ proc_exit(proc_t p) if (sessp->s_ttyvp != NULLVP) { struct vnode *ttyvp; int ttyvid; + int cttyflag = 0; struct vfs_context context; - struct tty * tp; - + struct tty *tp; /* * Controlling process. @@ -487,55 +611,52 @@ proc_exit(proc_t p) * drain controlling terminal * and revoke access to controlling terminal. */ + session_lock(sessp); tp = SESSION_TP(sessp); - if ((tp != TTY_NULL) && (tp->t_session == sessp)) { - tty_pgsignal(tp, SIGHUP, 1); - - session_lock(sessp); - /* reget potentially tp due to revocation */ - tp = SESSION_TP(sessp); - ttyvp = sessp->s_ttyvp; - ttyvid = sessp->s_ttyvid; - sessp->s_ttyvp = NULLVP; - sessp->s_ttyvid = 0; - sessp->s_ttyp = TTY_NULL; - sessp->s_ttypgrpid = NO_PID; session_unlock(sessp); - if ((ttyvp != NULLVP) && (vnode_getwithvid(ttyvp, ttyvid) == 0)) { + tty_pgsignal(tp, SIGHUP, 1); - if (tp != TTY_NULL) { - tty_lock(tp); - (void) ttywait(tp); - tty_unlock(tp); - } - context.vc_thread = proc_thread(p); /* XXX */ - context.vc_ucred = kauth_cred_proc_ref(p); - VNOP_REVOKE(ttyvp, REVOKEALL, &context); - vnode_put(ttyvp); - kauth_cred_unref(&context.vc_ucred); - } - } else { session_lock(sessp); - /* reget potentially tp due to revocation */ tp = SESSION_TP(sessp); - ttyvp = sessp->s_ttyvp; - sessp->s_ttyvp = NULLVP; - sessp->s_ttyvid = 0; - sessp->s_ttyp = TTY_NULL; - sessp->s_ttypgrpid = NO_PID; - session_unlock(sessp); + } + cttyflag = sessp->s_flags & S_CTTYREF; + sessp->s_flags &= ~S_CTTYREF; + ttyvp = sessp->s_ttyvp; + ttyvid = sessp->s_ttyvid; + sessp->s_ttyvp = NULLVP; + sessp->s_ttyvid = 0; + sessp->s_ttyp = TTY_NULL; + sessp->s_ttypgrpid = NO_PID; + session_unlock(sessp); + + if ((ttyvp != NULLVP) && (vnode_getwithvid(ttyvp, ttyvid) == 0)) { + if (tp != TTY_NULL) { + tty_lock(tp); + (void) ttywait(tp); + tty_unlock(tp); + } + context.vc_thread = proc_thread(p); /* XXX */ + context.vc_ucred = kauth_cred_proc_ref(p); + vnode_rele(ttyvp); + VNOP_REVOKE(ttyvp, REVOKEALL, &context); + if (cttyflag) { + /* + * Release the extra usecount taken in cttyopen. + * usecount should be released after VNOP_REVOKE is called. + */ + vnode_rele(ttyvp); + } + vnode_put(ttyvp); + kauth_cred_unref(&context.vc_ucred); + ttyvp = NULLVP; } if (ttyvp) vnode_rele(ttyvp); - /* - * s_ttyp is not zero'd; we use this to indicate - * that the session once had a controlling terminal. - * (for logging and informational purposes) - */ + if (tp) + ttyfree(tp); } - session_lock(sessp); sessp->s_leader = NULL; session_unlock(sessp); @@ -561,7 +682,7 @@ proc_exit(proc_t p) /* wait till parentrefs are dropped and grant no more */ proc_childdrainstart(p); while ((q = p->p_children.lh_first) != NULL) { - q->p_listflag |= P_LIST_DEADPARENT; + int reparentedtoinit = (q->p_listflag & P_LIST_DEADPARENT) ? 1 : 0; if (q->p_stat == SZOMB) { if (p != q->p_pptr) panic("parent child linkage broken"); @@ -575,22 +696,52 @@ proc_exit(proc_t p) * if the reap is already in progress. So we get * the reference here exclusively and their can be * no waiters. So there is no need for a wakeup - * after we are done. AlsO the reap frees the structure + * after we are done. Also the reap frees the structure * and the proc struct cannot be used for wakeups as well. * It is safe to use q here as this is system reap */ - (void)reap_child_locked(p, q, 1, 1, 0); + (void)reap_child_locked(p, q, 1, reparentedtoinit, 1, 0); } else { - proc_reparentlocked(q, initproc, 0, 1); /* * Traced processes are killed * since their existence means someone is messing up. */ if (q->p_lflag & P_LTRACED) { + struct proc *opp; + + /* + * Take a reference on the child process to + * ensure it doesn't exit and disappear between + * the time we drop the list_lock and attempt + * to acquire its proc_lock. + */ + if (proc_ref_locked(q) != q) + continue; + proc_list_unlock(); + + opp = proc_find(q->p_oppid); + if (opp != PROC_NULL) { + proc_list_lock(); + q->p_oppid = 0; + proc_list_unlock(); + proc_reparentlocked(q, opp, 0, 0); + proc_rele(opp); + } else { + /* original parent exited while traced */ + proc_list_lock(); + q->p_listflag |= P_LIST_DEADPARENT; + q->p_oppid = 0; + proc_list_unlock(); + proc_reparentlocked(q, initproc, 0, 0); + } + proc_lock(q); q->p_lflag &= ~P_LTRACED; + if (q->sigwait_thread) { + thread_t thread = q->sigwait_thread; + proc_unlock(q); /* * The sigwait_thread could be stopped at a @@ -599,13 +750,19 @@ proc_exit(proc_t p) * the first thread in the task. So any attempts to kill * the process would result into a deadlock on q->sigwait. */ - thread_resume((thread_t)q->sigwait_thread); - clear_wait(q->sigwait_thread, THREAD_INTERRUPTED); - threadsignal((thread_t)q->sigwait_thread, SIGKILL, 0); - } else + thread_resume(thread); + clear_wait(thread, THREAD_INTERRUPTED); + threadsignal(thread, SIGKILL, 0); + } else { proc_unlock(q); + } + psignal(q, SIGKILL); proc_list_lock(); + proc_rele_locked(q); + } else { + q->p_listflag |= P_LIST_DEADPARENT; + proc_reparentlocked(q, initproc, 0, 1); } } } @@ -627,13 +784,11 @@ proc_exit(proc_t p) * info and self times. If we were unable to allocate a zombie * structure, this information is lost. */ - /* No need for locking here as no one than this thread can access this */ if (p->p_ru != NULL) { - *p->p_ru = p->p_stats->p_ru; + calcru(p, &p->p_stats->p_ru.ru_utime, &p->p_stats->p_ru.ru_stime, NULL); + p->p_ru->ru = p->p_stats->p_ru; - calcru(p, &p->p_ru->ru_utime, &p->p_ru->ru_stime, NULL); - - ruadd(p->p_ru, &p->p_stats->p_cru); + ruadd(&(p->p_ru->ru), &p->p_stats->p_cru); } /* @@ -689,7 +844,8 @@ proc_exit(proc_t p) p->task = TASK_NULL; set_bsdtask_info(task, NULL); - proc_knote(p, NOTE_EXIT); + knote_hint = NOTE_EXIT | (p->p_xstat & 0xffff); + proc_knote(p, knote_hint); /* mark the thread as the one that is doing proc_exit * no need to hold proc lock in uthread_free @@ -701,6 +857,8 @@ proc_exit(proc_t p) pp = proc_parent(p); if (pp->p_flag & P_NOCLDWAIT) { + if (p->p_ru != NULL) { + proc_lock(pp); #if 3839178 /* * If the parent is ignoring SIGCHLD, then POSIX requires @@ -715,19 +873,18 @@ proc_exit(proc_t p) * zombie to init. If we were unable to allocate a * zombie structure, this information is lost. */ - if (p->p_ru != NULL) { - proc_lock(pp); - ruadd(&pp->p_stats->p_cru, p->p_ru); + ruadd(&pp->p_stats->p_cru, &p->p_ru->ru); +#endif /* !3839178 */ + update_rusage_info_child(&pp->p_stats->ri_child, &p->p_ru->ri); proc_unlock(pp); } -#endif /* !3839178 */ - + /* kernel can reap this one, no need to move it to launchd */ proc_list_lock(); p->p_listflag |= P_LIST_DEADPARENT; proc_list_unlock(); } - if ((p->p_listflag & P_LIST_DEADPARENT) == 0) { + if ((p->p_listflag & P_LIST_DEADPARENT) == 0 || p->p_oppid) { if (pp != initproc) { proc_lock(pp); pp->si_pid = p->p_pid; @@ -737,7 +894,7 @@ proc_exit(proc_t p) * p_ucred usage is safe as it is an exiting process * and reference is dropped in reap */ - pp->si_uid = p->p_ucred->cr_ruid; + pp->si_uid = kauth_cred_getruid(p->p_ucred); proc_unlock(pp); } /* mark as a zombie */ @@ -746,8 +903,9 @@ proc_exit(proc_t p) * The write is to an int and is coherent. Also parent is * keyed off of list lock for reaping */ - KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXIT) | DBG_FUNC_END, - pid, exitval, 0, 0, 0); + KERNEL_DEBUG_CONSTANT_IST(KDEBUG_COMMON, + BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXIT) | DBG_FUNC_END, + pid, exitval, 0, 0, 0); p->p_stat = SZOMB; /* * The current process can be reaped so, no one @@ -769,8 +927,9 @@ proc_exit(proc_t p) * keyed off of list lock for reaping */ proc_list_lock(); - KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXIT) | DBG_FUNC_END, - pid, exitval, 0, 0, 0); + KERNEL_DEBUG_CONSTANT_IST(KDEBUG_COMMON, + BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXIT) | DBG_FUNC_END, + pid, exitval, 0, 0, 0); /* check for sysctl zomb lookup */ while ((p->p_listflag & P_LIST_WAITING) == P_LIST_WAITING) { msleep(&p->p_stat, proc_list_mlock, PWAIT, "waitcoll", 0); @@ -788,7 +947,7 @@ proc_exit(proc_t p) * and the proc struct cannot be used for wakeups as well. * It is safe to use p here as this is system reap */ - (void)reap_child_locked(pp, p, 1, 1, 1); + (void)reap_child_locked(pp, p, 1, 0, 1, 1); /* list lock dropped by reap_child_locked */ } if (uth->uu_lowpri_window) { @@ -798,7 +957,7 @@ proc_exit(proc_t p) * no need to throttle this thread since its going away * but we do need to update our bookeeping w/r to throttled threads */ - throttle_lowpri_io(FALSE); + throttle_lowpri_io(0); } proc_rele(pp); @@ -823,7 +982,7 @@ proc_exit(proc_t p) * 1 Process was reaped */ static int -reap_child_locked(proc_t parent, proc_t child, int deadparent, int locked, int droplock) +reap_child_locked(proc_t parent, proc_t child, int deadparent, int reparentedtoinit, int locked, int droplock) { proc_t trace_parent = PROC_NULL; /* Traced parent process, if tracing */ @@ -838,44 +997,69 @@ reap_child_locked(proc_t parent, proc_t child, int deadparent, int locked, int d * ptraced can simply be reaped, refer to radar 5677288 * p_oppid -> ptraced * trace_parent == initproc -> away from launchd - * P_LIST_DEADPARENT -> came to launchd by reparenting + * reparentedtoinit -> came to launchd by reparenting */ - if (child->p_oppid && (trace_parent = proc_find(child->p_oppid)) - && !((trace_parent == initproc) && (child->p_lflag & P_LIST_DEADPARENT))) { + if (child->p_oppid) { + int knote_hint; + pid_t oppid; + proc_lock(child); + oppid = child->p_oppid; child->p_oppid = 0; + knote_hint = NOTE_EXIT | (child->p_xstat & 0xffff); proc_unlock(child); - if (trace_parent != initproc) { - /* - * proc internal fileds and p_ucred usage safe - * here as child is dead and is not reaped or - * reparented yet - */ - proc_lock(trace_parent); - trace_parent->si_pid = child->p_pid; - trace_parent->si_status = child->p_xstat; - trace_parent->si_code = CLD_CONTINUED; - trace_parent->si_uid = child->p_ucred->cr_ruid; - proc_unlock(trace_parent); - } - proc_reparentlocked(child, trace_parent, 1, 0); - psignal(trace_parent, SIGCHLD); - proc_list_lock(); - wakeup((caddr_t)trace_parent); - child->p_listflag &= ~P_LIST_WAITING; - wakeup(&child->p_stat); - proc_list_unlock(); - proc_rele(trace_parent); - if ((locked == 1) && (droplock == 0)) + + if ((trace_parent = proc_find(oppid)) + && !((trace_parent == initproc) && reparentedtoinit)) { + + if (trace_parent != initproc) { + /* + * proc internal fileds and p_ucred usage safe + * here as child is dead and is not reaped or + * reparented yet + */ + proc_lock(trace_parent); + trace_parent->si_pid = child->p_pid; + trace_parent->si_status = child->p_xstat; + trace_parent->si_code = CLD_CONTINUED; + trace_parent->si_uid = kauth_cred_getruid(child->p_ucred); + proc_unlock(trace_parent); + } + proc_reparentlocked(child, trace_parent, 1, 0); + + /* resend knote to original parent (and others) after reparenting */ + proc_knote(child, knote_hint); + + psignal(trace_parent, SIGCHLD); proc_list_lock(); - return (0); - } - - if (trace_parent != PROC_NULL) { - proc_rele(trace_parent); + wakeup((caddr_t)trace_parent); + child->p_listflag &= ~P_LIST_WAITING; + wakeup(&child->p_stat); + proc_list_unlock(); + proc_rele(trace_parent); + if ((locked == 1) && (droplock == 0)) + proc_list_lock(); + return (0); + } + + /* + * If we can't reparent (e.g. the original parent exited while child was being debugged, or + * original parent is the same as the debugger currently exiting), we still need to satisfy + * the knote lifecycle for other observers on the system. While the debugger was attached, + * the NOTE_EXIT would not have been broadcast during initial child termination. + */ + proc_knote(child, knote_hint); + + if (trace_parent != PROC_NULL) { + proc_rele(trace_parent); + } } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" proc_knote(child, NOTE_REAP); +#pragma clang diagnostic pop + proc_knote_drain(child); child->p_xstat = 0; @@ -891,7 +1075,8 @@ reap_child_locked(proc_t parent, proc_t child, int deadparent, int locked, int d */ if (!(parent->p_flag & P_NOCLDWAIT)) #endif /* 3839178 */ - ruadd(&parent->p_stats->p_cru, child->p_ru); + ruadd(&parent->p_stats->p_cru, &child->p_ru->ru); + update_rusage_info_child(&parent->p_stats->ri_child, &child->p_ru->ri); proc_unlock(parent); FREE_ZONE(child->p_ru, sizeof *child->p_ru, M_ZOMBIE); child->p_ru = NULL; @@ -899,7 +1084,7 @@ reap_child_locked(proc_t parent, proc_t child, int deadparent, int locked, int d printf("Warning : lost p_ru for %s\n", child->p_comm); } - AUDIT_SESSION_PROCEXIT(child->p_ucred); + AUDIT_SESSION_PROCEXIT(child); /* * Decrement the count of procs running with this uid. @@ -907,7 +1092,7 @@ reap_child_locked(proc_t parent, proc_t child, int deadparent, int locked, int d * and refernce is dropped after these calls down below * (locking protection is provided by list lock held in chgproccnt) */ - (void)chgproccnt(child->p_ucred->cr_ruid, -1); + (void)chgproccnt(kauth_cred_getruid(child->p_ucred), -1); #if CONFIG_LCTX ALLLCTX_LOCK; @@ -946,24 +1131,31 @@ reap_child_locked(proc_t parent, proc_t child, int deadparent, int locked, int d proc_checkdeadrefs(child); nprocs--; - proc_list_unlock(); + if (deadparent) { + /* + * If a child zombie is being reaped because its parent + * is exiting, make sure we update the list flag + */ + child->p_listflag |= P_LIST_DEADPARENT; + } -#ifdef CONFIG_EMBEDDED - lck_mtx_destroy(&child->p_mlock, proc_lck_grp); - lck_mtx_destroy(&child->p_fdmlock, proc_lck_grp); -#if CONFIG_DTRACE - lck_mtx_destroy(&child->p_dtrace_sprlock, proc_lck_grp); -#endif - lck_spin_destroy(&child->p_slock, proc_lck_grp); + proc_list_unlock(); -#else +#if CONFIG_FINE_LOCK_GROUPS lck_mtx_destroy(&child->p_mlock, proc_mlock_grp); lck_mtx_destroy(&child->p_fdmlock, proc_fdmlock_grp); #if CONFIG_DTRACE lck_mtx_destroy(&child->p_dtrace_sprlock, proc_lck_grp); #endif lck_spin_destroy(&child->p_slock, proc_slock_grp); +#else /* CONFIG_FINE_LOCK_GROUPS */ + lck_mtx_destroy(&child->p_mlock, proc_lck_grp); + lck_mtx_destroy(&child->p_fdmlock, proc_lck_grp); +#if CONFIG_DTRACE + lck_mtx_destroy(&child->p_dtrace_sprlock, proc_lck_grp); #endif + lck_spin_destroy(&child->p_slock, proc_lck_grp); +#endif /* CONFIG_FINE_LOCK_GROUPS */ workqueue_destroy_lock(child); FREE_ZONE(child, sizeof *child, M_PROC); @@ -1038,6 +1230,8 @@ loop1: if (p->p_stat == SZOMB) { + int reparentedtoinit = (p->p_listflag & P_LIST_DEADPARENT) ? 1 : 0; + proc_list_unlock(); #if CONFIG_MACF if ((error = mac_proc_check_wait(q, p)) != 0) @@ -1059,14 +1253,14 @@ loop1: } else { if (IS_64BIT_PROCESS(q)) { struct user64_rusage my_rusage; - munge_user64_rusage(p->p_ru, &my_rusage); + munge_user64_rusage(&p->p_ru->ru, &my_rusage); error = copyout((caddr_t)&my_rusage, uap->rusage, sizeof (my_rusage)); } else { struct user32_rusage my_rusage; - munge_user32_rusage(p->p_ru, &my_rusage); + munge_user32_rusage(&p->p_ru->ru, &my_rusage); error = copyout((caddr_t)&my_rusage, uap->rusage, sizeof (my_rusage)); @@ -1098,7 +1292,7 @@ loop1: } /* Clean up */ - (void)reap_child_locked(q, p, 0, 0, 0); + (void)reap_child_locked(q, p, 0, reparentedtoinit, 0, 0); return (0); } @@ -1173,6 +1367,12 @@ out: return (error); } +#if DEBUG +#define ASSERT_LCK_MTX_OWNED(lock) \ + lck_mtx_assert(lock, LCK_MTX_ASSERT_OWNED) +#else +#define ASSERT_LCK_MTX_OWNED(lock) /* nothing */ +#endif int waitidcontinue(int result) @@ -1182,12 +1382,12 @@ waitidcontinue(int result) int *retval; if (result) - return(result); + return (result); thread = current_thread(); vt = get_bsduthreadarg(thread); retval = get_bsduthreadrval(thread); - return(waitid(current_proc(), (struct waitid_args *)vt, retval)); + return (waitid(current_proc(), (struct waitid_args *)vt, retval)); } /* @@ -1196,7 +1396,7 @@ waitidcontinue(int result) * * Parameters: uap->idtype one of P_PID, P_PGID, P_ALL * uap->id pid_t or gid_t or ignored - * uap->infop Address of signinfo_t struct in + * uap->infop Address of siginfo_t struct in * user space into which to return status * uap->options flag values * @@ -1207,33 +1407,24 @@ int waitid(proc_t q, struct waitid_args *uap, int32_t *retval) { __pthread_testcancel(1); - return(waitid_nocancel(q, (struct waitid_nocancel_args *)uap, retval)); + return (waitid_nocancel(q, (struct waitid_nocancel_args *)uap, retval)); } int -waitid_nocancel(proc_t q, struct waitid_nocancel_args *uap, __unused int32_t *retval) +waitid_nocancel(proc_t q, struct waitid_nocancel_args *uap, + __unused int32_t *retval) { - user_siginfo_t collect64; /* siginfo data to return to caller */ - + user_siginfo_t siginfo; /* siginfo data to return to caller */ + boolean_t caller64 = IS_64BIT_PROCESS(q); int nfound; proc_t p; int error; - /* - * Forced validation of options for T.waitpid 21; should be a TSD! - * This will pass the test, but note that we have more bits than the - * standard specifies that we will allow in, in this case. The test - * passes because they light all the bits, not just the ones we allow, - * and so the following check returns EINVAL like the test wants. - */ - if (((uap->options & (WNOHANG|WNOWAIT|WCONTINUED|WUNTRACED|WSTOPPED|WEXITED)) != uap->options) || - (uap->options == 0)) + if (uap->options == 0 || + (uap->options & ~(WNOHANG|WNOWAIT|WCONTINUED|WSTOPPED|WEXITED))) return (EINVAL); /* bits set that aren't recognized */ - /* - * Overly critical options checking, per POSIX - */ - switch(uap->idtype) { + switch (uap->idtype) { case P_PID: /* child with process ID equal to... */ case P_PGID: /* child with process group ID equal to... */ if (((int)uap->id) < 0) @@ -1248,7 +1439,8 @@ loop: loop1: nfound = 0; for (p = q->p_children.lh_first; p != 0; p = p->p_sibling.le_next) { - switch(uap->idtype) { + + switch (uap->idtype) { case P_PID: /* child with process ID equal to... */ if (p->p_pid != (pid_t)uap->id) continue; @@ -1268,68 +1460,44 @@ loop1: * the single return for waited process guarantee. */ if (p->p_listflag & P_LIST_WAITING) { - (void)msleep(&p->p_stat, proc_list_mlock, PWAIT, "waitidcoll", 0); + (void) msleep(&p->p_stat, proc_list_mlock, + PWAIT, "waitidcoll", 0); goto loop1; } p->p_listflag |= P_LIST_WAITING; /* mark busy */ nfound++; - /* - * Types of processes we are interested in - * - * XXX Don't know what to do for WCONTINUED?!? - */ - switch(p->p_stat) { + bzero(&siginfo, sizeof (siginfo)); + + switch (p->p_stat) { case SZOMB: /* Exited */ if (!(uap->options & WEXITED)) break; - - /* drop the lock and the thread is going to return */ proc_list_unlock(); +#if CONFIG_MACF + if ((error = mac_proc_check_wait(q, p)) != 0) + goto out; +#endif + siginfo.si_signo = SIGCHLD; + siginfo.si_pid = p->p_pid; + siginfo.si_status = WEXITSTATUS(p->p_xstat); + if (WIFSIGNALED(p->p_xstat)) { + siginfo.si_code = WCOREDUMP(p->p_xstat) ? + CLD_DUMPED : CLD_KILLED; + } else + siginfo.si_code = CLD_EXITED; - /* Collect "siginfo" information for caller */ - collect64.si_signo = SIGCHLD; - collect64.si_code = 0; - collect64.si_errno = 0; - collect64.si_pid = 0; - collect64.si_uid = 0; - collect64.si_addr = 0; - collect64.si_status = WEXITSTATUS(p->p_xstat); - collect64.si_band = 0; - - if (IS_64BIT_PROCESS(p)) { - user64_siginfo_t sinfo64; - - siginfo_user_to_user64(&collect64, &sinfo64); - - error = copyout((caddr_t)&sinfo64, - uap->infop, - sizeof(sinfo64)); - } else { - user32_siginfo_t sinfo32; - - siginfo_user_to_user32(&collect64, &sinfo32); - - error = copyout((caddr_t)&sinfo32, - uap->infop, - sizeof(sinfo32)); - } - /* information unavailable? */ - if (error) + if ((error = copyoutsiginfo(&siginfo, + caller64, uap->infop)) != 0) goto out; /* Prevent other process for waiting for this event? */ if (!(uap->options & WNOWAIT)) { - /* Clean up */ - (void)reap_child_locked(q, p, 0, 0, 0); - } else { - proc_list_lock(); - p->p_listflag &= ~P_LIST_WAITING; - proc_list_unlock(); + (void) reap_child_locked(q, p, 0, 0, 0, 0); + return (0); } - - return (0); + goto out; case SSTOP: /* Stopped */ /* @@ -1345,41 +1513,18 @@ loop1: */ if ((p->p_lflag & P_LWAITED) != 0) break; - - /* drop the lock and the thread is going to return */ proc_list_unlock(); +#if CONFIG_MACF + if ((error = mac_proc_check_wait(q, p)) != 0) + goto out; +#endif + siginfo.si_signo = SIGCHLD; + siginfo.si_pid = p->p_pid; + siginfo.si_status = p->p_xstat; /* signal number */ + siginfo.si_code = CLD_STOPPED; - /* Collect "siginfo" information for caller */ - collect64.si_signo = SIGCHLD; - collect64.si_code = 0; - collect64.si_errno = 0; - collect64.si_pid = 0; - collect64.si_uid = 0; - collect64.si_addr = 0; - proc_lock(p); - collect64.si_status = p->p_xstat; - proc_unlock(p); - collect64.si_band = 0; - - if (IS_64BIT_PROCESS(p)) { - user64_siginfo_t sinfo64; - - siginfo_user_to_user64(&collect64, &sinfo64); - - error = copyout((caddr_t)&sinfo64, - uap->infop, - sizeof(sinfo64)); - } else { - user32_siginfo_t sinfo32; - - siginfo_user_to_user32(&collect64, &sinfo32); - - error = copyout((caddr_t)&sinfo32, - uap->infop, - sizeof(sinfo32)); - } - /* information unavailable? */ - if (error) + if ((error = copyoutsiginfo(&siginfo, + caller64, uap->infop)) != 0) goto out; /* Prevent other process for waiting for this event? */ @@ -1388,12 +1533,9 @@ loop1: p->p_lflag |= P_LWAITED; proc_unlock(p); } - - error = 0; goto out; - default: /* All others */ - /* ...meaning Continued */ + default: /* All other states => Continued */ if (!(uap->options & WCONTINUED)) break; @@ -1404,60 +1546,40 @@ loop1: */ if ((p->p_flag & P_CONTINUED) == 0) break; - - /* drop the lock and the thread is going to return */ proc_list_unlock(); - - /* Collect "siginfo" information for caller */ +#if CONFIG_MACF + if ((error = mac_proc_check_wait(q, p)) != 0) + goto out; +#endif + siginfo.si_signo = SIGCHLD; + siginfo.si_code = CLD_CONTINUED; proc_lock(p); - collect64.si_signo = SIGCHLD; - collect64.si_code = CLD_CONTINUED; - collect64.si_errno = 0; - collect64.si_pid = p->p_contproc; - collect64.si_uid = 0; - collect64.si_addr = 0; - collect64.si_status = p->p_xstat; - collect64.si_band = 0; + siginfo.si_pid = p->p_contproc; + siginfo.si_status = p->p_xstat; proc_unlock(p); - if (IS_64BIT_PROCESS(p)) { - user64_siginfo_t sinfo64; - - siginfo_user_to_user64(&collect64, &sinfo64); - - error = copyout((caddr_t)&sinfo64, - uap->infop, - sizeof(sinfo64)); - } else { - user32_siginfo_t sinfo32; - - siginfo_user_to_user32(&collect64, &sinfo32); - - error = copyout((caddr_t)&sinfo32, - uap->infop, - sizeof(sinfo32)); - } - /* information unavailable? */ - if (error) + if ((error = copyoutsiginfo(&siginfo, + caller64, uap->infop)) != 0) goto out; /* Prevent other process for waiting for this event? */ if (!(uap->options & WNOWAIT)) { - OSBitAndAtomic(~((uint32_t)P_CONTINUED), &p->p_flag); + OSBitAndAtomic(~((uint32_t)P_CONTINUED), + &p->p_flag); } - - error = 0; goto out; } - /* LIST LOCK IS HELD HERE */ + ASSERT_LCK_MTX_OWNED(proc_list_mlock); + /* Not a process we are interested in; go on to next child */ - + p->p_listflag &= ~P_LIST_WAITING; wakeup(&p->p_stat); } + ASSERT_LCK_MTX_OWNED(proc_list_mlock); - /* list lock is always held */ /* No child processes that could possibly satisfy the request? */ + if (nfound == 0) { proc_list_unlock(); return (ECHILD); @@ -1465,10 +1587,24 @@ loop1: if (uap->options & WNOHANG) { proc_list_unlock(); +#if CONFIG_MACF + if ((error = mac_proc_check_wait(q, p)) != 0) + return (error); +#endif + /* + * The state of the siginfo structure in this case + * is undefined. Some implementations bzero it, some + * (like here) leave it untouched for efficiency. + * + * Thus the most portable check for "no matching pid with + * WNOHANG" is to store a zero into si_pid before + * invocation, then check for a non-zero value afterwards. + */ return (0); } - if ((error = msleep0((caddr_t)q, proc_list_mlock, PWAIT | PCATCH | PDROP, "waitid", 0, waitidcontinue))) + if ((error = msleep0(q, proc_list_mlock, + PWAIT | PCATCH | PDROP, "waitid", 0, waitidcontinue)) != 0) return (error); goto loop; @@ -1586,6 +1722,10 @@ vfork_exit_internal(proc_t p, int rv, int forceexit) proc_list_lock(); +#if CONFIG_MEMORYSTATUS + memorystatus_remove(p, TRUE); +#endif + LIST_REMOVE(p, p_list); LIST_INSERT_HEAD(&zombproc, p, p_list); /* Place onto zombproc. */ /* will not be visible via proc_find */ @@ -1632,11 +1772,11 @@ vproc_exit(proc_t p) #endif struct pgrp * pg; struct session *sessp; + struct rusage_superset *rup; /* XXX Zombie allocation may fail, in which case stats get lost */ - MALLOC_ZONE(p->p_ru, struct rusage *, - sizeof (*p->p_ru), M_ZOMBIE, M_WAITOK); - + MALLOC_ZONE(rup, struct rusage_superset *, + sizeof (*rup), M_ZOMBIE, M_WAITOK); proc_refdrain(p); @@ -1652,8 +1792,9 @@ vproc_exit(proc_t p) if (sessp->s_ttyvp != NULLVP) { struct vnode *ttyvp; int ttyvid; + int cttyflag = 0; struct vfs_context context; - struct tty * tp; + struct tty *tp; /* * Controlling process. @@ -1661,54 +1802,52 @@ vproc_exit(proc_t p) * drain controlling terminal * and revoke access to controlling terminal. */ + session_lock(sessp); tp = SESSION_TP(sessp); - if ((tp != TTY_NULL) && (tp->t_session == sessp)) { + session_unlock(sessp); + tty_pgsignal(tp, SIGHUP, 1); - tty_lock(tp); - (void) ttywait(tp); - tty_unlock(tp); - /* - * The tty could have been revoked - * if we blocked. - */ session_lock(sessp); - /* reget in case of race */ tp = SESSION_TP(sessp); - ttyvp = sessp->s_ttyvp; - ttyvid = sessp->s_ttyvid; - sessp->s_ttyvp = NULL; - sessp->s_ttyvid = 0; - sessp->s_ttyp = TTY_NULL; - sessp->s_ttypgrpid = NO_PID; - session_unlock(sessp); - - if ((ttyvp != NULLVP) && (vnode_getwithvid(ttyvp, ttyvid) == 0)) { - context.vc_thread = proc_thread(p); /* XXX */ - context.vc_ucred = kauth_cred_proc_ref(p); - VNOP_REVOKE(ttyvp, REVOKEALL, &context); - vnode_put(ttyvp); - kauth_cred_unref(&context.vc_ucred); + } + cttyflag = sessp->s_flags & S_CTTYREF; + sessp->s_flags &= ~S_CTTYREF; + ttyvp = sessp->s_ttyvp; + ttyvid = sessp->s_ttyvid; + sessp->s_ttyvp = NULL; + sessp->s_ttyvid = 0; + sessp->s_ttyp = TTY_NULL; + sessp->s_ttypgrpid = NO_PID; + session_unlock(sessp); + + if ((ttyvp != NULLVP) && (vnode_getwithvid(ttyvp, ttyvid) == 0)) { + if (tp != TTY_NULL) { + tty_lock(tp); + (void) ttywait(tp); + tty_unlock(tp); } - } else { - session_lock(sessp); - ttyvp = sessp->s_ttyvp; - sessp->s_ttyvp = NULL; - sessp->s_ttyvid = 0; - sessp->s_ttyp = TTY_NULL; - sessp->s_ttypgrpid = NO_PID; - session_unlock(sessp); + context.vc_thread = proc_thread(p); /* XXX */ + context.vc_ucred = kauth_cred_proc_ref(p); + vnode_rele(ttyvp); + VNOP_REVOKE(ttyvp, REVOKEALL, &context); + if (cttyflag) { + /* + * Release the extra usecount taken in cttyopen. + * usecount should be released after VNOP_REVOKE is called. + */ + vnode_rele(ttyvp); + } + vnode_put(ttyvp); + kauth_cred_unref(&context.vc_ucred); + ttyvp = NULLVP; } if (ttyvp) vnode_rele(ttyvp); - /* - * s_ttyp is not zero'd; we use this to indicate - * that the session once had a controlling terminal. - * (for logging and informational purposes) - */ + if (tp) + ttyfree(tp); } - session_lock(sessp); sessp->s_leader = NULL; session_unlock(sessp); @@ -1724,7 +1863,6 @@ vproc_exit(proc_t p) proc_list_lock(); proc_childdrainstart(p); while ((q = p->p_children.lh_first) != NULL) { - q->p_listflag |= P_LIST_DEADPARENT; if (q->p_stat == SZOMB) { if (p != q->p_pptr) panic("parent child linkage broken"); @@ -1742,18 +1880,39 @@ vproc_exit(proc_t p) * and the proc struct cannot be used for wakeups as well. * It is safe to use q here as this is system reap */ - (void)reap_child_locked(p, q, 1, 1, 0); + (void)reap_child_locked(p, q, 1, 0, 1, 0); } else { - proc_reparentlocked(q, initproc, 0, 1); /* * Traced processes are killed * since their existence means someone is messing up. */ if (q->p_lflag & P_LTRACED) { + struct proc *opp; + proc_list_unlock(); + + opp = proc_find(q->p_oppid); + if (opp != PROC_NULL) { + proc_list_lock(); + q->p_oppid = 0; + proc_list_unlock(); + proc_reparentlocked(q, opp, 0, 0); + proc_rele(opp); + } else { + /* original parent exited while traced */ + proc_list_lock(); + q->p_listflag |= P_LIST_DEADPARENT; + q->p_oppid = 0; + proc_list_unlock(); + proc_reparentlocked(q, initproc, 0, 0); + } + proc_lock(q); q->p_lflag &= ~P_LTRACED; + if (q->sigwait_thread) { + thread_t thread = q->sigwait_thread; + proc_unlock(q); /* * The sigwait_thread could be stopped at a @@ -1762,14 +1921,18 @@ vproc_exit(proc_t p) * the first thread in the task. So any attempts to kill * the process would result into a deadlock on q->sigwait. */ - thread_resume((thread_t)q->sigwait_thread); - clear_wait(q->sigwait_thread, THREAD_INTERRUPTED); - threadsignal((thread_t)q->sigwait_thread, SIGKILL, 0); - } else + thread_resume(thread); + clear_wait(thread, THREAD_INTERRUPTED); + threadsignal(thread, SIGKILL, 0); + } else { proc_unlock(q); - + } + psignal(q, SIGKILL); proc_list_lock(); + } else { + q->p_listflag |= P_LIST_DEADPARENT; + proc_reparentlocked(q, initproc, 0, 1); } } } @@ -1791,26 +1954,25 @@ vproc_exit(proc_t p) * info and self times. If we were unable to allocate a zombie * structure, this information is lost. */ - /* No need for locking here as no one than this thread can access this */ - if (p->p_ru != NULL) { - *p->p_ru = p->p_stats->p_ru; - timerclear(&p->p_ru->ru_utime); - timerclear(&p->p_ru->ru_stime); + if (rup != NULL) { + rup->ru = p->p_stats->p_ru; + timerclear(&rup->ru.ru_utime); + timerclear(&rup->ru.ru_stime); #ifdef FIXME if (task) { - task_basic_info_data_t tinfo; + mach_task_basic_info_data_t tinfo; task_thread_times_info_data_t ttimesinfo; int task_info_stuff, task_ttimes_stuff; struct timeval ut,st; - task_info_stuff = TASK_BASIC_INFO_COUNT; - task_info(task, TASK_BASIC_INFO, + task_info_stuff = MACH_TASK_BASIC_INFO_COUNT; + task_info(task, MACH_TASK_BASIC_INFO, &tinfo, &task_info_stuff); - p->p_ru->ru_utime.tv_sec = tinfo.user_time.seconds; - p->p_ru->ru_utime.tv_usec = tinfo.user_time.microseconds; - p->p_ru->ru_stime.tv_sec = tinfo.system_time.seconds; - p->p_ru->ru_stime.tv_usec = tinfo.system_time.microseconds; + p->p_ru->ru.ru_utime.tv_sec = tinfo.user_time.seconds; + p->p_ru->ru.ru_utime.tv_usec = tinfo.user_time.microseconds; + p->p_ru->ru.ru_stime.tv_sec = tinfo.system_time.seconds; + p->p_ru->ru.ru_stime.tv_usec = tinfo.system_time.microseconds; task_ttimes_stuff = TASK_THREAD_TIMES_INFO_COUNT; task_info(task, TASK_THREAD_TIMES_INFO, @@ -1820,12 +1982,22 @@ vproc_exit(proc_t p) ut.tv_usec = ttimesinfo.user_time.microseconds; st.tv_sec = ttimesinfo.system_time.seconds; st.tv_usec = ttimesinfo.system_time.microseconds; - timeradd(&ut,&p->p_ru->ru_utime,&p->p_ru->ru_utime); - timeradd(&st,&p->p_ru->ru_stime,&p->p_ru->ru_stime); + timeradd(&ut,&p->p_ru->ru.ru_utime,&p->p_ru->ru.ru_utime); + timeradd(&st,&p->p_ru->ru.ru_stime,&p->p_ru->ru.ru_stime); } #endif /* FIXME */ - ruadd(p->p_ru, &p->p_stats->p_cru); + ruadd(&rup->ru, &p->p_stats->p_cru); + + gather_rusage_info_v2(p, &rup->ri, RUSAGE_INFO_V2); + rup->ri.ri_phys_footprint = 0; + rup->ri.ri_proc_exit_abstime = mach_absolute_time(); + + /* + * Now that we have filled in the rusage info, make it + * visible to an external observer via proc_pid_rusage(). + */ + p->p_ru = rup; } /* @@ -1844,6 +2016,10 @@ vproc_exit(proc_t p) } } +#if PSYNCH + pth_proc_hashdelete(p); +#endif /* PSYNCH */ + /* * Other substructures are freed from wait(). */ @@ -1877,7 +2053,7 @@ vproc_exit(proc_t p) * p_ucred usage is safe as it is an exiting process * and reference is dropped in reap */ - pp->si_uid = p->p_ucred->cr_ruid; + pp->si_uid = kauth_cred_getruid(p->p_ucred); proc_unlock(pp); } /* mark as a zombie */ @@ -1913,7 +2089,7 @@ vproc_exit(proc_t p) * and the proc struct cannot be used for wakeups as well. * It is safe to use p here as this is system reap */ - (void)reap_child_locked(pp, p, 0, 1, 1); + (void)reap_child_locked(pp, p, 0, 0, 1, 1); /* list lock dropped by reap_child_locked */ } proc_rele(pp);