X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/7ddcb079202367355dddccdfa4318e57d50318be..c6bf4f310a33a9262d455ea4d3f0630b1255e3fe:/bsd/kern/kern_fork.c diff --git a/bsd/kern/kern_fork.c b/bsd/kern/kern_fork.c index 76c1fbae6..c25c85ad2 100644 --- a/bsd/kern/kern_fork.c +++ b/bsd/kern/kern_fork.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2007 Apple Inc. All rights reserved. + * Copyright (c) 2000-2019 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * 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 @@ -11,10 +11,10 @@ * 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. - * + * * 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, @@ -22,7 +22,7 @@ * 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_OSREFERENCE_LICENSE_HEADER_END@ */ /* Copyright (c) 1995, 1997 Apple Computer, Inc. All Rights Reserved */ @@ -87,17 +87,27 @@ #include #include #include +#include #include #include #include #include #include #include +#if CONFIG_PERSONAS +#include +#endif +#include #if CONFIG_DTRACE /* Do not include dtrace.h, it redefines kmem_[alloc/free] */ -extern void dtrace_fasttrap_fork(proc_t, proc_t); -extern void (*dtrace_helpers_fork)(proc_t, proc_t); -extern void dtrace_lazy_dofs_duplicate(proc_t, proc_t); +extern void (*dtrace_proc_waitfor_exec_ptr)(proc_t); +extern void dtrace_proc_fork(proc_t, proc_t, int); + +/* + * Since dtrace_proc_waitfor_exec_ptr can be added/removed in dtrace_subr.c, + * we will store its value before actually calling it. + */ +static void (*dtrace_proc_waitfor_hook)(proc_t) = NULL; #include #endif @@ -105,17 +115,21 @@ extern void dtrace_lazy_dofs_duplicate(proc_t, proc_t); #include #include +#include #include #include #include #include +#include #include #include -#include +#include + +#include #if CONFIG_MACF -#include +#include #include #endif @@ -123,28 +137,40 @@ extern void dtrace_lazy_dofs_duplicate(proc_t, proc_t); #include #include -#include /* for shmfork() */ -#include /* for thread_create() */ -#include /* for thread_resume() */ +#include /* for shmfork() */ +#include /* for thread_create() */ +#include /* for thread_resume() */ #include +#if CONFIG_MEMORYSTATUS +#include +#endif + /* XXX routines which should have Mach prototypes, but don't */ void thread_set_parent(thread_t parent, int pid); extern void act_thread_catt(void *ctx); void thread_set_child(thread_t child, int pid); void *act_thread_csave(void); +extern boolean_t task_is_exec_copy(task_t); +int nextpidversion = 0; -thread_t cloneproc(task_t, proc_t, int); +thread_t cloneproc(task_t, coalition_t *, proc_t, int, int); proc_t forkproc(proc_t); void forkproc_free(proc_t); -thread_t fork_create_child(task_t parent_task, proc_t child, int inherit_memory, int is64bit); +thread_t fork_create_child(task_t parent_task, + coalition_t *parent_coalitions, + proc_t child, + int inherit_memory, + int is_64bit_addr, + int is_64bit_data, + int in_exec); void proc_vfork_begin(proc_t parent_proc); void proc_vfork_end(proc_t parent_proc); -#define DOFORK 0x1 /* fork() system call */ -#define DOVFORK 0x2 /* vfork() system call */ +#define DOFORK 0x1 /* fork() system call */ +#define DOVFORK 0x2 /* vfork() system call */ /* * proc_vfork_begin @@ -158,8 +184,8 @@ void proc_vfork_end(proc_t parent_proc); * Notes: Although this function increments a count, a count in * excess of 1 is not currently supported. According to the * POSIX standard, calling anything other than execve() or - * _exit() fillowing a vfork(), including calling vfork() - * itself again, will result in undefned behaviour + * _exit() following a vfork(), including calling vfork() + * itself again, will result in undefined behaviour */ void proc_vfork_begin(proc_t parent_proc) @@ -179,7 +205,7 @@ proc_vfork_begin(proc_t parent_proc) * * Returns: (void) * - * Notes: Decerements the count; currently, reentrancy of vfork() + * Notes: Decrements the count; currently, reentrancy of vfork() * is unsupported on the current process */ void @@ -187,11 +213,12 @@ proc_vfork_end(proc_t parent_proc) { proc_lock(parent_proc); parent_proc->p_vforkcnt--; - if (parent_proc->p_vforkcnt < 0) + if (parent_proc->p_vforkcnt < 0) { panic("vfork cnt is -ve"); - /* resude the vfork count; clear the flag when it goes to 0 */ - if (parent_proc->p_vforkcnt == 0) + } + if (parent_proc->p_vforkcnt == 0) { parent_proc->p_lflag &= ~P_LVFORK; + } proc_unlock(parent_proc); } @@ -274,18 +301,14 @@ vfork(proc_t parent_proc, __unused struct vfork_args *uap, int32_t *retval) thread_t child_thread; int err; - if ((err = fork1(parent_proc, &child_thread, PROC_CREATE_VFORK)) != 0) { + if ((err = fork1(parent_proc, &child_thread, PROC_CREATE_VFORK, NULL)) != 0) { retval[1] = 0; } else { - /* - * kludge: rely on uu_proc being set in the vfork case, - * rather than returning the actual thread. We can remove - * this when we remove the uu_proc/current_proc() kludge. - */ - proc_t child_proc = current_proc(); + uthread_t ut = get_bsdthread_info(current_thread()); + proc_t child_proc = ut->uu_proc; retval[0] = child_proc->p_pid; - retval[1] = 1; /* flag child return for user space */ + retval[1] = 1; /* flag child return for user space */ /* * Drop the signal lock on the child which was taken on our @@ -295,12 +318,12 @@ vfork(proc_t parent_proc, __unused struct vfork_args *uap, int32_t *retval) proc_signalend(child_proc, 0); proc_transend(child_proc, 0); - /* flag the fork has occurred */ proc_knote(parent_proc, NOTE_FORK | child_proc->p_pid); DTRACE_PROC1(create, proc_t, child_proc); + ut->uu_flag &= ~UT_VFORKING; } - return(err); + return err; } @@ -313,8 +336,14 @@ vfork(proc_t parent_proc, __unused struct vfork_args *uap, int32_t *retval) * Parameters: parent_proc parent process of the process being * child_threadp pointer to location to receive the * Mach thread_t of the child process - * breated + * created * kind kind of creation being requested + * coalitions if spawn, the set of coalitions the + * child process should join, or NULL to + * inherit the parent's. On non-spawns, + * this param is ignored and the child + * always inherits the parent's + * coalitions. * * Notes: Permissable values for 'kind': * @@ -346,11 +375,11 @@ vfork(proc_t parent_proc, __unused struct vfork_args *uap, int32_t *retval) * back to the other information. */ int -fork1(proc_t parent_proc, thread_t *child_threadp, int kind) +fork1(proc_t parent_proc, thread_t *child_threadp, int kind, coalition_t *coalitions) { thread_t parent_thread = (thread_t)current_thread(); uthread_t parent_uthread = (uthread_t)get_bsdthread_info(parent_thread); - proc_t child_proc = NULL; /* set in switch, but compiler... */ + proc_t child_proc = NULL; /* set in switch, but compiler... */ thread_t child_thread = NULL; uid_t uid; int count; @@ -367,9 +396,17 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) uid = kauth_getruid(); proc_list_lock(); if ((nprocs >= maxproc - 1 && uid != 0) || nprocs >= maxproc) { +#if (DEVELOPMENT || DEBUG) && CONFIG_EMBEDDED + /* + * On the development kernel, panic so that the fact that we hit + * the process limit is obvious, as this may very well wedge the + * system. + */ + panic("The process table is full; parent pid=%d", parent_proc->p_pid); +#endif proc_list_unlock(); tablefull("proc"); - return (EAGAIN); + return EAGAIN; } proc_list_unlock(); @@ -382,7 +419,16 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) count = chgproccnt(uid, 1); if (uid != 0 && (rlim_t)count > parent_proc->p_rlimit[RLIMIT_NPROC].rlim_cur) { - err = EAGAIN; +#if (DEVELOPMENT || DEBUG) && CONFIG_EMBEDDED + /* + * On the development kernel, panic so that the fact that we hit + * the per user process limit is obvious. This may be less dire + * than hitting the global process limit, but we cannot rely on + * that. + */ + panic("The per-user process limit has been hit; parent pid=%d, uid=%d", parent_proc->p_pid, uid); +#endif + err = EAGAIN; goto bad; } @@ -392,12 +438,12 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) * it to fork. This is an advisory-only check. */ err = mac_proc_check_fork(parent_proc); - if (err != 0) { + if (err != 0) { goto bad; } #endif - switch(kind) { + switch (kind) { case PROC_CREATE_VFORK: /* * Prevent a vfork while we are in vfork(); we should @@ -480,7 +526,20 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) child_proc->p_vforkact = parent_thread; child_proc->p_stat = SRUN; - parent_uthread->uu_flag |= UT_VFORK; + /* + * Until UT_VFORKING is cleared at the end of the vfork + * syscall, the process identity of this thread is slightly + * murky. + * + * As long as UT_VFORK and it's associated field (uu_proc) + * is set, current_proc() will always return the child process. + * + * However dtrace_proc_selfpid() returns the parent pid to + * ensure that e.g. the proc:::create probe actions accrue + * to the parent. (Otherwise the child magically seems to + * have created itself!) + */ + parent_uthread->uu_flag |= UT_VFORK | UT_VFORKING; parent_uthread->uu_proc = child_proc; parent_uthread->uu_userstate = (void *)act_thread_csave(); parent_uthread->uu_vforkmask = parent_uthread->uu_sigmask; @@ -495,7 +554,7 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) /* XXX is this actually necessary, given syscall return? */ thread_set_child(parent_thread, child_proc->p_pid); - child_proc->p_acflag = AFORK; /* forked but not exec'ed */ + child_proc->p_acflag = AFORK; /* forked but not exec'ed */ /* * Preserve synchronization semantics of vfork. If @@ -503,7 +562,7 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) * on child, and sleep on our proc (in case of exit). */ child_proc->p_lflag |= P_LPPWAIT; - pinsertchild(parent_proc, child_proc); /* set visible */ + pinsertchild(parent_proc, child_proc); /* set visible */ break; @@ -516,7 +575,7 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) */ spawn = 1; - /* FALLSTHROUGH */ + /* FALLSTHROUGH */ case PROC_CREATE_FORK: /* @@ -525,7 +584,11 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) * will, in effect, create a duplicate of it, with only minor * differences. Contrarily, spawned processes do not inherit. */ - if ((child_thread = cloneproc(parent_proc->task, parent_proc, spawn ? FALSE : TRUE)) == NULL) { + if ((child_thread = cloneproc(parent_proc->task, + spawn ? coalitions : NULL, + parent_proc, + spawn ? FALSE : TRUE, + FALSE)) == NULL) { /* Failed to create thread */ err = EAGAIN; goto bad; @@ -581,64 +644,19 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) */ thread_set_child(child_thread, child_proc->p_pid); - child_proc->p_acflag = AFORK; /* forked but not exec'ed */ + child_proc->p_acflag = AFORK; /* forked but not exec'ed */ -// dtrace code cleanup needed #if CONFIG_DTRACE - /* - * This code applies to new processes who are copying the task - * and thread state and address spaces of their parent process. - */ + dtrace_proc_fork(parent_proc, child_proc, spawn); +#endif /* CONFIG_DTRACE */ if (!spawn) { -// call dtrace specific function here instead of all this... - /* - * APPLE NOTE: Solaris does a sprlock() and drops the - * proc_lock here. We're cheating a bit and only taking - * the p_dtrace_sprlock lock. A full sprlock would - * task_suspend the parent. - */ - lck_mtx_lock(&parent_proc->p_dtrace_sprlock); - - /* - * Remove all DTrace tracepoints from the child process. We - * need to do this _before_ duplicating USDT providers since - * any associated probes may be immediately enabled. - */ - if (parent_proc->p_dtrace_count > 0) { - dtrace_fasttrap_fork(parent_proc, child_proc); + /* + * Of note, we need to initialize the bank context behind + * the protection of the proc_trans lock to prevent a race with exit. + */ + task_bank_init(get_threadtask(child_thread)); } - lck_mtx_unlock(&parent_proc->p_dtrace_sprlock); - - /* - * Duplicate any lazy dof(s). This must be done while NOT - * holding the parent sprlock! Lock ordering is - * dtrace_dof_mode_lock, then sprlock. It is imperative we - * always call dtrace_lazy_dofs_duplicate, rather than null - * check and call if !NULL. If we NULL test, during lazy dof - * faulting we can race with the faulting code and proceed - * from here to beyond the helpers copy. The lazy dof - * faulting will then fail to copy the helpers to the child - * process. - */ - dtrace_lazy_dofs_duplicate(parent_proc, child_proc); - - /* - * Duplicate any helper actions and providers. The SFORKING - * we set above informs the code to enable USDT probes that - * sprlock() may fail because the child is being forked. - */ - /* - * APPLE NOTE: As best I can tell, Apple's sprlock() equivalent - * never fails to find the child. We do not set SFORKING. - */ - if (parent_proc->p_dtrace_helpers != NULL && dtrace_helpers_fork) { - (*dtrace_helpers_fork)(parent_proc, child_proc); - } - - } -#endif /* CONFIG_DTRACE */ - break; default: @@ -660,7 +678,7 @@ bad: (void)chgproccnt(uid, -1); } - return (err); + return err; } @@ -671,48 +689,53 @@ bad: * this is done by reassociating the parent process structure * with the task, thread, and uthread. * + * Refer to the ASCII art above vfork() to figure out the + * state we're undoing. + * * Parameters: child_proc Child process * retval System call return value array * rval Return value to present to parent * * Returns: void * - * Note: The caller resumes or exits the parent, as appropriate, after - * callling this function. + * Notes: The caller resumes or exits the parent, as appropriate, after + * calling this function. */ void vfork_return(proc_t child_proc, int32_t *retval, int rval) { - proc_t parent_proc = child_proc->p_pptr; - thread_t parent_thread = (thread_t)current_thread(); - uthread_t parent_uthread = (uthread_t)get_bsdthread_info(parent_thread); - - act_thread_catt(parent_uthread->uu_userstate); + task_t parent_task = get_threadtask(child_proc->p_vforkact); + proc_t parent_proc = get_bsdtask_info(parent_task); + thread_t th = current_thread(); + uthread_t uth = get_bsdthread_info(th); + + act_thread_catt(uth->uu_userstate); - /* end vfork in parent */ + /* clear vfork state in parent proc structure */ proc_vfork_end(parent_proc); /* REPATRIATE PARENT TASK, THREAD, UTHREAD */ - parent_uthread->uu_userstate = 0; - parent_uthread->uu_flag &= ~UT_VFORK; + uth->uu_userstate = 0; + uth->uu_flag &= ~UT_VFORK; /* restore thread-set-id state */ - if (parent_uthread->uu_flag & UT_WASSETUID) { - parent_uthread->uu_flag |= UT_SETUID; - parent_uthread->uu_flag &= UT_WASSETUID; + if (uth->uu_flag & UT_WASSETUID) { + uth->uu_flag |= UT_SETUID; + uth->uu_flag &= ~UT_WASSETUID; } - parent_uthread->uu_proc = 0; - parent_uthread->uu_sigmask = parent_uthread->uu_vforkmask; - child_proc->p_lflag &= ~P_LINVFORK; - child_proc->p_vforkact = (void *)0; + uth->uu_proc = 0; + uth->uu_sigmask = uth->uu_vforkmask; + + proc_lock(child_proc); + child_proc->p_lflag &= ~P_LINVFORK; + child_proc->p_vforkact = 0; + proc_unlock(child_proc); - thread_set_parent(parent_thread, rval); + thread_set_parent(th, rval); if (retval) { retval[0] = rval; - retval[1] = 0; /* mark parent */ + retval[1] = 0; /* mark parent */ } - - return; } @@ -723,12 +746,16 @@ vfork_return(proc_t child_proc, int32_t *retval, int rval) * process * * Parameters: parent_task parent task - * child_proc child process + * parent_coalitions parent's set of coalitions + * child_proc child process * inherit_memory TRUE, if the parents address space is - * to be inherited by the child - * is64bit TRUE, if the child being created will - * be associated with a 64 bit process - * rather than a 32 bit process + * to be inherited by the child + * is_64bit_addr TRUE, if the child being created will + * be associated with a 64 bit address space + * is_64bit_data TRUE if the child being created will use a + * 64-bit register state + * in_exec TRUE, if called from execve or posix spawn set exec + * FALSE, if called from fork or vfexec * * Note: This code is called in the fork() case, from the execve() call * graph, if implementing an execve() following a vfork(), from @@ -736,72 +763,94 @@ vfork_return(proc_t child_proc, int32_t *retval, int rval) * vfork() equivalent call, and in the system bootstrap case. * * It creates a new task and thread (and as a side effect of the - * thread creation, a uthread), which is then associated with the - * process 'child'. If the parent process address space is to - * be inherited, then a flag indicates that the newly created - * task should inherit this from the child task. + * thread creation, a uthread) in the parent coalition set, which is + * then associated with the process 'child'. If the parent + * process address space is to be inherited, then a flag + * indicates that the newly created task should inherit this from + * the child task. * * As a special concession to bootstrapping the initial process * in the system, it's possible for 'parent_task' to be TASK_NULL; * in this case, 'inherit_memory' MUST be FALSE. */ thread_t -fork_create_child(task_t parent_task, proc_t child_proc, int inherit_memory, int is64bit) +fork_create_child(task_t parent_task, + coalition_t *parent_coalitions, + proc_t child_proc, + int inherit_memory, + int is_64bit_addr, + int is_64bit_data, + int in_exec) { - thread_t child_thread = NULL; - task_t child_task; - kern_return_t result; + thread_t child_thread = NULL; + task_t child_task; + kern_return_t result; /* Create a new task for the child process */ result = task_create_internal(parent_task, - inherit_memory, - is64bit, - &child_task); + parent_coalitions, + inherit_memory, + is_64bit_addr, + is_64bit_data, + TF_NONE, + in_exec ? TPF_EXEC_COPY : TPF_NONE, /* Mark the task exec copy if in execve */ + (TRW_LRETURNWAIT | TRW_LRETURNWAITER), /* All created threads will wait in task_wait_to_return */ + &child_task); if (result != KERN_SUCCESS) { - printf("execve: task_create_internal failed. Code: %d\n", result); + printf("%s: task_create_internal failed. Code: %d\n", + __func__, result); goto bad; } - /* Set the child process task to the new task */ - child_proc->task = child_task; + if (!in_exec) { + /* + * Set the child process task to the new task if not in exec, + * will set the task for exec case in proc_exec_switch_task after image activation. + */ + child_proc->task = child_task; + } /* Set child task process to child proc */ set_bsdtask_info(child_task, child_proc); /* Propagate CPU limit timer from parent */ - if (timerisset(&child_proc->p_rlim_cpu)) + if (timerisset(&child_proc->p_rlim_cpu)) { task_vtimer_set(child_task, TASK_VTIMER_RLIM); - - /* Set/clear 64 bit vm_map flag */ - if (is64bit) - vm_map_set_64bit(get_task_map(child_task)); - else - vm_map_set_32bit(get_task_map(child_task)); - -#if CONFIG_MACF - /* Update task for MAC framework */ - /* valid to use p_ucred as child is still not running ... */ - mac_task_label_update_cred(child_proc->p_ucred, child_task); -#endif + } /* * Set child process BSD visible scheduler priority if nice value * inherited from parent */ - if (child_proc->p_nice != 0) + if (child_proc->p_nice != 0) { resetpriority(child_proc); + } + + /* + * Create a new thread for the child process + * The new thread is waiting on the event triggered by 'task_clear_return_wait' + */ + result = thread_create_waiting(child_task, + (thread_continue_t)task_wait_to_return, + task_get_return_wait_event(child_task), + &child_thread); - /* Create a new thread for the child process */ - result = thread_create(child_task, &child_thread); if (result != KERN_SUCCESS) { - printf("execve: thread_create failed. Code: %d\n", result); + printf("%s: thread_create failed. Code: %d\n", + __func__, result); task_deallocate(child_task); child_task = NULL; } + + /* + * Tag thread as being the first thread in its task. + */ + thread_set_tag(child_thread, THREAD_TAG_MAINTHREAD); + bad: thread_yield_internal(1); - return(child_thread); + return child_thread; } @@ -845,9 +894,9 @@ fork(proc_t parent_proc, __unused struct fork_args *uap, int32_t *retval) thread_t child_thread; int err; - retval[1] = 0; /* flag parent return for user space */ + retval[1] = 0; /* flag parent return for user space */ - if ((err = fork1(parent_proc, &child_thread, PROC_CREATE_FORK)) == 0) { + if ((err = fork1(parent_proc, &child_thread, PROC_CREATE_FORK, NULL)) == 0) { task_t child_task; proc_t child_proc; @@ -867,8 +916,14 @@ fork(proc_t parent_proc, __unused struct fork_args *uap, int32_t *retval) proc_knote(parent_proc, NOTE_FORK | child_proc->p_pid); DTRACE_PROC1(create, proc_t, child_proc); +#if CONFIG_DTRACE + if ((dtrace_proc_waitfor_hook = dtrace_proc_waitfor_exec_ptr) != NULL) { + (*dtrace_proc_waitfor_hook)(child_proc); + } +#endif + /* "Return" to the child */ - (void)thread_resume(child_thread); + task_clear_return_wait(get_threadtask(child_thread), TCRW_CLEAR_ALL_WAIT); /* drop the extra references we got during the creation */ if ((child_task = (task_t)get_threadtask(child_thread)) != NULL) { @@ -877,7 +932,7 @@ fork(proc_t parent_proc, __unused struct fork_args *uap, int32_t *retval) thread_deallocate(child_thread); } - return(err); + return err; } @@ -897,6 +952,8 @@ fork(proc_t parent_proc, __unused struct fork_args *uap, int32_t *retval) * memory from the parent; if this is * non-NULL, then the parent_task must * also be non-NULL + * memstat_internal Whether to track the process in the + * jetsam priority list (if configured) * * Returns: !NULL pointer to new child thread * NULL Failure (unspecified) @@ -918,8 +975,11 @@ fork(proc_t parent_proc, __unused struct fork_args *uap, int32_t *retval) * live with this being somewhat awkward. */ thread_t -cloneproc(task_t parent_task, proc_t parent_proc, int inherit_memory) +cloneproc(task_t parent_task, coalition_t *parent_coalitions, proc_t parent_proc, int inherit_memory, int memstat_internal) { +#if !CONFIG_MEMORYSTATUS +#pragma unused(memstat_internal) +#endif task_t child_task; proc_t child_proc; thread_t child_thread = NULL; @@ -929,7 +989,26 @@ cloneproc(task_t parent_task, proc_t parent_proc, int inherit_memory) goto bad; } - child_thread = fork_create_child(parent_task, child_proc, inherit_memory, (parent_task == TASK_NULL) ? FALSE : (parent_proc->p_flag & P_LP64)); + /* + * In the case where the parent_task is TASK_NULL (during the init path) + * we make the assumption that the register size will be the same as the + * address space size since there's no way to determine the possible + * register size until an image is exec'd. + * + * The only architecture that has different address space and register sizes + * (arm64_32) isn't being used within kernel-space, so the above assumption + * always holds true for the init path. + */ + const int parent_64bit_addr = parent_proc->p_flag & P_LP64; + const int parent_64bit_data = (parent_task == TASK_NULL) ? parent_64bit_addr : task_get_64bit_data(parent_task); + + child_thread = fork_create_child(parent_task, + parent_coalitions, + child_proc, + inherit_memory, + parent_64bit_addr, + parent_64bit_data, + FALSE); if (child_thread == NULL) { /* @@ -941,14 +1020,20 @@ cloneproc(task_t parent_task, proc_t parent_proc, int inherit_memory) } child_task = get_threadtask(child_thread); - if (parent_proc->p_flag & P_LP64) { - task_set_64bit(child_task, TRUE); + if (parent_64bit_addr) { OSBitOrAtomic(P_LP64, (UInt32 *)&child_proc->p_flag); } else { - task_set_64bit(child_task, FALSE); OSBitAndAtomic(~((uint32_t)P_LP64), (UInt32 *)&child_proc->p_flag); } +#if CONFIG_MEMORYSTATUS + if (memstat_internal) { + proc_list_lock(); + child_proc->p_memstat_state |= P_MEMSTAT_INTERNAL; + proc_list_unlock(); + } +#endif + /* make child visible */ pinsertchild(parent_proc, child_proc); @@ -957,7 +1042,7 @@ cloneproc(task_t parent_task, proc_t parent_proc, int inherit_memory) */ child_proc->p_stat = SRUN; bad: - return(child_thread); + return child_thread; } @@ -978,6 +1063,13 @@ bad: void forkproc_free(proc_t p) { +#if CONFIG_PERSONAS + persona_proc_drop(p); +#endif /* CONFIG_PERSONAS */ + +#if PSYNCH + pth_proc_hashdelete(p); +#endif /* PSYNCH */ /* We held signal and a transition locks; drop them */ proc_signalend(p, 0); @@ -1016,25 +1108,37 @@ forkproc_free(proc_t p) p->p_textvp = NULL; } - /* Stop the profiling clock */ - stopprofclock(p); - /* Update the audit session proc count */ AUDIT_SESSION_PROCEXIT(p); + lck_mtx_destroy(&p->p_mlock, proc_mlock_grp); + lck_mtx_destroy(&p->p_fdmlock, proc_fdmlock_grp); + lck_mtx_destroy(&p->p_ucred_mlock, proc_ucred_mlock_grp); +#if CONFIG_DTRACE + lck_mtx_destroy(&p->p_dtrace_sprlock, proc_lck_grp); +#endif + lck_spin_destroy(&p->p_slock, proc_slock_grp); + /* Release the credential reference */ kauth_cred_unref(&p->p_ucred); proc_list_lock(); /* Decrement the count of processes in the system */ nprocs--; + + /* Take it out of process hash */ + LIST_REMOVE(p, p_hash); + proc_list_unlock(); thread_call_free(p->p_rcall); /* Free allocated memory */ FREE_ZONE(p->p_sigacts, sizeof *p->p_sigacts, M_SIGACTS); + p->p_sigacts = NULL; FREE_ZONE(p->p_stats, sizeof *p->p_stats, M_PSTATS); + p->p_stats = NULL; + proc_checkdeadrefs(p); FREE_ZONE(p, sizeof *p, M_PROC); } @@ -1058,14 +1162,14 @@ forkproc_free(proc_t p) proc_t forkproc(proc_t parent_proc) { - proc_t child_proc; /* Our new process */ - static int nextpid = 0, pidwrap = 0, nextpidversion = 0; + proc_t child_proc; /* Our new process */ + static int nextpid = 0, pidwrap = 0; static uint64_t nextuniqueid = 0; int error = 0; struct session *sessp; uthread_t parent_uthread = (uthread_t)get_bsdthread_info(current_thread()); - MALLOC_ZONE(child_proc, proc_t , sizeof *child_proc, M_PROC, M_WAITOK); + MALLOC_ZONE(child_proc, proc_t, sizeof *child_proc, M_PROC, M_WAITOK); if (child_proc == NULL) { printf("forkproc: M_PROC zone exhausted\n"); goto bad; @@ -1074,7 +1178,7 @@ forkproc(proc_t parent_proc) bzero(child_proc, sizeof *child_proc); MALLOC_ZONE(child_proc->p_stats, struct pstats *, - sizeof *child_proc->p_stats, M_PSTATS, M_WAITOK); + sizeof *child_proc->p_stats, M_PSTATS, M_WAITOK); if (child_proc->p_stats == NULL) { printf("forkproc: M_SUBPROC zone exhausted (p_stats)\n"); FREE_ZONE(child_proc, sizeof *child_proc, M_PROC); @@ -1082,10 +1186,11 @@ forkproc(proc_t parent_proc) goto bad; } MALLOC_ZONE(child_proc->p_sigacts, struct sigacts *, - sizeof *child_proc->p_sigacts, M_SIGACTS, M_WAITOK); + sizeof *child_proc->p_sigacts, M_SIGACTS, M_WAITOK); if (child_proc->p_sigacts == NULL) { printf("forkproc: M_SUBPROC zone exhausted (p_sigacts)\n"); FREE_ZONE(child_proc->p_stats, sizeof *child_proc->p_stats, M_PSTATS); + child_proc->p_stats = NULL; FREE_ZONE(child_proc, sizeof *child_proc, M_PROC); child_proc = NULL; goto bad; @@ -1095,7 +1200,9 @@ forkproc(proc_t parent_proc) child_proc->p_rcall = thread_call_allocate((thread_call_func_t)realitexpire, child_proc); if (child_proc->p_rcall == NULL) { FREE_ZONE(child_proc->p_sigacts, sizeof *child_proc->p_sigacts, M_SIGACTS); + child_proc->p_sigacts = NULL; FREE_ZONE(child_proc->p_stats, sizeof *child_proc->p_stats, M_PSTATS); + child_proc->p_stats = NULL; FREE_ZONE(child_proc, sizeof *child_proc, M_PROC); child_proc = NULL; goto bad; @@ -1103,7 +1210,7 @@ forkproc(proc_t parent_proc) /* - * Find an unused PID. + * Find an unused PID. */ proc_list_lock(); @@ -1120,9 +1227,8 @@ retry: pidwrap = 1; } if (pidwrap != 0) { - /* if the pid stays in hash both for zombie and runniing state */ - if (pfind_locked(nextpid) != PROC_NULL) { + if (pfind_locked(nextpid) != PROC_NULL) { nextpid++; goto retry; } @@ -1130,21 +1236,22 @@ retry: if (pgfind_internal(nextpid) != PGRP_NULL) { nextpid++; goto retry; - } + } if (session_find_internal(nextpid) != SESSION_NULL) { nextpid++; goto retry; - } + } } nprocs++; child_proc->p_pid = nextpid; - child_proc->p_idversion = nextpidversion++; + child_proc->p_idversion = OSIncrementAtomic(&nextpidversion); /* kernel process is handcrafted and not from fork, so start from 1 */ child_proc->p_uniqueid = ++nextuniqueid; #if 1 if (child_proc->p_pid != 0) { - if (pfind_locked(child_proc->p_pid) != PROC_NULL) + if (pfind_locked(child_proc->p_pid) != PROC_NULL) { panic("proc in the list already\n"); + } } #endif /* Insert in the hash */ @@ -1152,6 +1259,13 @@ retry: LIST_INSERT_HEAD(PIDHASH(child_proc->p_pid), child_proc, p_hash); proc_list_unlock(); + if (child_proc->p_uniqueid == startup_serial_num_procs) { + /* + * Turn off startup serial logging now that we have reached + * the defined number of startup processes. + */ + startup_serial_logging_active = false; + } /* * We've identified the PID we are going to use; initialize the new @@ -1165,7 +1279,7 @@ retry: * for insertion to hash. Copy the section that is to be copied * directly from the parent. */ - bcopy(&parent_proc->p_startcopy, &child_proc->p_startcopy, + __nochk_bcopy(&parent_proc->p_startcopy, &child_proc->p_startcopy, (unsigned) ((caddr_t)&child_proc->p_endcopy - (caddr_t)&child_proc->p_startcopy)); /* @@ -1174,9 +1288,16 @@ retry: * Increase reference counts on shared objects. * The p_stats and p_sigacts substructs are set in vm_fork. */ - child_proc->p_flag = (parent_proc->p_flag & (P_LP64 | P_TRANSLATED | P_AFFINITY | P_DISABLE_ASLR)); - if (parent_proc->p_flag & P_PROFIL) - startprofclock(child_proc); +#if !CONFIG_EMBEDDED + child_proc->p_flag = (parent_proc->p_flag & (P_LP64 | P_DISABLE_ASLR | P_DELAYIDLESLEEP | P_SUGID)); +#else /* !CONFIG_EMBEDDED */ + child_proc->p_flag = (parent_proc->p_flag & (P_LP64 | P_DISABLE_ASLR | P_SUGID)); +#endif /* !CONFIG_EMBEDDED */ + + child_proc->p_vfs_iopolicy = (parent_proc->p_vfs_iopolicy & (P_VFS_IOPOLICY_VALID_MASK)); + + child_proc->p_responsible_pid = parent_proc->p_responsible_pid; + /* * Note that if the current thread has an assumed identity, this * credential will be granted to the new process. @@ -1187,31 +1308,26 @@ retry: /* update audit session proc count */ AUDIT_SESSION_PROCNEW(child_proc); -#if CONFIG_FINE_LOCK_GROUPS lck_mtx_init(&child_proc->p_mlock, proc_mlock_grp, proc_lck_attr); lck_mtx_init(&child_proc->p_fdmlock, proc_fdmlock_grp, proc_lck_attr); + lck_mtx_init(&child_proc->p_ucred_mlock, proc_ucred_mlock_grp, proc_lck_attr); #if CONFIG_DTRACE lck_mtx_init(&child_proc->p_dtrace_sprlock, proc_lck_grp, proc_lck_attr); #endif lck_spin_init(&child_proc->p_slock, proc_slock_grp, proc_lck_attr); -#else /* !CONFIG_FINE_LOCK_GROUPS */ - lck_mtx_init(&child_proc->p_mlock, proc_lck_grp, proc_lck_attr); - lck_mtx_init(&child_proc->p_fdmlock, proc_lck_grp, proc_lck_attr); -#if CONFIG_DTRACE - lck_mtx_init(&child_proc->p_dtrace_sprlock, proc_lck_grp, proc_lck_attr); -#endif - lck_spin_init(&child_proc->p_slock, proc_lck_grp, proc_lck_attr); -#endif /* !CONFIG_FINE_LOCK_GROUPS */ + klist_init(&child_proc->p_klist); if (child_proc->p_textvp != NULLVP) { /* bump references to the text vnode */ /* Need to hold iocount across the ref call */ - if (vnode_getwithref(child_proc->p_textvp) == 0) { + if ((error = vnode_getwithref(child_proc->p_textvp)) == 0) { error = vnode_ref(child_proc->p_textvp); vnode_put(child_proc->p_textvp); - if (error != 0) - child_proc->p_textvp = NULLVP; + } + + if (error != 0) { + child_proc->p_textvp = NULLVP; } } @@ -1243,22 +1359,20 @@ retry: /* Intialize new process stats, including start time */ /* non-zeroed portion contains garbage AFAICT */ - bzero(&child_proc->p_stats->pstat_startzero, - (unsigned) ((caddr_t)&child_proc->p_stats->pstat_endzero - - (caddr_t)&child_proc->p_stats->pstat_startzero)); - bzero(&child_proc->p_stats->user_p_prof, sizeof(struct user_uprof)); - microtime(&child_proc->p_start); - child_proc->p_stats->p_start = child_proc->p_start; /* for compat */ - - if (parent_proc->p_sigacts != NULL) + bzero(child_proc->p_stats, sizeof(*child_proc->p_stats)); + microtime_with_abstime(&child_proc->p_start, &child_proc->p_stats->ps_start); + + if (parent_proc->p_sigacts != NULL) { (void)memcpy(child_proc->p_sigacts, - parent_proc->p_sigacts, sizeof *child_proc->p_sigacts); - else + parent_proc->p_sigacts, sizeof *child_proc->p_sigacts); + } else { (void)memset(child_proc->p_sigacts, 0, sizeof *child_proc->p_sigacts); + } sessp = proc_session(parent_proc); - if (sessp->s_ttyvp != NULL && parent_proc->p_flag & P_CONTROLT) + if (sessp->s_ttyvp != NULL && parent_proc->p_flag & P_CONTROLT) { OSBitOrAtomic(P_CONTROLT, &child_proc->p_flag); + } session_rele(sessp); /* @@ -1267,9 +1381,10 @@ retry: * but indicate that the process is in (the creation) transition. */ proc_signalstart(child_proc, 0); - proc_transstart(child_proc, 0); + proc_transstart(child_proc, 0, 0); + + child_proc->p_pcaction = 0; - child_proc->p_pcaction = (parent_proc->p_pcaction) & P_PCMAX; TAILQ_INIT(&child_proc->p_uthlist); TAILQ_INIT(&child_proc->p_aio_activeq); TAILQ_INIT(&child_proc->p_aio_doneq); @@ -1277,12 +1392,6 @@ retry: /* Inherit the parent flags for code sign */ child_proc->p_csflags = (parent_proc->p_csflags & ~CS_KILLED); - /* - * All processes have work queue locks; cleaned up by - * reap_child_locked() - */ - workqueue_init_lock(child_proc); - /* * Copy work queue information * @@ -1297,35 +1406,58 @@ retry: child_proc->p_wqthread = parent_proc->p_wqthread; child_proc->p_threadstart = parent_proc->p_threadstart; child_proc->p_pthsize = parent_proc->p_pthsize; - child_proc->p_targconc = parent_proc->p_targconc; if ((parent_proc->p_lflag & P_LREGISTER) != 0) { child_proc->p_lflag |= P_LREGISTER; } child_proc->p_dispatchqueue_offset = parent_proc->p_dispatchqueue_offset; + child_proc->p_dispatchqueue_serialno_offset = parent_proc->p_dispatchqueue_serialno_offset; + child_proc->p_dispatchqueue_label_offset = parent_proc->p_dispatchqueue_label_offset; + child_proc->p_return_to_kernel_offset = parent_proc->p_return_to_kernel_offset; + child_proc->p_mach_thread_self_offset = parent_proc->p_mach_thread_self_offset; + child_proc->p_pth_tsd_offset = parent_proc->p_pth_tsd_offset; #if PSYNCH pth_proc_hashinit(child_proc); #endif /* PSYNCH */ -#if CONFIG_LCTX - child_proc->p_lctx = NULL; - /* Add new process to login context (if any). */ - if (parent_proc->p_lctx != NULL) { - /* - * This should probably be delayed in the - * vfork() or posix_spawn() cases. - */ - LCTX_LOCK(parent_proc->p_lctx); - enterlctx(child_proc, parent_proc->p_lctx, 0); +#if CONFIG_PERSONAS + child_proc->p_persona = NULL; + error = persona_proc_inherit(child_proc, parent_proc); + if (error != 0) { + printf("forkproc: persona_proc_inherit failed (persona %d being destroyed?)\n", persona_get_uid(parent_proc->p_persona)); + forkproc_free(child_proc); + child_proc = NULL; + goto bad; } #endif +#if CONFIG_MEMORYSTATUS + /* Memorystatus init */ + child_proc->p_memstat_state = 0; + child_proc->p_memstat_effectivepriority = JETSAM_PRIORITY_DEFAULT; + child_proc->p_memstat_requestedpriority = JETSAM_PRIORITY_DEFAULT; + child_proc->p_memstat_assertionpriority = 0; + child_proc->p_memstat_userdata = 0; + child_proc->p_memstat_idle_start = 0; + child_proc->p_memstat_idle_delta = 0; + child_proc->p_memstat_memlimit = 0; + child_proc->p_memstat_memlimit_active = 0; + child_proc->p_memstat_memlimit_inactive = 0; + child_proc->p_memstat_relaunch_flags = P_MEMSTAT_RELAUNCH_UNKNOWN; +#if CONFIG_FREEZE + child_proc->p_memstat_freeze_sharedanon_pages = 0; +#endif + child_proc->p_memstat_dirty = 0; + child_proc->p_memstat_idledeadline = 0; +#endif /* CONFIG_MEMORYSTATUS */ + bad: - return(child_proc); + return child_proc; } void proc_lock(proc_t p) { + LCK_MTX_ASSERT(proc_list_mlock, LCK_MTX_ASSERT_NOTOWNED); lck_mtx_lock(&p->p_mlock); } @@ -1338,7 +1470,7 @@ proc_unlock(proc_t p) void proc_spinlock(proc_t p) { - lck_spin_lock(&p->p_slock); + lck_spin_lock_grp(&p->p_slock, proc_slock_grp); } void @@ -1347,33 +1479,51 @@ proc_spinunlock(proc_t p) lck_spin_unlock(&p->p_slock); } -void +void proc_list_lock(void) { lck_mtx_lock(proc_list_mlock); } -void +void proc_list_unlock(void) { lck_mtx_unlock(proc_list_mlock); } +void +proc_ucred_lock(proc_t p) +{ + lck_mtx_lock(&p->p_ucred_mlock); +} + +void +proc_ucred_unlock(proc_t p) +{ + lck_mtx_unlock(&p->p_ucred_mlock); +} + #include -struct zone *uthread_zone; -static int uthread_zone_inited = 0; +struct zone *uthread_zone = NULL; + +static lck_grp_t *rethrottle_lock_grp; +static lck_attr_t *rethrottle_lock_attr; +static lck_grp_attr_t *rethrottle_lock_grp_attr; static void uthread_zone_init(void) { - if (!uthread_zone_inited) { - uthread_zone = zinit(sizeof(struct uthread), - thread_max * sizeof(struct uthread), - THREAD_CHUNK * sizeof(struct uthread), - "uthreads"); - uthread_zone_inited = 1; - } + assert(uthread_zone == NULL); + + rethrottle_lock_grp_attr = lck_grp_attr_alloc_init(); + rethrottle_lock_grp = lck_grp_alloc_init("rethrottle", rethrottle_lock_grp_attr); + rethrottle_lock_attr = lck_attr_alloc_init(); + + uthread_zone = zinit(sizeof(struct uthread), + thread_max * sizeof(struct uthread), + THREAD_CHUNK * sizeof(struct uthread), + "uthreads"); } void * @@ -1384,15 +1534,19 @@ uthread_alloc(task_t task, thread_t thread, int noinherit) uthread_t uth_parent; void *ut; - if (!uthread_zone_inited) + if (uthread_zone == NULL) { uthread_zone_init(); + } ut = (void *)zalloc(uthread_zone); bzero(ut, sizeof(struct uthread)); p = (proc_t) get_bsdtask_info(task); uth = (uthread_t)ut; - uth->uu_kwe.kwe_uth = uth; + uth->uu_thread = thread; + + lck_spin_init(&uth->uu_rethrottle_lock, rethrottle_lock_grp, + rethrottle_lock_attr); /* * Thread inherits credential from the creating thread, if both @@ -1403,7 +1557,7 @@ uthread_alloc(task_t task, thread_t thread, int noinherit) * one later, it will be lazily assigned from the task's process. */ uth_parent = (uthread_t)get_bsdthread_info(current_thread()); - if ((noinherit == 0) && task == current_task() && + if ((noinherit == 0) && task == current_task() && uth_parent != NULL && IS_VALID_CRED(uth_parent->uu_ucred)) { /* @@ -1414,45 +1568,78 @@ uthread_alloc(task_t task, thread_t thread, int noinherit) kauth_cred_ref(uth_parent->uu_ucred); uth->uu_ucred = uth_parent->uu_ucred; /* the credential we just inherited is an assumed credential */ - if (uth_parent->uu_flag & UT_SETUID) + if (uth_parent->uu_flag & UT_SETUID) { uth->uu_flag |= UT_SETUID; + } } else { /* sometimes workqueue threads are created out task context */ - if ((task != kernel_task) && (p != PROC_NULL)) + if ((task != kernel_task) && (p != PROC_NULL)) { uth->uu_ucred = kauth_cred_proc_ref(p); - else + } else { uth->uu_ucred = NOCRED; + } } - + if ((task != kernel_task) && p) { - proc_lock(p); if (noinherit != 0) { /* workq threads will not inherit masks */ uth->uu_sigmask = ~workq_threadmask; } else if (uth_parent) { - if (uth_parent->uu_flag & UT_SAS_OLDMASK) + if (uth_parent->uu_flag & UT_SAS_OLDMASK) { uth->uu_sigmask = uth_parent->uu_oldmask; - else + } else { uth->uu_sigmask = uth_parent->uu_sigmask; + } } uth->uu_context.vc_thread = thread; - TAILQ_INSERT_TAIL(&p->p_uthlist, uth, uu_list); + /* + * Do not add the uthread to proc uthlist for exec copy task, + * since they do not hold a ref on proc. + */ + if (!task_is_exec_copy(task)) { + TAILQ_INSERT_TAIL(&p->p_uthlist, uth, uu_list); + } proc_unlock(p); #if CONFIG_DTRACE - if (p->p_dtrace_ptss_pages != NULL) { + if (p->p_dtrace_ptss_pages != NULL && !task_is_exec_copy(task)) { uth->t_dtrace_scratch = dtrace_ptss_claim_entry(p); } #endif } - return (ut); + return ut; } +/* + * This routine frees the thread name field of the uthread_t structure. Split out of + * uthread_cleanup() so thread name does not get deallocated while generating a corpse fork. + */ +void +uthread_cleanup_name(void *uthread) +{ + uthread_t uth = (uthread_t)uthread; + + /* + * + * Set pth_name to NULL before calling free(). + * Previously there was a race condition in the + * case this code was executing during a stackshot + * where the stackshot could try and copy pth_name + * after it had been freed and before if was marked + * as null. + */ + if (uth->pth_name != NULL) { + void *pth_name = uth->pth_name; + uth->pth_name = NULL; + kfree(pth_name, MAXTHREADNAMESIZE); + } + return; +} -/* +/* * This routine frees all the BSD context in uthread except the credential. * It does not free the uthread structure as well */ @@ -1463,9 +1650,14 @@ uthread_cleanup(task_t task, void *uthread, void * bsd_info) uthread_t uth = (uthread_t)uthread; proc_t p = (proc_t)bsd_info; +#if PROC_REF_DEBUG + if (__improbable(uthread_get_proc_refcount(uthread) != 0)) { + panic("uthread_cleanup called for uthread %p with uu_proc_refcount != 0", uthread); + } +#endif if (uth->uu_lowpri_window || uth->uu_throttle_info) { - /* + /* * task is marked as a low priority I/O type * and we've somehow managed to not dismiss the throttle * through the normal exit paths back to user space... @@ -1475,7 +1667,7 @@ uthread_cleanup(task_t task, void *uthread, void * bsd_info) * Calling this routine will clean up any throttle info reference * still inuse by the thread. */ - throttle_lowpri_io(FALSE); + throttle_lowpri_io(0); } /* * Per-thread audit state should never last beyond system @@ -1485,6 +1677,10 @@ uthread_cleanup(task_t task, void *uthread, void * bsd_info) */ assert(uth->uu_ar == NULL); + if (uth->uu_kqr_bound) { + kqueue_threadreq_unbind(p, uth->uu_kqr_bound); + } + sel = &uth->uu_select; /* cleanup the select bit space */ if (sel->nbytes) { @@ -1498,30 +1694,31 @@ uthread_cleanup(task_t task, void *uthread, void * bsd_info) uth->uu_cdir = NULLVP; } - if (uth->uu_allocsize && uth->uu_wqset){ - kfree(uth->uu_wqset, uth->uu_allocsize); - sel->count = 0; - uth->uu_allocsize = 0; - uth->uu_wqset = 0; - sel->wql = 0; + if (uth->uu_wqset) { + if (waitq_set_is_valid(uth->uu_wqset)) { + waitq_set_deinit(uth->uu_wqset); + } + FREE(uth->uu_wqset, M_SELECT); + uth->uu_wqset = NULL; + uth->uu_wqstate_sz = 0; } - if(uth->pth_name != NULL) - { - kfree(uth->pth_name, MAXTHREADNAMESIZE); - uth->pth_name = 0; - } - if ((task != kernel_task) && p) { + os_reason_free(uth->uu_exit_reason); - if (((uth->uu_flag & UT_VFORK) == UT_VFORK) && (uth->uu_proc != PROC_NULL)) { + if ((task != kernel_task) && p) { + if (((uth->uu_flag & UT_VFORK) == UT_VFORK) && (uth->uu_proc != PROC_NULL)) { vfork_exit_internal(uth->uu_proc, 0, 1); } /* * Remove the thread from the process list and * transfer [appropriate] pending signals to the process. + * Do not remove the uthread from proc uthlist for exec + * copy task, since they does not have a ref on proc and + * would not have been added to the list. */ - if (get_bsdtask_info(task) == p) { + if (get_bsdtask_info(task) == p && !task_is_exec_copy(task)) { proc_lock(p); + TAILQ_REMOVE(&p->p_uthlist, uth, uu_list); p->p_siglist |= (uth->uu_siglist & execmask & (~p->p_sigignore | sigcantmask)); proc_unlock(p); @@ -1529,7 +1726,7 @@ uthread_cleanup(task_t task, void *uthread, void * bsd_info) #if CONFIG_DTRACE struct dtrace_ptss_page_entry *tmpptr = uth->t_dtrace_scratch; uth->t_dtrace_scratch = NULL; - if (tmpptr != NULL) { + if (tmpptr != NULL && !task_is_exec_copy(task)) { dtrace_ptss_release_entry(p, tmpptr); } #endif @@ -1554,6 +1751,16 @@ uthread_cred_free(void *uthread) void uthread_zone_free(void *uthread) { + uthread_t uth = (uthread_t)uthread; + + if (uth->t_tombstone) { + kfree(uth->t_tombstone, sizeof(struct doc_tombstone)); + uth->t_tombstone = NULL; + } + + lck_spin_destroy(&uth->uu_rethrottle_lock, rethrottle_lock_grp); + + uthread_cleanup_name(uthread); /* and free the uthread itself */ zfree(uthread_zone, uthread); }