#include <kern/kalloc.h>
#include <kern/mach_param.h>
#include <kern/task.h>
+#include <kern/thread.h>
#include <kern/thread_call.h>
#include <kern/zalloc.h>
#include <sys/sdt.h>
+#if CONFIG_MEMORYSTATUS
+#include <sys/kern_memorystatus.h>
+#endif
/* XXX routines which should have Mach prototypes, but don't */
void thread_set_parent(thread_t parent, int pid);
void *act_thread_csave(void);
-thread_t cloneproc(task_t, proc_t, int);
+thread_t cloneproc(task_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);
* 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)
*
* 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
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);
if ((err = fork1(parent_proc, &child_thread, PROC_CREATE_VFORK)) != 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 */
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);
}
* 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();
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)
/*
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;
* 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, parent_proc, spawn ? FALSE : TRUE, FALSE)) == NULL) {
/* Failed to create thread */
err = EAGAIN;
goto bad;
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)
/*
* 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;
}
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;
}
/* 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);
* 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)
* 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, 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;
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);
/* 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);
{
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());
nprocs++;
child_proc->p_pid = nextpid;
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)
* 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_TRANSLATED | P_AFFINITY | P_DISABLE_ASLR | P_DELAYIDLESLEEP));
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);
#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);
+#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) {
/* Intialize new process stats, including start time */
/* <rdar://6640543> 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,
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
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;
#if PSYNCH
pth_proc_hashinit(child_proc);
#endif /* PSYNCH */
}
#endif
+#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);
}
THREAD_CHUNK * sizeof(struct uthread),
"uthreads");
uthread_zone_inited = 1;
-
- zone_change(uthread_zone, Z_NOENCRYPT, TRUE);
}
}
p = (proc_t) get_bsdtask_info(task);
uth = (uthread_t)ut;
+ uth->uu_thread = thread;
/*
* Thread inherits credential from the creating thread, if both
if (p->p_dtrace_ptss_pages != NULL) {
uth->t_dtrace_scratch = dtrace_ptss_claim_entry(p);
}
+#endif
+#if CONFIG_MACF
+ mac_thread_label_init(uth);
#endif
}
* 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
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->pth_name != NULL)
if (tmpptr != NULL) {
dtrace_ptss_release_entry(p, tmpptr);
}
+#endif
+#if CONFIG_MACF
+ mac_thread_label_destroy(uth);
#endif
}
}
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);
}