+ return task_set_special_port_internal(task, which, port);
+}
+
+/*
+ * Routine: task_set_special_port_internal
+ * Purpose:
+ * Changes one of the task's special ports,
+ * setting it to the supplied send right.
+ * Conditions:
+ * Nothing locked. If successful, consumes
+ * the supplied send right.
+ * Returns:
+ * KERN_SUCCESS Changed the special port.
+ * KERN_INVALID_ARGUMENT The task is null.
+ * KERN_FAILURE The task/space is dead.
+ * KERN_INVALID_ARGUMENT Invalid special port.
+ * KERN_NO_ACCESS Restricted access to overwrite port.
+ */
+
+kern_return_t
+task_set_special_port_internal(
+ task_t task,
+ int which,
+ ipc_port_t port)
+{
+ ipc_port_t old = IP_NULL;
+ kern_return_t rc = KERN_INVALID_ARGUMENT;
+
+ if (task == TASK_NULL) {
+ goto out;
+ }
+
+ itk_lock(task);
+ if (task->itk_self[TASK_FLAVOR_CONTROL] == IP_NULL) {
+ rc = KERN_FAILURE;
+ goto out_unlock;
+ }
+
+ switch (which) {
+ case TASK_KERNEL_PORT:
+ old = task->itk_settable_self;
+ task->itk_settable_self = port;
+ break;
+
+ case TASK_HOST_PORT:
+ old = task->itk_host;
+ task->itk_host = port;
+ break;
+
+ case TASK_BOOTSTRAP_PORT:
+ old = task->itk_bootstrap;
+ task->itk_bootstrap = port;
+ break;
+
+ /* Never allow overwrite of seatbelt port */
+ case TASK_SEATBELT_PORT:
+ if (IP_VALID(task->itk_seatbelt)) {
+ rc = KERN_NO_ACCESS;
+ goto out_unlock;
+ }
+ task->itk_seatbelt = port;
+ break;
+
+ /* Never allow overwrite of the task access port */
+ case TASK_ACCESS_PORT:
+ if (IP_VALID(task->itk_task_access)) {
+ rc = KERN_NO_ACCESS;
+ goto out_unlock;
+ }
+ task->itk_task_access = port;
+ break;
+
+ case TASK_DEBUG_CONTROL_PORT:
+ old = task->itk_debug_control;
+ task->itk_debug_control = port;
+ break;
+
+ default:
+ rc = KERN_INVALID_ARGUMENT;
+ goto out_unlock;
+ }/* switch */
+
+ rc = KERN_SUCCESS;
+
+out_unlock:
+ itk_unlock(task);
+
+ if (IP_VALID(old)) {
+ ipc_port_release_send(old);
+ }
+out:
+ return rc;
+}
+/*
+ * Routine: mach_ports_register [kernel call]
+ * Purpose:
+ * Stash a handful of port send rights in the task.
+ * Child tasks will inherit these rights, but they
+ * must use mach_ports_lookup to acquire them.
+ *
+ * The rights are supplied in a (wired) kalloc'd segment.
+ * Rights which aren't supplied are assumed to be null.
+ * Conditions:
+ * Nothing locked. If successful, consumes
+ * the supplied rights and memory.
+ * Returns:
+ * KERN_SUCCESS Stashed the port rights.
+ * KERN_INVALID_ARGUMENT The task is null.
+ * KERN_INVALID_ARGUMENT The task is dead.
+ * KERN_INVALID_ARGUMENT The memory param is null.
+ * KERN_INVALID_ARGUMENT Too many port rights supplied.
+ */
+
+kern_return_t
+mach_ports_register(
+ task_t task,
+ mach_port_array_t memory,
+ mach_msg_type_number_t portsCnt)
+{
+ ipc_port_t ports[TASK_PORT_REGISTER_MAX];
+ unsigned int i;
+
+ if ((task == TASK_NULL) ||
+ (portsCnt > TASK_PORT_REGISTER_MAX) ||
+ (portsCnt && memory == NULL)) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ /*
+ * Pad the port rights with nulls.
+ */
+
+ for (i = 0; i < portsCnt; i++) {
+ ports[i] = memory[i];
+ }
+ for (; i < TASK_PORT_REGISTER_MAX; i++) {
+ ports[i] = IP_NULL;
+ }
+
+ itk_lock(task);
+ if (task->itk_self[TASK_FLAVOR_CONTROL] == IP_NULL) {
+ itk_unlock(task);
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ /*
+ * Replace the old send rights with the new.
+ * Release the old rights after unlocking.
+ */
+
+ for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
+ ipc_port_t old;
+
+ old = task->itk_registered[i];
+ task->itk_registered[i] = ports[i];
+ ports[i] = old;
+ }
+
+ itk_unlock(task);
+
+ for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
+ if (IP_VALID(ports[i])) {
+ ipc_port_release_send(ports[i]);
+ }
+ }
+
+ /*
+ * Now that the operation is known to be successful,
+ * we can free the memory.
+ */
+
+ if (portsCnt != 0) {
+ kfree(memory,
+ (vm_size_t) (portsCnt * sizeof(mach_port_t)));
+ }
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_ports_lookup [kernel call]
+ * Purpose:
+ * Retrieves (clones) the stashed port send rights.
+ * Conditions:
+ * Nothing locked. If successful, the caller gets
+ * rights and memory.
+ * Returns:
+ * KERN_SUCCESS Retrieved the send rights.
+ * KERN_INVALID_ARGUMENT The task is null.
+ * KERN_INVALID_ARGUMENT The task is dead.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+mach_ports_lookup(
+ task_t task,
+ mach_port_array_t *portsp,
+ mach_msg_type_number_t *portsCnt)
+{
+ void *memory;
+ vm_size_t size;
+ ipc_port_t *ports;
+ int i;
+
+ if (task == TASK_NULL) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ size = (vm_size_t) (TASK_PORT_REGISTER_MAX * sizeof(ipc_port_t));
+
+ memory = kalloc(size);
+ if (memory == 0) {
+ return KERN_RESOURCE_SHORTAGE;
+ }
+
+ itk_lock(task);
+ if (task->itk_self[TASK_FLAVOR_CONTROL] == IP_NULL) {
+ itk_unlock(task);
+
+ kfree(memory, size);
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ ports = (ipc_port_t *) memory;
+
+ /*
+ * Clone port rights. Because kalloc'd memory
+ * is wired, we won't fault while holding the task lock.
+ */
+
+ for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
+ ports[i] = ipc_port_copy_send(task->itk_registered[i]);
+ }
+
+ itk_unlock(task);
+
+ *portsp = (mach_port_array_t) ports;
+ *portsCnt = TASK_PORT_REGISTER_MAX;
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+task_conversion_eval(task_t caller, task_t victim)
+{
+ /*
+ * Tasks are allowed to resolve their own task ports, and the kernel is
+ * allowed to resolve anyone's task port.
+ */
+ if (caller == kernel_task) {
+ return KERN_SUCCESS;
+ }
+
+ if (caller == victim) {
+ return KERN_SUCCESS;
+ }
+
+ /*
+ * Only the kernel can can resolve the kernel's task port. We've established
+ * by this point that the caller is not kernel_task.
+ */
+ if (victim == TASK_NULL || victim == kernel_task) {
+ return KERN_INVALID_SECURITY;
+ }
+
+ task_require(victim);
+
+#if !defined(XNU_TARGET_OS_OSX)
+ /*
+ * On platforms other than macOS, only a platform binary can resolve the task port
+ * of another platform binary.
+ */
+ if ((victim->t_flags & TF_PLATFORM) && !(caller->t_flags & TF_PLATFORM)) {
+#if SECURE_KERNEL
+ return KERN_INVALID_SECURITY;
+#else
+ if (cs_relax_platform_task_ports) {
+ return KERN_SUCCESS;
+ } else {
+ return KERN_INVALID_SECURITY;
+ }
+#endif /* SECURE_KERNEL */
+ }
+#endif /* !defined(XNU_TARGET_OS_OSX) */
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: convert_port_to_locked_task
+ * Purpose:
+ * Internal helper routine to convert from a port to a locked
+ * task. Used by several routines that try to convert from a
+ * task port to a reference on some task related object.
+ * Conditions:
+ * Nothing locked, blocking OK.
+ */
+task_t
+convert_port_to_locked_task(ipc_port_t port, boolean_t eval)
+{
+ int try_failed_count = 0;
+
+ while (IP_VALID(port)) {
+ task_t ct = current_task();
+ task_t task;
+
+ ip_lock(port);
+ if (!ip_active(port) || (ip_kotype(port) != IKOT_TASK_CONTROL)) {
+ ip_unlock(port);
+ return TASK_NULL;
+ }
+ task = (task_t) ip_get_kobject(port);
+ assert(task != TASK_NULL);
+
+ if (eval && task_conversion_eval(ct, task)) {
+ ip_unlock(port);
+ return TASK_NULL;
+ }
+
+ /*
+ * Normal lock ordering puts task_lock() before ip_lock().
+ * Attempt out-of-order locking here.
+ */
+ if (task_lock_try(task)) {
+ ip_unlock(port);
+ return task;
+ }
+ try_failed_count++;
+
+ ip_unlock(port);
+ mutex_pause(try_failed_count);
+ }
+ return TASK_NULL;
+}
+
+/*
+ * Routine: convert_port_to_locked_task_inspect
+ * Purpose:
+ * Internal helper routine to convert from a port to a locked
+ * task inspect right. Used by internal routines that try to convert from a
+ * task inspect port to a reference on some task related object.
+ * Conditions:
+ * Nothing locked, blocking OK.
+ */
+task_inspect_t
+convert_port_to_locked_task_inspect(ipc_port_t port)
+{
+ int try_failed_count = 0;
+
+ while (IP_VALID(port)) {
+ task_inspect_t task;
+
+ ip_lock(port);
+ if (!ip_active(port) || (ip_kotype(port) != IKOT_TASK_CONTROL &&
+ ip_kotype(port) != IKOT_TASK_READ &&
+ ip_kotype(port) != IKOT_TASK_INSPECT)) {
+ ip_unlock(port);
+ return TASK_INSPECT_NULL;
+ }
+ task = (task_inspect_t) ip_get_kobject(port);
+ assert(task != TASK_INSPECT_NULL);
+ /*
+ * Normal lock ordering puts task_lock() before ip_lock().
+ * Attempt out-of-order locking here.
+ */
+ if (task_lock_try((task_t)task)) {
+ ip_unlock(port);
+ return task;
+ }
+ try_failed_count++;
+
+ ip_unlock(port);
+ mutex_pause(try_failed_count);
+ }
+ return TASK_INSPECT_NULL;
+}
+
+/*
+ * Routine: convert_port_to_locked_task_read
+ * Purpose:
+ * Internal helper routine to convert from a port to a locked
+ * task read right. Used by internal routines that try to convert from a
+ * task read port to a reference on some task related object.
+ * Conditions:
+ * Nothing locked, blocking OK.
+ */
+task_read_t
+convert_port_to_locked_task_read(ipc_port_t port)
+{
+ int try_failed_count = 0;
+
+ while (IP_VALID(port)) {
+ task_read_t task;
+
+ ip_lock(port);
+ if (!ip_active(port) || (ip_kotype(port) != IKOT_TASK_CONTROL &&
+ ip_kotype(port) != IKOT_TASK_READ)) {
+ ip_unlock(port);
+ return TASK_READ_NULL;
+ }
+ task = (task_read_t)port->ip_kobject;
+ assert(task != TASK_READ_NULL);
+ /*
+ * Normal lock ordering puts task_lock() before ip_lock().
+ * Attempt out-of-order locking here.
+ */
+ if (task_lock_try((task_t)task)) {
+ ip_unlock(port);
+ return task;
+ }
+ try_failed_count++;
+
+ ip_unlock(port);
+ mutex_pause(try_failed_count);
+ }
+ return TASK_READ_NULL;
+}
+
+static task_t
+convert_port_to_task_locked(
+ ipc_port_t port,
+ uint32_t *exec_token,
+ boolean_t eval)
+{
+ task_t task = TASK_NULL;
+
+ ip_lock_held(port);
+ require_ip_active(port);
+
+ if (ip_kotype(port) == IKOT_TASK_CONTROL) {
+ task = (task_t) ip_get_kobject(port);
+ assert(task != TASK_NULL);
+
+ if (eval && task_conversion_eval(current_task(), task)) {
+ return TASK_NULL;
+ }
+
+ if (exec_token) {
+ *exec_token = task->exec_token;
+ }
+
+ task_reference_internal(task);
+ }
+
+ return task;
+}
+
+/*
+ * Routine: convert_port_to_task_with_exec_token
+ * Purpose:
+ * Convert from a port to a task and return
+ * the exec token stored in the task.
+ * Doesn't consume the port ref; produces a task ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+task_t
+convert_port_to_task_with_exec_token(
+ ipc_port_t port,
+ uint32_t *exec_token,
+ boolean_t eval)
+{
+ task_t task = TASK_NULL;
+
+ if (IP_VALID(port)) {
+ ip_lock(port);
+ if (ip_active(port)) {
+ task = convert_port_to_task_locked(port, exec_token, eval);
+ }
+ ip_unlock(port);
+ }
+
+ return task;
+}
+
+/*
+ * Routine: convert_port_to_task
+ * Purpose:
+ * Convert from a port to a task.
+ * Doesn't consume the port ref; produces a task ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+task_t
+convert_port_to_task(
+ ipc_port_t port)
+{
+ return convert_port_to_task_with_exec_token(port, NULL, TRUE);
+}
+
+/*
+ * Routine: convert_port_to_task_no_eval
+ * Purpose:
+ * Convert from a port to a task, skips task_conversion_eval.
+ * Doesn't consume the port ref; produces a task ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+static task_t
+convert_port_to_task_no_eval(
+ ipc_port_t port)
+{
+ return convert_port_to_task_with_exec_token(port, NULL, FALSE);
+}
+
+/*
+ * Routine: convert_port_to_task_name
+ * Purpose:
+ * Convert from a port to a task name.
+ * Doesn't consume the port ref; produces a task name ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+
+static task_name_t
+convert_port_to_task_name_locked(
+ ipc_port_t port)
+{
+ task_name_t task = TASK_NAME_NULL;
+
+ ip_lock_held(port);
+ require_ip_active(port);
+
+ if (ip_kotype(port) == IKOT_TASK_CONTROL ||
+ ip_kotype(port) == IKOT_TASK_READ ||
+ ip_kotype(port) == IKOT_TASK_INSPECT ||
+ ip_kotype(port) == IKOT_TASK_NAME) {
+ task = (task_name_t) ip_get_kobject(port);
+ assert(task != TASK_NAME_NULL);
+
+ task_reference_internal(task);
+ }
+
+ return task;
+}
+
+task_name_t
+convert_port_to_task_name(
+ ipc_port_t port)
+{
+ task_name_t task = TASK_NULL;
+
+ if (IP_VALID(port)) {
+ ip_lock(port);
+ if (ip_active(port)) {
+ task = convert_port_to_task_name_locked(port);
+ }
+ ip_unlock(port);
+ }
+
+ return task;
+}
+
+/*
+ * Routine: convert_port_to_task_policy
+ * Purpose:
+ * Convert from a port to a task.
+ * Doesn't consume the port ref; produces a task ref,
+ * which may be null.
+ * If the port is being used with task_port_set(), any task port
+ * type other than TASK_CONTROL requires an entitlement. If the
+ * port is being used with task_port_get(), TASK_NAME requires an
+ * entitlement.
+ * Conditions:
+ * Nothing locked.
+ */
+static task_t
+convert_port_to_task_policy(ipc_port_t port, boolean_t set)
+{
+ task_t task = TASK_NULL;
+ task_t ctask = current_task();
+
+ if (!IP_VALID(port)) {
+ return TASK_NULL;
+ }
+
+ task = set ?
+ convert_port_to_task(port) :
+ convert_port_to_task_inspect(port);
+
+ if (task == TASK_NULL &&
+ IOTaskHasEntitlement(ctask, "com.apple.private.task_policy")) {
+ task = convert_port_to_task_name(port);
+ }
+
+ if (task_conversion_eval(ctask, task) != KERN_SUCCESS) {
+ task_deallocate(task);
+ return TASK_NULL;
+ }
+
+ return task;
+}
+
+task_policy_set_t
+convert_port_to_task_policy_set(ipc_port_t port)
+{
+ return convert_port_to_task_policy(port, true);
+}
+
+task_policy_get_t
+convert_port_to_task_policy_get(ipc_port_t port)
+{
+ return convert_port_to_task_policy(port, false);
+}
+
+static task_inspect_t
+convert_port_to_task_inspect_locked(
+ ipc_port_t port)
+{
+ task_inspect_t task = TASK_INSPECT_NULL;
+
+ ip_lock_held(port);
+ require_ip_active(port);
+
+ if (ip_kotype(port) == IKOT_TASK_CONTROL ||
+ ip_kotype(port) == IKOT_TASK_READ ||
+ ip_kotype(port) == IKOT_TASK_INSPECT) {
+ task = (task_inspect_t) ip_get_kobject(port);
+ assert(task != TASK_INSPECT_NULL);
+
+ task_reference_internal(task);
+ }
+
+ return task;
+}
+
+static task_read_t
+convert_port_to_task_read_locked(
+ ipc_port_t port)
+{
+ task_read_t task = TASK_READ_NULL;
+
+ ip_lock_held(port);
+ require_ip_active(port);
+
+ if (ip_kotype(port) == IKOT_TASK_CONTROL ||
+ ip_kotype(port) == IKOT_TASK_READ) {
+ task_t ct = current_task();
+ task = (task_t)port->ip_kobject;
+
+ assert(task != TASK_READ_NULL);
+
+ if (task_conversion_eval(ct, task)) {
+ return TASK_READ_NULL;
+ }
+
+ task_reference_internal(task);
+ }
+
+ return task;
+}
+
+/*
+ * Routine: convert_port_to_task_check_type
+ * Purpose:
+ * Convert from a port to a task based on port's type.
+ * Doesn't consume the port ref; produces a task ref,
+ * which may be null.
+ * Arguments:
+ * port: The port that we do conversion on
+ * kotype: Returns the IKOT_TYPE of the port, if translation succeeded
+ * at_most: The lowest capability flavor allowed. In mach_task_flavor_t,
+ * the higher the flavor number, the lesser the capability, hence the name.
+ * eval_check: Whether to run task_conversion_eval check during the conversion.
+ * For backward compatibility, some interfaces does not run conversion
+ * eval on IKOT_TASK_CONTROL.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * task_t and port's type, if translation succeeded;
+ * TASK_NULL and IKOT_NONE, if translation failed
+ */
+task_t
+convert_port_to_task_check_type(
+ ipc_port_t port,
+ ipc_kobject_type_t *kotype,
+ mach_task_flavor_t at_most,
+ boolean_t eval_check)
+{
+ task_t task = TASK_NULL;
+ ipc_kobject_type_t type = IKOT_NONE;
+
+ if (!IP_VALID(port) || !ip_active(port)) {
+ goto out;
+ }
+
+ switch (ip_kotype(port)) {
+ case IKOT_TASK_CONTROL:
+ task = eval_check ? convert_port_to_task(port) : convert_port_to_task_no_eval(port);
+ if (task != TASK_NULL) {
+ type = IKOT_TASK_CONTROL;
+ }
+ break;
+ case IKOT_TASK_READ:
+ if (at_most >= TASK_FLAVOR_READ) {
+ task = convert_port_to_task_read(port);
+ if (task != TASK_READ_NULL) {
+ type = IKOT_TASK_READ;
+ }
+ }
+ break;
+ case IKOT_TASK_INSPECT:
+ if (at_most >= TASK_FLAVOR_INSPECT) {
+ task = convert_port_to_task_inspect(port);
+ if (task != TASK_INSPECT_NULL) {
+ type = IKOT_TASK_INSPECT;
+ }
+ }
+ break;
+ case IKOT_TASK_NAME:
+ if (at_most >= TASK_FLAVOR_NAME) {
+ task = convert_port_to_task_name(port);
+ if (task != TASK_NAME_NULL) {
+ type = IKOT_TASK_NAME;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+out:
+ if (kotype) {
+ *kotype = type;
+ }
+ return task;
+}
+
+/*
+ * Routine: convert_port_to_thread_check_type
+ * Purpose:
+ * Convert from a port to a thread based on port's type.
+ * Doesn't consume the port ref; produces a thread ref,
+ * which may be null.
+ * This conversion routine is _ONLY_ supposed to be used
+ * by thread_get_special_port.
+ * Arguments:
+ * port: The port that we do conversion on
+ * kotype: Returns the IKOT_TYPE of the port, if translation succeeded
+ * at_most: The lowest capability flavor allowed. In mach_thread_flavor_t,
+ * the higher the flavor number, the lesser the capability, hence the name.
+ * eval_check: Whether to run task_conversion_eval check during the conversion.
+ * For backward compatibility, some interfaces do not run
+ * conversion eval on IKOT_THREAD_CONTROL.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * thread_t and port's type, if translation succeeded;
+ * THREAD_NULL and IKOT_NONE, if translation failed
+ */
+thread_t
+convert_port_to_thread_check_type(
+ ipc_port_t port,
+ ipc_kobject_type_t *kotype,
+ mach_thread_flavor_t at_most,
+ boolean_t eval_check)
+{
+ thread_t thread = THREAD_NULL;
+ ipc_kobject_type_t type = IKOT_NONE;
+
+ if (!IP_VALID(port) || !ip_active(port)) {
+ goto out;
+ }
+
+ switch (ip_kotype(port)) {
+ case IKOT_THREAD_CONTROL:
+ thread = eval_check ? convert_port_to_thread(port) : convert_port_to_thread_no_eval(port);
+ if (thread != THREAD_NULL) {
+ type = IKOT_THREAD_CONTROL;
+ }
+ break;
+ case IKOT_THREAD_READ:
+ if (at_most >= THREAD_FLAVOR_READ) {
+ thread = convert_port_to_thread_read(port);
+ if (thread != THREAD_READ_NULL) {
+ type = IKOT_THREAD_READ;
+ }
+ }
+ break;
+ case IKOT_THREAD_INSPECT:
+ if (at_most >= THREAD_FLAVOR_INSPECT) {
+ thread = convert_port_to_thread_inspect(port);
+ if (thread != THREAD_INSPECT_NULL) {
+ type = IKOT_THREAD_INSPECT;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+out:
+ if (kotype) {
+ *kotype = type;
+ }
+ return thread;
+}
+
+/*
+ * Routine: convert_port_to_space_check_type
+ * Purpose:
+ * Convert from a port to a space based on port's type.
+ * Doesn't consume the port ref; produces a space ref,
+ * which may be null.
+ * Arguments:
+ * port: The port that we do conversion on
+ * kotype: Returns the IKOT_TYPE of the port, if translation succeeded
+ * at_most: The lowest capability flavor allowed. In mach_task_flavor_t,
+ * the higher the flavor number, the lesser the capability, hence the name.
+ * eval_check: Whether to run task_conversion_eval check during the conversion.
+ * For backward compatibility, some interfaces do not run
+ * conversion eval on IKOT_TASK_CONTROL.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * ipc_space_t and port's type, if translation succeeded;
+ * IPC_SPACE_NULL and IKOT_NONE, if translation failed
+ */
+ipc_space_t
+convert_port_to_space_check_type(
+ ipc_port_t port,
+ ipc_kobject_type_t *kotype,
+ mach_task_flavor_t at_most,
+ boolean_t eval_check)
+{
+ ipc_space_t space = IPC_SPACE_NULL;
+ ipc_kobject_type_t type = IKOT_NONE;
+
+ if (!IP_VALID(port) || !ip_active(port)) {
+ goto out;
+ }
+
+ switch (ip_kotype(port)) {
+ case IKOT_TASK_CONTROL:
+ space = eval_check ? convert_port_to_space(port) : convert_port_to_space_no_eval(port);
+ if (space != IPC_SPACE_NULL) {
+ type = IKOT_TASK_CONTROL;
+ }
+ break;
+ case IKOT_TASK_READ:
+ if (at_most >= TASK_FLAVOR_READ) {
+ space = convert_port_to_space_read(port);
+ if (space != IPC_SPACE_READ_NULL) {
+ type = IKOT_TASK_READ;
+ }
+ }
+ break;
+ case IKOT_TASK_INSPECT:
+ if (at_most >= TASK_FLAVOR_INSPECT) {
+ space = convert_port_to_space_inspect(port);
+ if (space != IPC_SPACE_INSPECT_NULL) {
+ type = IKOT_TASK_INSPECT;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+out:
+ if (kotype) {
+ *kotype = type;
+ }
+ return space;
+}
+
+/*
+ * Routine: convert_port_to_task_inspect
+ * Purpose:
+ * Convert from a port to a task inspection right
+ * Doesn't consume the port ref; produces a task ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+task_inspect_t
+convert_port_to_task_inspect(
+ ipc_port_t port)
+{
+ task_inspect_t task = TASK_INSPECT_NULL;
+
+ if (IP_VALID(port)) {
+ ip_lock(port);
+ if (ip_active(port)) {
+ task = convert_port_to_task_inspect_locked(port);
+ }
+ ip_unlock(port);
+ }
+
+ return task;
+}
+
+/*
+ * Routine: convert_port_to_task_read
+ * Purpose:
+ * Convert from a port to a task read right
+ * Doesn't consume the port ref; produces a task ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+task_read_t
+convert_port_to_task_read(
+ ipc_port_t port)
+{
+ task_read_t task = TASK_READ_NULL;
+
+ if (IP_VALID(port)) {
+ ip_lock(port);
+ if (ip_active(port)) {
+ task = convert_port_to_task_read_locked(port);
+ }
+ ip_unlock(port);
+ }
+
+ return task;
+}
+
+/*
+ * Routine: convert_port_to_task_suspension_token
+ * Purpose:
+ * Convert from a port to a task suspension token.
+ * Doesn't consume the port ref; produces a suspension token ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+task_suspension_token_t
+convert_port_to_task_suspension_token(
+ ipc_port_t port)
+{
+ task_suspension_token_t task = TASK_NULL;
+
+ if (IP_VALID(port)) {
+ ip_lock(port);
+
+ if (ip_active(port) &&
+ ip_kotype(port) == IKOT_TASK_RESUME) {
+ task = (task_suspension_token_t) ip_get_kobject(port);
+ assert(task != TASK_NULL);
+
+ task_reference_internal(task);
+ }
+
+ ip_unlock(port);
+ }
+
+ return task;
+}
+
+/*
+ * Routine: convert_port_to_space_with_flavor
+ * Purpose:
+ * Convert from a port to a space.
+ * Doesn't consume the port ref; produces a space ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+static ipc_space_t
+convert_port_to_space_with_flavor(
+ ipc_port_t port,
+ mach_task_flavor_t flavor,
+ boolean_t eval)
+{
+ ipc_space_t space;
+ task_t task;
+
+ switch (flavor) {
+ case TASK_FLAVOR_CONTROL:
+ task = convert_port_to_locked_task(port, eval);
+ break;
+ case TASK_FLAVOR_READ:
+ task = convert_port_to_locked_task_read(port);
+ break;
+ case TASK_FLAVOR_INSPECT:
+ task = convert_port_to_locked_task_inspect(port);
+ break;
+ default:
+ task = TASK_NULL;
+ break;
+ }
+
+ if (task == TASK_NULL) {
+ return IPC_SPACE_NULL;
+ }
+
+ if (!task->active) {
+ task_unlock(task);
+ return IPC_SPACE_NULL;
+ }
+
+ space = task->itk_space;
+ is_reference(space);
+ task_unlock(task);
+ return space;
+}
+
+ipc_space_t
+convert_port_to_space(
+ ipc_port_t port)
+{
+ return convert_port_to_space_with_flavor(port, TASK_FLAVOR_CONTROL, TRUE);
+}
+
+static ipc_space_t
+convert_port_to_space_no_eval(
+ ipc_port_t port)
+{
+ return convert_port_to_space_with_flavor(port, TASK_FLAVOR_CONTROL, FALSE);
+}
+
+ipc_space_read_t
+convert_port_to_space_read(
+ ipc_port_t port)
+{
+ return convert_port_to_space_with_flavor(port, TASK_FLAVOR_READ, TRUE);
+}
+
+ipc_space_inspect_t
+convert_port_to_space_inspect(
+ ipc_port_t port)
+{
+ return convert_port_to_space_with_flavor(port, TASK_FLAVOR_INSPECT, TRUE);
+}
+
+/*
+ * Routine: convert_port_to_map_with_flavor
+ * Purpose:
+ * Convert from a port to a map.
+ * Doesn't consume the port ref; produces a map ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+
+static vm_map_t
+convert_port_to_map_with_flavor(
+ ipc_port_t port,
+ mach_task_flavor_t flavor)
+{
+ task_t task;
+ vm_map_t map;
+
+ switch (flavor) {
+ case TASK_FLAVOR_CONTROL:
+ task = convert_port_to_locked_task(port, TRUE);
+ break;
+ case TASK_FLAVOR_READ:
+ task = convert_port_to_locked_task_read(port);
+ break;
+ case TASK_FLAVOR_INSPECT:
+ task = convert_port_to_locked_task_inspect(port);
+ break;
+ default:
+ task = TASK_NULL;
+ break;
+ }
+
+ if (task == TASK_NULL) {
+ return VM_MAP_NULL;
+ }
+
+ if (!task->active) {
+ task_unlock(task);
+ return VM_MAP_NULL;
+ }
+
+ map = task->map;
+ if (map->pmap == kernel_pmap) {
+ if (flavor == TASK_FLAVOR_CONTROL) {
+ panic("userspace has control access to a "
+ "kernel map %p through task %p", map, task);
+ }
+ if (task != kernel_task) {
+ panic("userspace has access to a "
+ "kernel map %p through task %p", map, task);
+ }
+ } else {
+ pmap_require(map->pmap);
+ }
+
+ vm_map_reference_swap(map);
+ task_unlock(task);
+ return map;
+}
+
+vm_map_read_t
+convert_port_to_map(
+ ipc_port_t port)
+{
+ return convert_port_to_map_with_flavor(port, TASK_FLAVOR_CONTROL);
+}
+
+vm_map_read_t
+convert_port_to_map_read(
+ ipc_port_t port)
+{
+ return convert_port_to_map_with_flavor(port, TASK_FLAVOR_READ);
+}
+
+vm_map_inspect_t
+convert_port_to_map_inspect(
+ ipc_port_t port)
+{
+ return convert_port_to_map_with_flavor(port, TASK_FLAVOR_INSPECT);
+}
+
+
+/*
+ * Routine: convert_port_to_thread
+ * Purpose:
+ * Convert from a port to a thread.
+ * Doesn't consume the port ref; produces an thread ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+
+static thread_t
+convert_port_to_thread_locked(
+ ipc_port_t port,
+ port_to_thread_options_t options,
+ boolean_t eval)
+{
+ thread_t thread = THREAD_NULL;
+
+ ip_lock_held(port);
+ require_ip_active(port);
+
+ if (ip_kotype(port) == IKOT_THREAD_CONTROL) {
+ thread = (thread_t) ip_get_kobject(port);
+ assert(thread != THREAD_NULL);
+
+ if (options & PORT_TO_THREAD_NOT_CURRENT_THREAD) {
+ if (thread == current_thread()) {
+ return THREAD_NULL;
+ }
+ }
+
+ if (options & PORT_TO_THREAD_IN_CURRENT_TASK) {
+ if (thread->task != current_task()) {
+ return THREAD_NULL;
+ }
+ } else {
+ /* Use task conversion rules for thread control conversions */
+ if (eval && task_conversion_eval(current_task(), thread->task) != KERN_SUCCESS) {
+ return THREAD_NULL;
+ }
+ }
+
+ thread_reference_internal(thread);
+ }
+
+ return thread;
+}
+
+thread_t
+convert_port_to_thread(
+ ipc_port_t port)
+{
+ thread_t thread = THREAD_NULL;
+
+ if (IP_VALID(port)) {
+ ip_lock(port);
+ if (ip_active(port)) {
+ thread = convert_port_to_thread_locked(port, PORT_TO_THREAD_NONE, TRUE);
+ }
+ ip_unlock(port);
+ }
+
+ return thread;
+}
+
+static thread_t
+convert_port_to_thread_no_eval(
+ ipc_port_t port)
+{
+ thread_t thread = THREAD_NULL;
+
+ if (IP_VALID(port)) {
+ ip_lock(port);
+ if (ip_active(port)) {
+ thread = convert_port_to_thread_locked(port, PORT_TO_THREAD_NONE, FALSE);
+ }
+ ip_unlock(port);
+ }
+
+ return thread;
+}
+
+/*
+ * Routine: convert_port_to_thread_inspect
+ * Purpose:
+ * Convert from a port to a thread inspect right
+ * Doesn't consume the port ref; produces a thread ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+static thread_inspect_t
+convert_port_to_thread_inspect_locked(
+ ipc_port_t port)
+{
+ thread_inspect_t thread = THREAD_INSPECT_NULL;