+ * Reset signal state.
+ */
+ execsigs(p, thread);
+
+ /*
+ * need to cancel async IO requests that can be cancelled and wait for those
+ * already active. MAY BLOCK!
+ */
+ _aio_exec( p );
+
+#if SYSV_SHM
+ /* FIXME: Till vmspace inherit is fixed: */
+ if (!vfexec && p->vm_shm)
+ shmexec(p);
+#endif
+#if SYSV_SEM
+ /* Clean up the semaphores */
+ semexit(p);
+#endif
+
+ /*
+ * Remember file name for accounting.
+ */
+ p->p_acflag &= ~AFORK;
+ /* If the translated name isn't NULL, then we want to use
+ * that translated name as the name we show as the "real" name.
+ * Otherwise, use the name passed into exec.
+ */
+ if (0 != imgp->ip_p_comm[0]) {
+ bcopy((caddr_t)imgp->ip_p_comm, (caddr_t)p->p_comm,
+ sizeof(p->p_comm));
+ } else {
+ if (imgp->ip_ndp->ni_cnd.cn_namelen > MAXCOMLEN)
+ imgp->ip_ndp->ni_cnd.cn_namelen = MAXCOMLEN;
+ bcopy((caddr_t)imgp->ip_ndp->ni_cnd.cn_nameptr, (caddr_t)p->p_comm,
+ (unsigned)imgp->ip_ndp->ni_cnd.cn_namelen);
+ p->p_comm[imgp->ip_ndp->ni_cnd.cn_namelen] = '\0';
+ }
+
+#if CONFIG_DTRACE
+ /*
+ * Invalidate any predicate evaluation already cached for this thread by DTrace.
+ * That's because we've just stored to p_comm and DTrace refers to that when it
+ * evaluates the "execname" special variable. uid and gid may have changed as well.
+ */
+ dtrace_set_thread_predcache(current_thread(), 0);
+
+ /*
+ * Free any outstanding lazy dof entries. It is imperative we
+ * always call dtrace_lazy_dofs_destroy, 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 cleanup. The lazy dof faulting will then
+ * install new helpers which no longer belong to this process!
+ */
+ dtrace_lazy_dofs_destroy(p);
+
+
+ /*
+ * Clean up any DTrace helpers for the process.
+ */
+ if (p->p_dtrace_helpers != NULL && dtrace_helpers_cleanup) {
+ (*dtrace_helpers_cleanup)(p);
+ }
+
+ /*
+ * Cleanup the DTrace provider associated with this process.
+ */
+ proc_lock(p);
+ if (p->p_dtrace_probes && dtrace_fasttrap_exec_ptr) {
+ (*dtrace_fasttrap_exec_ptr)(p);
+ }
+ proc_unlock(p);
+#endif
+
+ if (kdebug_enable) {
+ long dbg_arg1, dbg_arg2, dbg_arg3, dbg_arg4;
+
+ /*
+ * Collect the pathname for tracing
+ */
+ kdbg_trace_string(p, &dbg_arg1, &dbg_arg2, &dbg_arg3, &dbg_arg4);
+
+ if (vfexec) {
+ KERNEL_DEBUG_CONSTANT1((TRACEDBG_CODE(DBG_TRACE_DATA, 2)) | DBG_FUNC_NONE,
+ p->p_pid ,0,0,0, (unsigned int)thread);
+ KERNEL_DEBUG_CONSTANT1((TRACEDBG_CODE(DBG_TRACE_STRING, 2)) | DBG_FUNC_NONE,
+ dbg_arg1, dbg_arg2, dbg_arg3, dbg_arg4, (unsigned int)thread);
+ } else {
+ KERNEL_DEBUG_CONSTANT((TRACEDBG_CODE(DBG_TRACE_DATA, 2)) | DBG_FUNC_NONE,
+ p->p_pid ,0,0,0,0);
+ KERNEL_DEBUG_CONSTANT((TRACEDBG_CODE(DBG_TRACE_STRING, 2)) | DBG_FUNC_NONE,
+ dbg_arg1, dbg_arg2, dbg_arg3, dbg_arg4, 0);
+ }
+ }
+
+#ifdef IMGPF_POWERPC
+ /*
+ * Mark the process as powerpc or not. If powerpc, set the affinity
+ * flag, which will be used for grading binaries in future exec's
+ * from the process.
+ */
+ if (((imgp->ip_flags & IMGPF_POWERPC) != 0))
+ OSBitOrAtomic(P_TRANSLATED, (UInt32 *)&p->p_flag);
+ else
+#endif /* IMGPF_POWERPC */
+ OSBitAndAtomic(~((uint32_t)P_TRANSLATED), (UInt32 *)&p->p_flag);
+ OSBitAndAtomic(~((uint32_t)P_AFFINITY), (UInt32 *)&p->p_flag);
+
+ /*
+ * If posix_spawned with the START_SUSPENDED flag, stop the
+ * process before it runs.
+ */
+ if (imgp->ip_px_sa != NULL) {
+ psa = (struct _posix_spawnattr *) imgp->ip_px_sa;
+ if (psa->psa_flags & POSIX_SPAWN_START_SUSPENDED) {
+ proc_lock(p);
+ p->p_stat = SSTOP;
+ proc_unlock(p);
+ (void) task_suspend(p->task);
+ }
+ }
+
+ /*
+ * mark as execed, wakeup the process that vforked (if any) and tell
+ * it that it now has it's own resources back
+ */
+ OSBitOrAtomic(P_EXEC, (UInt32 *)&p->p_flag);
+ if (p->p_pptr && (p->p_lflag & P_LPPWAIT)) {
+ proc_lock(p);
+ p->p_lflag &= ~P_LPPWAIT;
+ proc_unlock(p);
+ wakeup((caddr_t)p->p_pptr);
+ }
+
+ if (vfexec && (p->p_lflag & P_LTRACED)) {
+ psignal_vfork(p, new_task, thread, SIGTRAP);
+ }
+
+badtoolate:
+ proc_knote(p, NOTE_EXEC);
+
+ if (vfexec) {
+ task_deallocate(new_task);
+ thread_deallocate(thread);
+ if (error)
+ error = 0;
+ }
+
+bad:
+ return(error);
+}
+
+
+
+
+/*
+ * Our image activator table; this is the table of the image types we are
+ * capable of loading. We list them in order of preference to ensure the
+ * fastest image load speed.
+ *
+ * XXX hardcoded, for now; should use linker sets
+ */
+struct execsw {
+ int (*ex_imgact)(struct image_params *);
+ const char *ex_name;
+} execsw[] = {
+ { exec_mach_imgact, "Mach-o Binary" },
+ { exec_fat_imgact, "Fat Binary" },
+#ifdef IMGPF_POWERPC
+ { exec_powerpc32_imgact, "PowerPC binary" },
+#endif /* IMGPF_POWERPC */
+ { exec_shell_imgact, "Interpreter Script" },
+ { NULL, NULL}
+};
+
+
+/*
+ * exec_activate_image
+ *
+ * Description: Iterate through the available image activators, and activate
+ * the image associated with the imgp structure. We start with
+ * the
+ *
+ * Parameters: struct image_params * Image parameter block
+ *
+ * Returns: 0 Success
+ * EBADEXEC The executable is corrupt/unknown
+ * execargs_alloc:EINVAL Invalid argument
+ * execargs_alloc:EACCES Permission denied
+ * execargs_alloc:EINTR Interrupted function
+ * execargs_alloc:ENOMEM Not enough space
+ * exec_save_path:EFAULT Bad address
+ * exec_save_path:ENAMETOOLONG Filename too long
+ * exec_check_permissions:EACCES Permission denied
+ * exec_check_permissions:ENOEXEC Executable file format error
+ * exec_check_permissions:ETXTBSY Text file busy [misuse of error code]
+ * exec_check_permissions:???
+ * namei:???
+ * vn_rdwr:??? [anything vn_rdwr can return]
+ * <ex_imgact>:??? [anything an imgact can return]
+ */
+static int
+exec_activate_image(struct image_params *imgp)
+{
+ struct nameidata nd;
+ int error;
+ int resid;
+ int once = 1; /* save SGUID-ness for interpreted files */
+ int i;
+ int iterlimit = EAI_ITERLIMIT;
+ proc_t p = vfs_context_proc(imgp->ip_vfs_context);
+
+ error = execargs_alloc(imgp);
+ if (error)
+ goto bad;
+
+ /*
+ * XXXAUDIT: Note: the double copyin introduces an audit
+ * race. To correct this race, we must use a single
+ * copyin(), e.g. by passing a flag to namei to indicate an
+ * external path buffer is being used.
+ */
+ error = exec_save_path(imgp, imgp->ip_user_fname, imgp->ip_seg);
+ if (error) {
+ goto bad_notrans;
+ }
+
+ DTRACE_PROC1(exec, uintptr_t, imgp->ip_strings);
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
+ imgp->ip_seg, imgp->ip_user_fname, imgp->ip_vfs_context);
+
+again:
+ error = namei(&nd);
+ if (error)
+ goto bad_notrans;
+ imgp->ip_ndp = &nd; /* successful namei(); call nameidone() later */
+ imgp->ip_vp = nd.ni_vp; /* if set, need to vnode_put() at some point */
+
+ proc_transstart(p, 0);
+
+ error = exec_check_permissions(imgp);
+ if (error)
+ goto bad;
+
+ /* Copy; avoid invocation of an interpreter overwriting the original */
+ if (once) {
+ once = 0;
+ *imgp->ip_origvattr = *imgp->ip_vattr;
+ }
+
+ error = vn_rdwr(UIO_READ, imgp->ip_vp, imgp->ip_vdata, PAGE_SIZE, 0,
+ UIO_SYSSPACE32, IO_NODELOCKED,
+ vfs_context_ucred(imgp->ip_vfs_context),
+ &resid, vfs_context_proc(imgp->ip_vfs_context));
+ if (error)
+ goto bad;
+
+encapsulated_binary:
+ /* Limit the number of iterations we will attempt on each binary */
+ if (--iterlimit == 0) {
+ error = EBADEXEC;
+ goto bad;
+ }
+ error = -1;
+ for(i = 0; error == -1 && execsw[i].ex_imgact != NULL; i++) {
+
+ error = (*execsw[i].ex_imgact)(imgp);
+
+ switch (error) {
+ /* case -1: not claimed: continue */
+ case -2: /* Encapsulated binary */
+ goto encapsulated_binary;
+
+ case -3: /* Interpreter */
+#if CONFIG_MACF
+ /*
+ * Copy the script label for later use. Note that
+ * the label can be different when the script is
+ * actually read by the interpreter.
+ */
+ if (imgp->ip_scriptlabelp)
+ mac_vnode_label_free(imgp->ip_scriptlabelp);
+ imgp->ip_scriptlabelp = mac_vnode_label_alloc();
+ if (imgp->ip_scriptlabelp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ mac_vnode_label_copy(imgp->ip_vp->v_label,
+ imgp->ip_scriptlabelp);
+#endif
+ vnode_put(imgp->ip_vp);
+ imgp->ip_vp = NULL; /* already put */
+ nd.ni_cnd.cn_nameiop = LOOKUP;
+ nd.ni_cnd.cn_flags = (nd.ni_cnd.cn_flags & HASBUF) |
+ (FOLLOW | LOCKLEAF);
+
+#ifdef IMGPF_POWERPC
+ /*
+ * PowerPC does not follow symlinks because the
+ * code which sets exec_archhandler_ppc.fsid and
+ * exec_archhandler_ppc.fileid doesn't follow them.
+ */
+ if (imgp->ip_flags & IMGPF_POWERPC)
+ nd.ni_cnd.cn_flags &= ~FOLLOW;
+#endif /* IMGPF_POWERPC */
+
+ nd.ni_segflg = UIO_SYSSPACE32;
+ nd.ni_dirp = CAST_USER_ADDR_T(imgp->ip_interp_name);
+ proc_transend(p, 0);
+ goto again;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Call out to allow 3rd party notification of exec.
+ * Ignore result of kauth_authorize_fileop call.
+ */
+ if (error == 0 && kauth_authorize_fileop_has_listeners()) {
+ kauth_authorize_fileop(vfs_context_ucred(imgp->ip_vfs_context),
+ KAUTH_FILEOP_EXEC,
+ (uintptr_t)nd.ni_vp, 0);
+ }
+
+bad:
+ proc_transend(p, 0);
+
+bad_notrans:
+ if (imgp->ip_strings)
+ execargs_free(imgp);
+ if (imgp->ip_ndp)
+ nameidone(imgp->ip_ndp);
+
+ return (error);
+}
+
+/*
+ * exec_handle_port_actions
+ *
+ * Description: Go through the _posix_port_actions_t contents,
+ * calling task_set_special_port and task_set_exception_ports
+ * for the current task.
+ *
+ * Parameters: struct image_params * Image parameter block
+ *
+ * Returns: 0 Success
+ * KERN_FAILURE Failure
+ */
+static int
+exec_handle_port_actions(struct image_params *imgp)
+{
+ _posix_spawn_port_actions_t pacts = imgp->ip_px_spa;
+ proc_t p = vfs_context_proc(imgp->ip_vfs_context);
+ _ps_port_action_t *act = NULL;
+ task_t task = p->task;
+ ipc_port_t port = NULL;
+ kern_return_t ret = KERN_SUCCESS;
+ int i;
+
+ for (i = 0; i < pacts->pspa_count; i++) {
+ act = &pacts->pspa_actions[i];
+
+ ret = ipc_object_copyin(get_task_ipcspace(current_task()),
+ (mach_port_name_t) act->new_port,
+ MACH_MSG_TYPE_COPY_SEND,
+ (ipc_object_t *) &port);
+
+ if (ret)
+ return ret;
+
+ switch (act->port_type) {
+ case PSPA_SPECIAL:
+ ret = task_set_special_port(task,
+ act->which,
+ port);
+ break;
+ case PSPA_EXCEPTION:
+ ret = task_set_exception_ports(task,
+ act->mask,
+ port,
+ act->behavior,
+ act->flavor);
+ break;
+ default:
+ ret = KERN_FAILURE;
+ }
+ /* action failed, so release port resources */
+ if (ret) {
+ ipc_port_release_send(port);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * exec_handle_file_actions
+ *
+ * Description: Go through the _posix_file_actions_t contents applying the
+ * open, close, and dup2 operations to the open file table for
+ * the current process.
+ *
+ * Parameters: struct image_params * Image parameter block
+ *
+ * Returns: 0 Success
+ * ???
+ *
+ * Note: Actions are applied in the order specified, with the credential
+ * of the parent process. This is done to permit the parent
+ * process to utilize POSIX_SPAWN_RESETIDS to drop privilege in
+ * the child following operations the child may in fact not be
+ * normally permitted to perform.
+ */
+static int
+exec_handle_file_actions(struct image_params *imgp)
+{
+ int error = 0;
+ int action;
+ proc_t p = vfs_context_proc(imgp->ip_vfs_context);
+ _posix_spawn_file_actions_t px_sfap = imgp->ip_px_sfa;
+ register_t ival[2]; /* dummy retval for system calls) */
+
+ for (action = 0; action < px_sfap->psfa_act_count; action++) {
+ _psfa_action_t *psfa = &px_sfap->psfa_act_acts[ action];
+
+ switch(psfa->psfaa_type) {
+ case PSFA_OPEN: {
+ /*
+ * Open is different, in that it requires the use of
+ * a path argument, which is normally copied in from
+ * user space; because of this, we have to support an
+ * open from kernel space that passes an address space
+ * context oof UIO_SYSSPACE, and casts the address
+ * argument to a user_addr_t.
+ */
+ struct vnode_attr va;
+ struct nameidata nd;
+ int mode = psfa->psfaa_openargs.psfao_mode;
+ struct dup2_args dup2a;
+ struct close_nocancel_args ca;
+ int origfd;
+
+ VATTR_INIT(&va);
+ /* Mask off all but regular access permissions */
+ mode = ((mode &~ p->p_fd->fd_cmask) & ALLPERMS) & ~S_ISTXT;
+ VATTR_SET(&va, va_mode, mode & ACCESSPERMS);
+
+ NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, UIO_SYSSPACE,
+ CAST_USER_ADDR_T(psfa->psfaa_openargs.psfao_path),
+ imgp->ip_vfs_context);
+
+ error = open1(imgp->ip_vfs_context,
+ &nd,
+ psfa->psfaa_openargs.psfao_oflag,
+ &va,
+ ival);
+
+ /*
+ * If there's an error, or we get the right fd by
+ * accident, then drop out here. This is easier that
+ * rearchitecting all the open code to preallocate fd
+ * slots, and internally taking one as an argument.
+ */
+ if (error || ival[0] == psfa->psfaa_filedes)
+ break;
+
+ origfd = ival[0];
+ /*
+ * If we didn't fall out from an error, we ended up
+ * with the wrong fd; so now we've got to try to dup2
+ * it to the right one.
+ */
+ dup2a.from = origfd;
+ dup2a.to = psfa->psfaa_filedes;
+
+ /*
+ * The dup2() system call implementation sets
+ * ival to newfd in the success case, but we
+ * can ignore that, since if we didn't get the
+ * fd we wanted, the error will stop us.
+ */
+ error = dup2(p, &dup2a, ival);
+ if (error)
+ break;
+
+ /*
+ * Finally, close the original fd.
+ */
+ ca.fd = origfd;
+
+ error = close_nocancel(p, &ca, ival);
+ }
+ break;
+
+ case PSFA_DUP2: {
+ struct dup2_args dup2a;
+
+ dup2a.from = psfa->psfaa_filedes;
+ dup2a.to = psfa->psfaa_openargs.psfao_oflag;
+
+ /*
+ * The dup2() system call implementation sets
+ * ival to newfd in the success case, but we
+ * can ignore that, since if we didn't get the
+ * fd we wanted, the error will stop us.
+ */
+ error = dup2(p, &dup2a, ival);
+ }
+ break;
+
+ case PSFA_CLOSE: {
+ struct close_nocancel_args ca;
+
+ ca.fd = psfa->psfaa_filedes;
+
+ error = close_nocancel(p, &ca, ival);
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ /* All file actions failures are considered fatal, per POSIX */
+ if (error)
+ break;
+ }
+
+ return (error);
+}
+
+
+/*
+ * posix_spawn
+ *
+ * Parameters: uap->pid Pointer to pid return area
+ * uap->fname File name to exec
+ * uap->argp Argument list
+ * uap->envp Environment list
+ *
+ * Returns: 0 Success
+ * EINVAL Invalid argument
+ * ENOTSUP Not supported
+ * ENOEXEC Executable file format error
+ * exec_activate_image:EINVAL Invalid argument
+ * exec_activate_image:EACCES Permission denied
+ * exec_activate_image:EINTR Interrupted function
+ * exec_activate_image:ENOMEM Not enough space
+ * exec_activate_image:EFAULT Bad address
+ * exec_activate_image:ENAMETOOLONG Filename too long
+ * exec_activate_image:ENOEXEC Executable file format error
+ * exec_activate_image:ETXTBSY Text file busy [misuse of error code]
+ * exec_activate_image:EBADEXEC The executable is corrupt/unknown
+ * exec_activate_image:???
+ * mac_execve_enter:???
+ *
+ * TODO: More gracefully handle failures after vfork
+ * Expect to need __mac_posix_spawn() at some point...
+ * Handle posix_spawnattr_t
+ * Handle posix_spawn_file_actions_t
+ */
+int
+posix_spawn(proc_t ap, struct posix_spawn_args *uap, register_t *retval)
+{
+ proc_t p = ap; /* quiet bogus GCC vfork() warning */
+ user_addr_t pid = uap->pid;
+ register_t ival[2]; /* dummy retval for vfork() */
+ struct image_params image_params, *imgp;
+ struct vnode_attr va;
+ struct vnode_attr origva;
+ struct uthread *uthread = 0; /* compiler complains if not set to 0*/
+ int error, sig;
+ task_t task;
+ int numthreads;
+ char alt_p_comm[sizeof(p->p_comm)] = {0}; /* for PowerPC */
+ int is_64 = IS_64BIT_PROCESS(p);
+ int undo_vfork = 0;
+ struct vfs_context context;
+ struct user__posix_spawn_args_desc px_args;
+ struct _posix_spawnattr px_sa;
+ _posix_spawn_file_actions_t px_sfap = NULL;
+ _posix_spawn_port_actions_t px_spap = NULL;
+ struct __user_sigaction vec;
+
+ imgp = &image_params;
+
+ /* Initialize the common data in the image_params structure */
+ bzero(imgp, sizeof(*imgp));
+ imgp->ip_user_fname = uap->path;
+ imgp->ip_user_argv = uap->argv;
+ imgp->ip_user_envv = uap->envp;
+ imgp->ip_vattr = &va;
+ imgp->ip_origvattr = &origva;
+ imgp->ip_vfs_context = &context;
+ imgp->ip_flags = (is_64 ? IMGPF_WAS_64BIT : IMGPF_NONE);
+ imgp->ip_p_comm = alt_p_comm; /* for PowerPC */
+ imgp->ip_seg = (is_64 ? UIO_USERSPACE64 : UIO_USERSPACE32);
+
+ if (uap->adesc != USER_ADDR_NULL) {
+ if(is_64) {
+ error = copyin(uap->adesc, &px_args, sizeof(px_args));
+ } else {
+ struct _posix_spawn_args_desc px_args32;
+
+ error = copyin(uap->adesc, &px_args32, sizeof(px_args32));
+
+ /*
+ * Convert arguments descriptor from external 32 bit
+ * representation to internal 64 bit representation
+ */
+ px_args.attr_size = px_args32.attr_size;
+ px_args.attrp = CAST_USER_ADDR_T(px_args32.attrp);
+ px_args.file_actions_size = px_args32.file_actions_size;
+ px_args.file_actions = CAST_USER_ADDR_T(px_args32.file_actions);
+ px_args.port_actions_size = px_args32.port_actions_size;
+ px_args.port_actions = CAST_USER_ADDR_T(px_args32.port_actions);
+ }
+ if (error)
+ goto bad;
+
+ if (px_args.attr_size != 0) {
+ /*
+ * This could lose some of the port_actions pointer,
+ * but we already have it from px_args.
+ */
+ if ((error = copyin(px_args.attrp, &px_sa, sizeof(px_sa))) != 0)
+ goto bad;
+
+ imgp->ip_px_sa = &px_sa;
+ }
+ if (px_args.file_actions_size != 0) {
+ /* Limit file_actions to allowed number of open files */
+ int maxfa = (p->p_limit ? p->p_rlimit[RLIMIT_NOFILE].rlim_cur : NOFILE);
+ if (px_args.file_actions_size < PSF_ACTIONS_SIZE(1) ||
+ px_args.file_actions_size > PSF_ACTIONS_SIZE(maxfa)) {
+ error = EINVAL;
+ goto bad;
+ }
+ MALLOC(px_sfap, _posix_spawn_file_actions_t, px_args.file_actions_size, M_TEMP, M_WAITOK);
+ if (px_sfap == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ imgp->ip_px_sfa = px_sfap;
+
+ if ((error = copyin(px_args.file_actions, px_sfap,
+ px_args.file_actions_size)) != 0)
+ goto bad;
+ }
+ if (px_args.port_actions_size != 0) {
+ /* Limit port_actions to one page of data */
+ if (px_args.port_actions_size < PS_PORT_ACTIONS_SIZE(1) ||
+ px_args.port_actions_size > PAGE_SIZE) {
+ error = EINVAL;
+ goto bad;
+ }
+
+ MALLOC(px_spap, _posix_spawn_port_actions_t,
+ px_args.port_actions_size, M_TEMP, M_WAITOK);
+ if (px_spap == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ imgp->ip_px_spa = px_spap;
+
+ if ((error = copyin(px_args.port_actions, px_spap,
+ px_args.port_actions_size)) != 0)
+ goto bad;
+ }
+ }
+
+ if (imgp->ip_px_sa == NULL || !(px_sa.psa_flags & POSIX_SPAWN_SETEXEC)){
+ if ((error = vfork(p, NULL, ival)) != 0)
+ goto bad;
+ undo_vfork = 1;
+ }
+
+ /* "reenter the kernel" on a new vfork()'ed process */
+ uthread = get_bsdthread_info(current_thread());
+ if (undo_vfork)
+ p = uthread->uu_proc;
+
+ context.vc_thread = current_thread();
+ context.vc_ucred = p->p_ucred; /* XXX must NOT be kauth_cred_get() */
+
+ /*
+ * Post fdcopy(), pre exec_handle_sugid() - this is where we want
+ * to handle the file_actions. Since vfork() also ends up setting
+ * us into the parent process group, and saved off the signal flags,
+ * this is also where we want to handle the spawn flags.
+ */
+ /* Has spawn file actions? */
+ if (imgp->ip_px_sfa != NULL &&
+ (error = exec_handle_file_actions(imgp)) != 0) {
+ goto bad;
+ }
+
+ /* Has spawn port actions? */
+ if (imgp->ip_px_spa != NULL) {
+ /* Only allowed when not under vfork */
+ if (!(px_sa.psa_flags & POSIX_SPAWN_SETEXEC)) {
+ error = ENOTSUP;
+ goto bad;
+ }
+ if((error = exec_handle_port_actions(imgp)) != 0)
+ goto bad;
+ }
+
+ /* Has spawn attr? */
+ if (imgp->ip_px_sa != NULL) {
+ /* Set the process group ID of the child process */
+ if (px_sa.psa_flags & POSIX_SPAWN_SETPGROUP) {
+ struct setpgid_args spga;
+ spga.pid = p->p_pid;
+ spga.pgid = px_sa.psa_pgroup;
+ /*
+ * Effectively, call setpgid() system call; works
+ * because there are no pointer arguments.
+ */
+ if((error = setpgid(p, &spga, ival)) != 0)
+ goto bad;
+ }
+ /*
+ * Reset UID/GID to parent's RUID/RGID; This works only
+ * because the operation occurs *after* the vfork() and
+ * before the call to exec_handle_sugid() by the image
+ * activator called from exec_activate_image().
+ *
+ * The use of p_ucred is safe, since we are acting on the
+ * new process, and it has no threads other than the one
+ * we are creating for it.
+ */
+ if (px_sa.psa_flags & POSIX_SPAWN_RESETIDS) {
+ kauth_cred_t my_cred = p->p_ucred;
+ kauth_cred_t my_new_cred = kauth_cred_setuidgid(my_cred, my_cred->cr_ruid, my_cred->cr_rgid);
+ if (my_new_cred != my_cred)
+ p->p_ucred = my_new_cred;
+ }
+ /*
+ * Mask a list of signals, instead of them being unmasked, if
+ * they were unmasked in the parent; note that some signals
+ * are not maskable.
+ */
+ if (px_sa.psa_flags & POSIX_SPAWN_SETSIGMASK)
+ uthread->uu_sigmask = (px_sa.psa_sigmask & ~sigcantmask);
+ /*
+ * Default a list of signals instead of ignoring them, if
+ * they were ignored in the parent.
+ */
+ if (px_sa.psa_flags & POSIX_SPAWN_SETSIGDEF) {
+ vec.sa_handler = SIG_DFL;
+ vec.sa_tramp = 0;
+ vec.sa_mask = 0;
+ vec.sa_flags = 0;
+ for (sig = 0; sig < NSIG; sig++)
+ if (px_sa.psa_sigdefault && 1 << sig) {
+ error = setsigvec(p, sig, &vec);
+ }
+ }
+ }
+
+ /*
+ * XXXAUDIT: Currently, we only audit the pathname of the binary.
+ * There may also be poor interaction with dyld.
+ */
+
+ task = current_task();
+
+ /* If we're not in vfork, don't permit a mutithreaded task to exec */
+ if (!(uthread->uu_flag & UT_VFORK)) {
+ if (task != kernel_task) {
+ numthreads = get_task_numacts(task);
+ if (numthreads <= 0 ) {
+ error = EINVAL;
+ goto bad;
+ }
+ if (numthreads > 1) {
+ error = ENOTSUP;
+ goto bad;
+ }
+ }
+ }
+
+#if MAC_SPAWN /* XXX */
+ if (uap->mac_p != USER_ADDR_NULL) {
+ error = mac_execve_enter(uap->mac_p, imgp);
+ if (error)
+ goto bad;
+ }
+#endif
+
+ if ((error = exec_activate_image(imgp)) != 0)
+ goto bad;
+bad:
+ /* Image not claimed by any activator? */
+ if (error == -1)
+ error = ENOEXEC;
+ if (error == 0) {
+ exec_resettextvp(p, imgp);
+ }
+ if (imgp->ip_vp)
+ vnode_put(imgp->ip_vp);
+ if (imgp->ip_strings)
+ execargs_free(imgp);
+ if (imgp->ip_px_sfa != NULL)
+ FREE(imgp->ip_px_sfa, M_TEMP);
+ if (imgp->ip_px_spa != NULL)
+ FREE(imgp->ip_px_spa, M_TEMP);
+
+#if CONFIG_MACF
+ if (imgp->ip_execlabelp)
+ mac_cred_label_free(imgp->ip_execlabelp);
+ if (imgp->ip_scriptlabelp)
+ mac_vnode_label_free(imgp->ip_scriptlabelp);
+#endif
+ if (undo_vfork) {
+ if (error) {
+ DTRACE_PROC1(exec__failure, int, error);
+ vfork_exit(p, W_EXITCODE(-1, 0));
+ } else {
+ DTRACE_PROC(exec__success);
+ }
+ /*
+ * Returning to the parent process...
+ *
+ * If the parent wants the pid, copy it out
+ */
+ if (pid != USER_ADDR_NULL)
+ (void)suword(pid, p->p_pid);
+ retval[0] = error;
+ /*
+ * Override inherited code signing flags with the
+ * ones for the process that is being successfully
+ * loaded
+ */
+ proc_lock(p);
+ p->p_csflags = imgp->ip_csflags;
+ proc_unlock(p);
+ vfork_return(p, NULL, error);
+ (void)thread_resume(imgp->ip_vfork_thread);
+ }
+
+ if (!error) {
+ /*
+ * Override inherited code signing flags with the
+ * ones for the process that is being successfully
+ * loaded
+ */
+ proc_lock(p);
+ p->p_csflags = imgp->ip_csflags;
+ proc_unlock(p);
+ DTRACE_PROC(exec__success);
+ } else {
+ DTRACE_PROC1(exec__failure, int, error);
+ }
+
+ return(error);
+}
+
+
+/*
+ * execve
+ *
+ * Parameters: uap->fname File name to exec
+ * uap->argp Argument list
+ * uap->envp Environment list
+ *
+ * Returns: 0 Success
+ * __mac_execve:EINVAL Invalid argument
+ * __mac_execve:ENOTSUP Invalid argument
+ * __mac_execve:EACCES Permission denied
+ * __mac_execve:EINTR Interrupted function
+ * __mac_execve:ENOMEM Not enough space
+ * __mac_execve:EFAULT Bad address
+ * __mac_execve:ENAMETOOLONG Filename too long
+ * __mac_execve:ENOEXEC Executable file format error
+ * __mac_execve:ETXTBSY Text file busy [misuse of error code]
+ * __mac_execve:???
+ *
+ * TODO: Dynamic linker header address on stack is copied via suword()
+ */
+/* ARGSUSED */
+int
+execve(proc_t p, struct execve_args *uap, register_t *retval)
+{
+ struct __mac_execve_args muap;
+ int err;
+
+ muap.fname = uap->fname;
+ muap.argp = uap->argp;
+ muap.envp = uap->envp;
+ muap.mac_p = USER_ADDR_NULL;
+ err = __mac_execve(p, &muap, retval);
+
+ return(err);
+}
+
+/*
+ * __mac_execve
+ *
+ * Parameters: uap->fname File name to exec
+ * uap->argp Argument list
+ * uap->envp Environment list
+ * uap->mac_p MAC label supplied by caller
+ *
+ * Returns: 0 Success
+ * EINVAL Invalid argument
+ * ENOTSUP Not supported
+ * ENOEXEC Executable file format error
+ * exec_activate_image:EINVAL Invalid argument
+ * exec_activate_image:EACCES Permission denied
+ * exec_activate_image:EINTR Interrupted function
+ * exec_activate_image:ENOMEM Not enough space
+ * exec_activate_image:EFAULT Bad address
+ * exec_activate_image:ENAMETOOLONG Filename too long
+ * exec_activate_image:ENOEXEC Executable file format error
+ * exec_activate_image:ETXTBSY Text file busy [misuse of error code]
+ * exec_activate_image:EBADEXEC The executable is corrupt/unknown
+ * exec_activate_image:???
+ * mac_execve_enter:???
+ *
+ * TODO: Dynamic linker header address on stack is copied via suword()
+ */
+int
+__mac_execve(proc_t p, struct __mac_execve_args *uap, register_t *retval)
+{
+ struct image_params image_params, *imgp;
+ struct vnode_attr va;
+ struct vnode_attr origva;
+ struct uthread *uthread;
+ int error;
+ task_t task;
+ int numthreads;
+ char alt_p_comm[sizeof(p->p_comm)] = {0}; /* for PowerPC */
+ int is_64 = IS_64BIT_PROCESS(p);
+ struct vfs_context context;
+
+ context.vc_thread = current_thread();
+ context.vc_ucred = kauth_cred_proc_ref(p); /* XXX must NOT be kauth_cred_get() */
+
+ imgp = &image_params;
+
+ /* Initialize the common data in the image_params structure */
+ bzero(imgp, sizeof(*imgp));
+ imgp->ip_user_fname = uap->fname;
+ imgp->ip_user_argv = uap->argp;
+ imgp->ip_user_envv = uap->envp;
+ imgp->ip_vattr = &va;
+ imgp->ip_origvattr = &origva;
+ imgp->ip_vfs_context = &context;
+ imgp->ip_flags = (is_64 ? IMGPF_WAS_64BIT : IMGPF_NONE);
+ imgp->ip_p_comm = alt_p_comm; /* for PowerPC */
+ imgp->ip_seg = (is_64 ? UIO_USERSPACE64 : UIO_USERSPACE32);
+
+ /*
+ * XXXAUDIT: Currently, we only audit the pathname of the binary.
+ * There may also be poor interaction with dyld.
+ */
+
+ task = current_task();
+ uthread = get_bsdthread_info(current_thread());
+
+ /* If we're not in vfork, don't permit a mutithreaded task to exec */
+ if (!(uthread->uu_flag & UT_VFORK)) {
+ if (task != kernel_task) {
+ proc_lock(p);
+ numthreads = get_task_numactivethreads(task);
+ if (numthreads <= 0 ) {
+ proc_unlock(p);
+ kauth_cred_unref(&context.vc_ucred);
+ return(EINVAL);
+ }
+ if (numthreads > 1) {
+ proc_unlock(p);
+ kauth_cred_unref(&context.vc_ucred);
+ return(ENOTSUP);
+ }
+ proc_unlock(p);
+ }
+ }
+
+#if CONFIG_MACF
+ if (uap->mac_p != USER_ADDR_NULL) {
+ error = mac_execve_enter(uap->mac_p, imgp);
+ if (error) {
+ kauth_cred_unref(&context.vc_ucred);
+ return (error);
+ }
+ }
+#endif
+
+ error = exec_activate_image(imgp);
+
+ kauth_cred_unref(&context.vc_ucred);
+
+ /* Image not claimed by any activator? */
+ if (error == -1)
+ error = ENOEXEC;
+
+ if (error == 0) {
+ exec_resettextvp(p, imgp);
+ }
+ if (imgp->ip_vp != NULLVP)
+ vnode_put(imgp->ip_vp);
+ if (imgp->ip_strings)
+ execargs_free(imgp);
+#if CONFIG_MACF
+ if (imgp->ip_execlabelp)
+ mac_cred_label_free(imgp->ip_execlabelp);
+ if (imgp->ip_scriptlabelp)
+ mac_vnode_label_free(imgp->ip_scriptlabelp);
+#endif
+ if (!error) {
+ /*
+ * Override inherited code signing flags with the
+ * ones for the process that is being successfully
+ * loaded
+ */
+ proc_lock(p);
+ p->p_csflags = imgp->ip_csflags;
+ proc_unlock(p);
+ DTRACE_PROC(exec__success);
+
+ if (uthread->uu_flag & UT_VFORK) {
+ vfork_return(p, retval, p->p_pid);
+ (void)thread_resume(imgp->ip_vfork_thread);
+ }
+ } else {
+ DTRACE_PROC1(exec__failure, int, error);
+ }
+
+ return(error);
+}
+
+
+/*
+ * copyinptr
+ *
+ * Description: Copy a pointer in from user space to a user_addr_t in kernel
+ * space, based on 32/64 bitness of the user space
+ *
+ * Parameters: froma User space address
+ * toptr Address of kernel space user_addr_t
+ * ptr_size 4/8, based on 'froma' address space
+ *
+ * Returns: 0 Success
+ * EFAULT Bad 'froma'
+ *
+ * Implicit returns:
+ * *ptr_size Modified
+ */
+static int
+copyinptr(user_addr_t froma, user_addr_t *toptr, int ptr_size)
+{
+ int error;
+
+ if (ptr_size == 4) {
+ /* 64 bit value containing 32 bit address */
+ unsigned int i;
+
+ error = copyin(froma, &i, 4);
+ *toptr = CAST_USER_ADDR_T(i); /* SAFE */
+ } else {
+ error = copyin(froma, toptr, 8);
+ }
+ return (error);
+}
+
+
+/*
+ * copyoutptr
+ *
+ * Description: Copy a pointer out from a user_addr_t in kernel space to
+ * user space, based on 32/64 bitness of the user space
+ *
+ * Parameters: ua User space address to copy to
+ * ptr Address of kernel space user_addr_t
+ * ptr_size 4/8, based on 'ua' address space
+ *
+ * Returns: 0 Success
+ * EFAULT Bad 'ua'
+ *
+ * Implicit returns:
+ * *ptr_size Modified
+ */
+static int
+copyoutptr(user_addr_t ua, user_addr_t ptr, int ptr_size)
+{
+ int error;
+
+ if (ptr_size == 4) {
+ /* 64 bit value containing 32 bit address */
+ unsigned int i = CAST_DOWN(unsigned int,ua); /* SAFE */
+
+ error = copyout(&i, ptr, 4);
+ } else {
+ error = copyout(&ua, ptr, 8);
+ }
+ return (error);
+}
+
+
+/*
+ * exec_copyout_strings
+ *
+ * Copy out the strings segment to user space. The strings segment is put
+ * on a preinitialized stack frame.
+ *
+ * Parameters: struct image_params * the image parameter block
+ * int * a pointer to the stack offset variable
+ *
+ * Returns: 0 Success
+ * !0 Faiure: errno
+ *
+ * Implicit returns:
+ * (*stackp) The stack offset, modified
+ *
+ * Note: The strings segment layout is backward, from the beginning
+ * of the top of the stack to consume the minimal amount of
+ * space possible; the returned stack pointer points to the
+ * end of the area consumed (stacks grow upward).
+ *
+ * argc is an int; arg[i] are pointers; env[i] are pointers;
+ * exec_path is a pointer; the 0's are (void *)NULL's
+ *
+ * The stack frame layout is:
+ *
+ * +-------------+
+ * sp-> | argc |
+ * +-------------+
+ * | arg[0] |
+ * +-------------+
+ * :
+ * :
+ * +-------------+
+ * | arg[argc-1] |
+ * +-------------+
+ * | 0 |
+ * +-------------+
+ * | env[0] |
+ * +-------------+
+ * :
+ * :
+ * +-------------+
+ * | env[n] |
+ * +-------------+
+ * | 0 |
+ * +-------------+
+ * | exec_path | In MacOS X PR2 Beaker2E the path passed to exec() is
+ * +-------------+ passed on the stack just after the trailing 0 of the
+ * | 0 | the envp[] array as a pointer to a string.
+ * +-------------+
+ * | PATH AREA |
+ * +-------------+
+ * | STRING AREA |
+ * :
+ * :
+ * | | <- p->user_stack
+ * +-------------+
+ *
+ * Although technically a part of the STRING AREA, we treat the PATH AREA as
+ * a separate entity. This allows us to align the beginning of the PATH AREA
+ * to a pointer boundary so that the exec_path, env[i], and argv[i] pointers
+ * which preceed it on the stack are properly aligned.
+ *
+ * TODO: argc copied with suword(), which takes a 64 bit address
+ */
+static int
+exec_copyout_strings(struct image_params *imgp, user_addr_t *stackp)
+{
+ proc_t p = vfs_context_proc(imgp->ip_vfs_context);
+ int ptr_size = (imgp->ip_flags & IMGPF_IS_64BIT) ? 8 : 4;
+ char *argv = imgp->ip_argv; /* modifiable copy of argv */
+ user_addr_t string_area; /* *argv[], *env[] */
+ user_addr_t path_area; /* package launch path */
+ user_addr_t ptr_area; /* argv[], env[], exec_path */
+ user_addr_t stack;
+ int stringc = imgp->ip_argc + imgp->ip_envc;
+ int len;
+ int error;
+ int strspace;
+
+ stack = *stackp;
+
+ unsigned patharea_len = imgp->ip_argv - imgp->ip_strings;
+ int envc_add = 0;
+
+ /*
+ * Set up pointers to the beginning of the string area, the beginning
+ * of the path area, and the beginning of the pointer area (actually,
+ * the location of argc, an int, which may be smaller than a pointer,
+ * but we use ptr_size worth of space for it, for alignment).
+ */
+ string_area = stack - (((imgp->ip_strendp - imgp->ip_strings) + ptr_size-1) & ~(ptr_size-1)) - ptr_size;
+ path_area = string_area - ((patharea_len + ptr_size-1) & ~(ptr_size-1));
+ ptr_area = path_area - ((imgp->ip_argc + imgp->ip_envc + 4 + envc_add) * ptr_size) - ptr_size /*argc*/;
+
+ /* Return the initial stack address: the location of argc */
+ *stackp = ptr_area;
+
+ /*
+ * Record the size of the arguments area so that sysctl_procargs()
+ * can return the argument area without having to parse the arguments.
+ */
+ proc_lock(p);
+ p->p_argc = imgp->ip_argc;
+ p->p_argslen = (int)(stack - path_area);
+ proc_unlock(p);
+
+
+ /*
+ * Support for new app package launching for Mac OS X allocates
+ * the "path" at the begining of the imgp->ip_strings buffer.
+ * copy it just before the string area.
+ */
+ len = 0;
+ error = copyoutstr(imgp->ip_strings, path_area,
+ patharea_len,
+ (size_t *)&len);
+ if (error)
+ goto bad;
+
+
+ /* Save a NULL pointer below it */
+ (void)copyoutptr(0LL, path_area - ptr_size, ptr_size);
+
+ /* Save the pointer to "path" just below it */
+ (void)copyoutptr(path_area, path_area - 2*ptr_size, ptr_size);
+
+ /*
+ * ptr_size for 2 NULL one each ofter arg[argc -1] and env[n]
+ * ptr_size for argc
+ * skip over saved path, ptr_size for pointer to path,
+ * and ptr_size for the NULL after pointer to path.
+ */
+
+ /* argc (int32, stored in a ptr_size area) */
+ (void)suword(ptr_area, imgp->ip_argc);
+ ptr_area += sizeof(int);
+ /* pad to ptr_size, if 64 bit image, to ensure user stack alignment */
+ if (imgp->ip_flags & IMGPF_IS_64BIT) {
+ (void)suword(ptr_area, 0); /* int, not long: ignored */
+ ptr_area += sizeof(int);
+ }
+
+#if CONFIG_DTRACE
+ p->p_dtrace_argv = ptr_area; /* user_addr_t &argv[0] for dtrace convenience */
+#endif /* CONFIG_DTRACE */
+
+ /*
+ * We use (string_area - path_area) here rather than the more
+ * intuitive (imgp->ip_argv - imgp->ip_strings) because we are
+ * interested in the length of the PATH_AREA in user space,
+ * rather than the actual length of the execution path, since
+ * it includes alignment padding of the PATH_AREA + STRING_AREA
+ * to a ptr_size boundary.
+ */
+ strspace = SIZE_IMG_STRSPACE - (string_area - path_area);
+ for (;;) {
+ if (stringc == imgp->ip_envc) {
+ /* argv[n] = NULL */
+ (void)copyoutptr(0LL, ptr_area, ptr_size);
+ ptr_area += ptr_size;
+#if CONFIG_DTRACE
+ p->p_dtrace_envp = ptr_area; /* user_addr_t &env[0] for dtrace convenience */
+#endif /* CONFIG_DTRACE */
+ }
+ if (--stringc < 0)
+ break;
+
+ /* pointer: argv[n]/env[n] */
+ (void)copyoutptr(string_area, ptr_area, ptr_size);
+
+ /* string : argv[n][]/env[n][] */
+ do {
+ if (strspace <= 0) {
+ error = E2BIG;
+ break;
+ }
+ error = copyoutstr(argv, string_area,
+ (unsigned)strspace,
+ (size_t *)&len);
+ string_area += len;
+ argv += len;
+ strspace -= len;
+ } while (error == ENAMETOOLONG);
+ if (error == EFAULT || error == E2BIG)
+ break; /* bad stack - user's problem */
+ ptr_area += ptr_size;
+ }
+ /* env[n] = NULL */
+ (void)copyoutptr(0LL, ptr_area, ptr_size);
+
+bad:
+ return(error);
+}
+
+
+/*
+ * exec_extract_strings
+ *
+ * Copy arguments and environment from user space into work area; we may
+ * have already copied some early arguments into the work area, and if
+ * so, any arguments opied in are appended to those already there.
+ *
+ * Parameters: struct image_params * the image parameter block
+ *
+ * Returns: 0 Success
+ * !0 Failure: errno
+ *
+ * Implicit returns;
+ * (imgp->ip_argc) Count of arguments, updated
+ * (imgp->ip_envc) Count of environment strings, updated
+ *
+ *
+ * Note: The argument and environment vectors are user space pointers
+ * to arrays of user space pointers.
+ */
+static int
+exec_extract_strings(struct image_params *imgp)
+{
+ int error = 0;
+ int ptr_size = (imgp->ip_flags & IMGPF_WAS_64BIT) ? 8 : 4;
+ user_addr_t argv = imgp->ip_user_argv;
+ user_addr_t envv = imgp->ip_user_envv;
+
+ /*
+ * If the argument vector is NULL, this is the system startup
+ * bootstrap from load_init_program(), and there's nothing to do