+vfork(proc_t parent, __unused struct vfork_args *uap, register_t *retval)
+{
+ proc_t child;
+ uid_t uid;
+ thread_t cur_act = (thread_t)current_thread();
+ int count;
+ uthread_t ut;
+#if CONFIG_MACF
+ int err;
+#endif
+
+ /*
+ * Although process entries are dynamically created, we still keep
+ * a global limit on the maximum number we will create. Don't allow
+ * a nonprivileged user to use the last process; don't let root
+ * exceed the limit. The variable nprocs is the current number of
+ * processes, maxproc is the limit.
+ */
+ uid = kauth_cred_get()->cr_ruid;
+ proc_list_lock();
+ if ((nprocs >= maxproc - 1 && uid != 0) || nprocs >= maxproc) {
+ proc_list_unlock();
+ tablefull("proc");
+ retval[1] = 0;
+ return (EAGAIN);
+ }
+ proc_list_unlock();
+
+ /*
+ * Increment the count of procs running with this uid. Don't allow
+ * a nonprivileged user to exceed their current limit, which is
+ * 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->p_rlimit[RLIMIT_NPROC].rlim_cur) {
+ (void)chgproccnt(uid, -1);
+ return (EAGAIN);
+ }
+
+ ut = (uthread_t)get_bsdthread_info(cur_act);
+ if (ut->uu_flag & UT_VFORK) {
+ printf("vfork called recursively by %s\n", parent->p_comm);
+ (void)chgproccnt(uid, -1);
+ return (EINVAL);
+ }
+
+#if CONFIG_MACF
+ /*
+ * Determine if MAC policies applied to the process will allow
+ * it to fork.
+ */
+ err = mac_proc_check_fork(parent);
+ if (err != 0) {
+ (void)chgproccnt(uid, -1);
+ return (err);
+ }
+#endif
+
+ proc_lock(parent);
+ parent->p_lflag |= P_LVFORK;
+ parent->p_vforkcnt++;
+ proc_unlock(parent);
+
+ /* The newly created process comes with signal lock held */
+ if ((child = forkproc(parent,1)) == NULL) {
+ /* Failed to allocate new process */
+ (void)chgproccnt(uid, -1);
+ /*
+ * XXX kludgy, but necessary without a full flags audit...
+ * XXX these are inherited by the child, which depends on
+ * XXX P_VFORK being set.
+ */
+ proc_lock(parent);
+ parent->p_lflag &= ~P_LVFORK;
+ parent->p_vforkcnt--;
+ proc_unlock(parent);
+ return (ENOMEM);
+ }
+
+#if CONFIG_MACF
+ /* allow policies to associate the credential/label */
+ /* that we referenced from the parent ... with the child */
+ /* JMM - this really isn't safe, as we can drop that */
+ /* association without informing the policy in other */
+ /* situations (keep long enough to get policies changed) */
+ mac_cred_label_associate_fork(child->p_ucred, child);
+#endif
+
+ AUDIT_ARG(pid, child->p_pid);
+
+ child->task = parent->task;
+
+ /* make child visible */
+ pinsertchild(parent, child);
+
+ child->p_lflag |= P_LINVFORK;
+ child->p_vforkact = cur_act;
+ child->p_stat = SRUN;
+
+ ut->uu_flag |= UT_VFORK;
+ ut->uu_proc = child;
+ ut->uu_userstate = (void *)act_thread_csave();
+ ut->uu_vforkmask = ut->uu_sigmask;
+
+ /* temporarily drop thread-set-id state */
+ if (ut->uu_flag & UT_SETUID) {
+ ut->uu_flag |= UT_WASSETUID;
+ ut->uu_flag &= ~UT_SETUID;
+ }
+
+ thread_set_child(cur_act, child->p_pid);
+
+ microtime(&child->p_start);
+ microtime(&child->p_stats->p_start); /* for compat sake */
+ child->p_acflag = AFORK;
+
+ /*
+ * Preserve synchronization semantics of vfork. If waiting for
+ * child to exec or exit, set P_PPWAIT on child, and sleep on our
+ * proc (in case of exit).
+ */
+ child->p_lflag |= P_LPPWAIT;
+
+ /* drop the signal lock on the child */
+ proc_signalend(child, 0);
+ proc_transend(child, 0);
+
+ retval[0] = child->p_pid;
+ retval[1] = 1; /* flag child return for user space */
+
+ DTRACE_PROC1(create, proc_t, child);
+
+ return (0);
+}
+
+/*
+ * vfork_return
+ *
+ * Description: "Return" to parent vfork thread() following execve/_exit;
+ * this is done by reassociating the parent process structure
+ * with the task, thread, and uthread.
+ *
+ * Parameters: child 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.
+ */
+void
+vfork_return(proc_t child, register_t *retval, int rval)