X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/3903760236c30e3b5ace7a4eefac3a269d68957c..a39ff7e25e19b3a8c3020042a3872ca9ec9659f1:/bsd/kern/kern_fork.c diff --git a/bsd/kern/kern_fork.c b/bsd/kern/kern_fork.c index 78e50cc5e..952b6f8fb 100644 --- a/bsd/kern/kern_fork.c +++ b/bsd/kern/kern_fork.c @@ -124,10 +124,12 @@ static void (*dtrace_proc_waitfor_hook)(proc_t) = NULL; #include #include -#include +#include + +#include #if CONFIG_MACF -#include +#include #include #endif @@ -150,12 +152,13 @@ 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); 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, coalition_t *parent_coalitions, 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, int in_exec); void proc_vfork_begin(proc_t parent_proc); void proc_vfork_end(proc_t parent_proc); @@ -324,7 +327,7 @@ 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 @@ -384,6 +387,14 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind, coalition_t *coalit 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); @@ -399,6 +410,15 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind, coalition_t *coalit count = chgproccnt(uid, 1); if (uid != 0 && (rlim_t)count > parent_proc->p_rlimit[RLIMIT_NPROC].rlim_cur) { +#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; } @@ -724,6 +744,8 @@ vfork_return(proc_t child_proc, int32_t *retval, int rval) * is64bit TRUE, if the child being created will * be associated with a 64 bit process * rather than a 32 bit process + * 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 @@ -742,7 +764,7 @@ vfork_return(proc_t child_proc, int32_t *retval, int rval) * in this case, 'inherit_memory' MUST be FALSE. */ thread_t -fork_create_child(task_t parent_task, coalition_t *parent_coalitions, 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, int in_exec) { thread_t child_thread = NULL; task_t child_task; @@ -753,7 +775,8 @@ fork_create_child(task_t parent_task, coalition_t *parent_coalitions, proc_t chi parent_coalitions, inherit_memory, is64bit, - TF_NONE, + TF_LRETURNWAIT | TF_LRETURNWAITER, /* All created threads will wait in task_wait_to_return */ + in_exec ? TPF_EXEC_COPY : TPF_NONE, /* Mark the task exec copy if in execve */ &child_task); if (result != KERN_SUCCESS) { printf("%s: task_create_internal failed. Code: %d\n", @@ -761,8 +784,13 @@ fork_create_child(task_t parent_task, coalition_t *parent_coalitions, proc_t chi 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); @@ -771,12 +799,6 @@ fork_create_child(task_t parent_task, coalition_t *parent_coalitions, proc_t chi 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)); - /* * Set child process BSD visible scheduler priority if nice value * inherited from parent @@ -784,8 +806,15 @@ fork_create_child(task_t parent_task, coalition_t *parent_coalitions, proc_t chi if (child_proc->p_nice != 0) resetpriority(child_proc); - /* Create a new thread for the child process */ - result = thread_create_with_continuation(child_task, &child_thread, (thread_continue_t)proc_wait_to_return); + /* + * 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); + if (result != KERN_SUCCESS) { printf("%s: thread_create failed. Code: %d\n", __func__, result); @@ -873,7 +902,7 @@ fork(proc_t parent_proc, __unused struct fork_args *uap, int32_t *retval) #endif /* "Return" to the child */ - proc_clear_return_wait(child_proc, child_thread); + task_clear_return_wait(get_threadtask(child_thread)); /* drop the extra references we got during the creation */ if ((child_task = (task_t)get_threadtask(child_thread)) != NULL) { @@ -939,7 +968,7 @@ cloneproc(task_t parent_task, coalition_t *parent_coalitions, proc_t parent_proc goto bad; } - child_thread = fork_create_child(parent_task, parent_coalitions, child_proc, inherit_memory, parent_proc->p_flag & P_LP64); + child_thread = fork_create_child(parent_task, parent_coalitions, child_proc, inherit_memory, parent_proc->p_flag & P_LP64, FALSE); if (child_thread == NULL) { /* @@ -1200,6 +1229,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 @@ -1222,7 +1258,11 @@ retry: * Increase reference counts on shared objects. * The p_stats and p_sigacts substructs are set in vm_fork. */ +#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 */ if (parent_proc->p_flag & P_PROFIL) startprofclock(child_proc); @@ -1317,7 +1357,6 @@ retry: */ proc_signalstart(child_proc, 0); proc_transstart(child_proc, 0, 0); - proc_set_return_wait(child_proc); child_proc->p_pcaction = 0; @@ -1345,9 +1384,11 @@ 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; + 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 */ @@ -1388,7 +1429,7 @@ bad: void proc_lock(proc_t p) { - lck_mtx_assert(proc_list_mlock, LCK_MTX_ASSERT_NOTOWNED); + LCK_MTX_ASSERT(proc_list_mlock, LCK_MTX_ASSERT_NOTOWNED); lck_mtx_lock(&p->p_mlock); } @@ -1522,11 +1563,17 @@ uthread_alloc(task_t task, thread_t thread, int noinherit) 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 @@ -1600,12 +1647,11 @@ uthread_cleanup(task_t task, void *uthread, void * bsd_info) assert(uth->uu_ar == NULL); if (uth->uu_kqueue_bound) { - kevent_qos_internal_unbind(p, - uth->uu_kqueue_bound, + kevent_qos_internal_unbind(p, + 0, /* didn't save qos_class */ uth->uu_thread, uth->uu_kqueue_flags); - uth->uu_kqueue_flags = 0; - uth->uu_kqueue_bound = 0; + assert(uth->uu_kqueue_override_is_sync == 0); } sel = &uth->uu_select; @@ -1639,9 +1685,13 @@ uthread_cleanup(task_t task, void *uthread, void * bsd_info) /* * 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); @@ -1649,7 +1699,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