X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/b0d623f7f2ae71ed96e60569f61f9a9a27016e80..c7d2c2c6ee645e10cbccdd01c6191873ec77239d:/bsd/kern/kern_fork.c diff --git a/bsd/kern/kern_fork.c b/bsd/kern/kern_fork.c index 666748b52..7b3e2440e 100644 --- a/bsd/kern/kern_fork.c +++ b/bsd/kern/kern_fork.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2007 Apple Inc. All rights reserved. + * Copyright (c) 2000-2007, 2015 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -93,22 +93,32 @@ #include #include #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_proc_waitfor_exec_ptr)(proc_t); extern void dtrace_lazy_dofs_duplicate(proc_t, proc_t); +/* + * 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 #include #include +#include #include #include #include #include +#include #include #include @@ -129,6 +139,9 @@ extern void dtrace_lazy_dofs_duplicate(proc_t, proc_t); #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); @@ -137,10 +150,10 @@ void thread_set_child(thread_t child, int pid); void *act_thread_csave(void); -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 is64bit); void proc_vfork_begin(proc_t parent_proc); void proc_vfork_end(proc_t parent_proc); @@ -159,8 +172,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) @@ -180,7 +193,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 @@ -190,7 +203,6 @@ proc_vfork_end(proc_t parent_proc) parent_proc->p_vforkcnt--; 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) parent_proc->p_lflag &= ~P_LVFORK; proc_unlock(parent_proc); @@ -275,15 +287,11 @@ 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 */ @@ -296,12 +304,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); } @@ -316,6 +324,12 @@ vfork(proc_t parent_proc, __unused struct vfork_args *uap, int32_t *retval) * Mach thread_t of the child process * breated * 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': * @@ -347,7 +361,7 @@ 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); @@ -365,7 +379,7 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) * exceed the limit. The variable nprocs is the current number of * processes, maxproc is the limit. */ - uid = kauth_cred_get()->cr_ruid; + uid = kauth_getruid(); proc_list_lock(); if ((nprocs >= maxproc - 1 && uid != 0) || nprocs >= maxproc) { proc_list_unlock(); @@ -380,6 +394,7 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) * always less than what an rlim_t can hold. * (locking protection is provided by list lock held in chgproccnt) */ + count = chgproccnt(uid, 1); if (uid != 0 && (rlim_t)count > parent_proc->p_rlimit[RLIMIT_NPROC].rlim_cur) { @@ -466,7 +481,6 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) AUDIT_ARG(pid, child_proc->p_pid); - AUDIT_SESSION_PROCNEW(child_proc->p_ucred); // XXX END: wants to move to be common code (and safe) /* @@ -482,7 +496,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; @@ -527,7 +554,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; @@ -570,7 +601,6 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind) AUDIT_ARG(pid, child_proc->p_pid); - AUDIT_SESSION_PROCNEW(child_proc->p_ucred); // XXX END: wants to move to be common code (and safe) /* @@ -674,49 +704,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); + 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); - act_thread_catt(parent_uthread->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 */ } - - return; } @@ -727,6 +761,7 @@ vfork_return(proc_t child_proc, int32_t *retval, int rval) * process * * Parameters: parent_task parent task + * 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 @@ -740,17 +775,18 @@ 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 is64bit) { thread_t child_thread = NULL; task_t child_task; @@ -758,11 +794,13 @@ fork_create_child(task_t parent_task, proc_t child_proc, int inherit_memory, int /* Create a new task for the child process */ result = task_create_internal(parent_task, + parent_coalitions, inherit_memory, is64bit, &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; } @@ -782,12 +820,6 @@ fork_create_child(task_t parent_task, proc_t child_proc, int inherit_memory, int 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 @@ -796,12 +828,19 @@ fork_create_child(task_t parent_task, proc_t child_proc, int inherit_memory, int resetpriority(child_proc); /* Create a new thread for the child process */ - result = thread_create(child_task, &child_thread); + result = thread_create_with_continuation(child_task, &child_thread, (thread_continue_t)proc_wait_to_return); 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); @@ -851,7 +890,7 @@ fork(proc_t parent_proc, __unused struct fork_args *uap, int32_t *retval) 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; @@ -871,8 +910,13 @@ 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); + proc_clear_return_wait(child_proc, child_thread); /* drop the extra references we got during the creation */ if ((child_task = (task_t)get_threadtask(child_thread)) != NULL) { @@ -901,6 +945,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) @@ -922,8 +968,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; @@ -933,7 +982,7 @@ 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)); + child_thread = fork_create_child(parent_task, parent_coalitions, child_proc, inherit_memory, (parent_task == TASK_NULL) ? FALSE : (parent_proc->p_flag & P_LP64)); if (child_thread == NULL) { /* @@ -948,19 +997,19 @@ cloneproc(task_t parent_task, proc_t parent_proc, int inherit_memory) if (parent_proc->p_flag & P_LP64) { task_set_64bit(child_task, TRUE); OSBitOrAtomic(P_LP64, (UInt32 *)&child_proc->p_flag); -#ifdef __ppc__ - /* - * PPC51: ppc64 is limited to 51-bit addresses. - * Memory above that limit is handled specially at - * the pmap level. - */ - pmap_map_sharedpage(child_task, get_map_pmap(get_task_map(child_task))); -#endif /* __ppc__ */ } 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); @@ -1031,6 +1080,9 @@ forkproc_free(proc_t p) /* Stop the profiling clock */ stopprofclock(p); + /* Update the audit session proc count */ + AUDIT_SESSION_PROCEXIT(p); + /* Release the credential reference */ kauth_cred_unref(&p->p_ucred); @@ -1069,6 +1121,7 @@ forkproc(proc_t parent_proc) { proc_t child_proc; /* Our new process */ static int nextpid = 0, pidwrap = 0, nextpidversion = 0; + static uint64_t nextuniqueid = 0; int error = 0; struct session *sessp; uthread_t parent_uthread = (uthread_t)get_bsdthread_info(current_thread()); @@ -1146,7 +1199,10 @@ retry: } nprocs++; child_proc->p_pid = nextpid; + child_proc->p_responsible_pid = nextpid; /* initially responsible for self */ child_proc->p_idversion = 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) @@ -1180,30 +1236,39 @@ 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)); + child_proc->p_flag = (parent_proc->p_flag & (P_LP64 | P_DISABLE_ASLR | P_DELAYIDLESLEEP | P_SUGID)); if (parent_proc->p_flag & P_PROFIL) startprofclock(child_proc); + + child_proc->p_vfs_iopolicy = (parent_proc->p_vfs_iopolicy & (P_VFS_IOPOLICY_FORCE_HFS_CASE_SENSITIVITY)); + /* * Note that if the current thread has an assumed identity, this * credential will be granted to the new process. */ child_proc->p_ucred = kauth_cred_get_with_ref(); + /* update cred on proc */ + PROC_UPDATE_CREDS_ONPROC(child_proc); + /* update audit session proc count */ + AUDIT_SESSION_PROCNEW(child_proc); -#ifdef CONFIG_EMBEDDED - 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); -#else /* !CONFIG_EMBEDDED */ +#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); -#endif /* !CONFIG_EMBEDDED */ +#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); + lck_mtx_init(&child_proc->p_ucred_mlock, 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) { @@ -1245,12 +1310,8 @@ 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 */ + 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, @@ -1269,15 +1330,17 @@ 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); + proc_set_return_wait(child_proc); + + 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); /* Inherit the parent flags for code sign */ - child_proc->p_csflags = parent_proc->p_csflags; + child_proc->p_csflags = (parent_proc->p_csflags & ~CS_KILLED); /* * All processes have work queue locks; cleaned up by @@ -1303,23 +1366,25 @@ retry: if ((parent_proc->p_lflag & P_LREGISTER) != 0) { child_proc->p_lflag |= P_LREGISTER; } + child_proc->p_wqkqueue = NULL; child_proc->p_dispatchqueue_offset = parent_proc->p_dispatchqueue_offset; + child_proc->p_dispatchqueue_serialno_offset = parent_proc->p_dispatchqueue_serialno_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_MEMORYSTATUS + /* Memorystatus + jetsam 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_userdata = 0; +#if CONFIG_FREEZE + child_proc->p_memstat_suspendedfootprint = 0; #endif + child_proc->p_memstat_dirty = 0; + child_proc->p_memstat_idledeadline = 0; +#endif /* CONFIG_MEMORYSTATUS */ bad: return(child_proc); @@ -1328,6 +1393,7 @@ bad: void proc_lock(proc_t p) { + lck_mtx_assert(proc_list_mlock, LCK_MTX_ASSERT_NOTOWNED); lck_mtx_lock(&p->p_mlock); } @@ -1361,6 +1427,18 @@ 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; @@ -1394,6 +1472,7 @@ uthread_alloc(task_t task, thread_t thread, int noinherit) p = (proc_t) get_bsdtask_info(task); uth = (uthread_t)ut; + uth->uu_thread = thread; /* * Thread inherits credential from the creating thread, if both @@ -1452,21 +1531,53 @@ uthread_alloc(task_t task, thread_t thread, int noinherit) return (ut); } +/* + * This routine frees the thread name field of the uthread_t structure. Split out of + * uthread_cleanup() so it can be called separately on the threads of a corpse after + * the corpse notification has been sent, and the handler has had a chance to extract + * the thread names. + */ +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 */ void -uthread_cleanup(task_t task, void *uthread, void * bsd_info) +uthread_cleanup(task_t task, void *uthread, void * bsd_info, boolean_t is_corpse) { struct _select *sel; 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... @@ -1476,7 +1587,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 @@ -1499,19 +1610,22 @@ 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; + /* + * defer the removal of the thread name on process corpses until the corpse has + * been autopsied. + */ + if (!is_corpse) { + uthread_cleanup_name(uth); } + if ((task != kernel_task) && p) { if (((uth->uu_flag & UT_VFORK) == UT_VFORK) && (uth->uu_proc != PROC_NULL)) { @@ -1555,6 +1669,13 @@ 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; + } + /* and free the uthread itself */ zfree(uthread_zone, uthread); }