+
+ 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;
+
+ ip_lock_held(port);
+ require_ip_active(port);
+
+ if (ip_kotype(port) == IKOT_THREAD_CONTROL ||
+ ip_kotype(port) == IKOT_THREAD_READ ||
+ ip_kotype(port) == IKOT_THREAD_INSPECT) {
+ thread = (thread_inspect_t)port->ip_kobject;
+ assert(thread != THREAD_INSPECT_NULL);
+ thread_reference_internal((thread_t)thread);
+ }
+
+ return thread;
+}
+
+thread_inspect_t
+convert_port_to_thread_inspect(
+ ipc_port_t port)
+{
+ thread_inspect_t thread = THREAD_INSPECT_NULL;
+
+ if (IP_VALID(port)) {
+ ip_lock(port);
+ if (ip_active(port)) {
+ thread = convert_port_to_thread_inspect_locked(port);
+ }
+ ip_unlock(port);
+ }
+
+ return thread;
+}
+
+/*
+ * Routine: convert_port_to_thread_read
+ * Purpose:
+ * Convert from a port to a thread read right
+ * Doesn't consume the port ref; produces a thread ref,
+ * which may be null.
+ * Conditions:
+ * Nothing locked.
+ */
+static thread_read_t
+convert_port_to_thread_read_locked(
+ ipc_port_t port)
+{
+ thread_read_t thread = THREAD_READ_NULL;
+
+ ip_lock_held(port);
+ require_ip_active(port);
+
+ if (ip_kotype(port) == IKOT_THREAD_CONTROL ||
+ ip_kotype(port) == IKOT_THREAD_READ) {
+ thread = (thread_read_t) ip_get_kobject(port);
+ assert(thread != THREAD_READ_NULL);
+
+ /* Use task conversion rules for thread control conversions */
+ if (task_conversion_eval(current_task(), thread->task) != KERN_SUCCESS) {
+ return THREAD_READ_NULL;
+ }
+
+ thread_reference_internal((thread_t)thread);
+ }
+
+ return thread;
+}
+
+thread_read_t
+convert_port_to_thread_read(
+ ipc_port_t port)
+{
+ thread_read_t thread = THREAD_READ_NULL;
+
+ if (IP_VALID(port)) {
+ ip_lock(port);
+ if (ip_active(port)) {
+ thread = convert_port_to_thread_read_locked(port);
+ }
+ ip_unlock(port);
+ }
+
+ return thread;
+}
+
+
+/*
+ * Routine: convert_thread_to_port_with_flavor
+ * Purpose:
+ * Convert from a thread to a port of given flavor.
+ * Consumes a thread ref; produces a naked send right
+ * which may be invalid.
+ * Conditions:
+ * Nothing locked.
+ */
+static ipc_port_t
+convert_thread_to_port_with_flavor(
+ thread_t thread,
+ mach_thread_flavor_t flavor)
+{
+ ipc_port_t port = IP_NULL;
+
+ thread_mtx_lock(thread);
+
+ if (thread->ith_self[THREAD_FLAVOR_CONTROL] == IP_NULL) {
+ goto exit;
+ }
+
+ if (flavor == THREAD_FLAVOR_CONTROL) {
+ port = ipc_port_make_send(thread->ith_self[flavor]);
+ } else {
+ if (!thread->active) {
+ goto exit;
+ }
+ ipc_kobject_type_t kotype = (flavor == THREAD_FLAVOR_READ) ? IKOT_THREAD_READ : IKOT_THREAD_INSPECT;
+ /*
+ * Claim a send right on the thread read/inspect port, and request a no-senders
+ * notification on that port (if none outstanding). A thread reference is not
+ * donated here even though the ports are created lazily because it doesn't own the
+ * kobject that it points to. Threads manage their lifetime explicitly and
+ * have to synchronize with each other, between the task/thread terminating and the
+ * send-once notification firing, and this is done under the thread mutex
+ * rather than with atomics.
+ */
+ (void)ipc_kobject_make_send_lazy_alloc_port(&thread->ith_self[flavor], (ipc_kobject_t)thread,
+ kotype, false, 0);
+ port = thread->ith_self[flavor];
+ }
+
+exit:
+ thread_mtx_unlock(thread);
+ thread_deallocate(thread);
+ return port;
+}
+
+ipc_port_t
+convert_thread_to_port(
+ thread_t thread)
+{
+ return convert_thread_to_port_with_flavor(thread, THREAD_FLAVOR_CONTROL);
+}
+
+ipc_port_t
+convert_thread_read_to_port(thread_read_t thread)
+{
+ return convert_thread_to_port_with_flavor(thread, THREAD_FLAVOR_READ);
+}
+
+ipc_port_t
+convert_thread_inspect_to_port(thread_inspect_t thread)
+{
+ return convert_thread_to_port_with_flavor(thread, THREAD_FLAVOR_INSPECT);
+}
+
+
+/*
+ * Routine: port_name_to_thread
+ * Purpose:
+ * Convert from a port name to a thread reference
+ * A name of MACH_PORT_NULL is valid for the null thread.
+ * Conditions:
+ * Nothing locked.
+ */
+thread_t
+port_name_to_thread(
+ mach_port_name_t name,
+ port_to_thread_options_t options)
+{
+ thread_t thread = THREAD_NULL;
+ ipc_port_t kport;
+ kern_return_t kr;
+
+ if (MACH_PORT_VALID(name)) {
+ kr = ipc_port_translate_send(current_space(), name, &kport);
+ if (kr == KERN_SUCCESS) {
+ thread = convert_port_to_thread_locked(kport, options, TRUE);
+ ip_unlock(kport);
+ }
+ }
+
+ return thread;
+}
+
+/*
+ * Routine: port_name_to_task
+ * Purpose:
+ * Convert from a port name to a task reference
+ * A name of MACH_PORT_NULL is valid for the null task.
+ * Conditions:
+ * Nothing locked.
+ */
+task_t
+port_name_to_task(
+ mach_port_name_t name)
+{
+ ipc_port_t kport;
+ kern_return_t kr;
+ task_t task = TASK_NULL;
+
+ if (MACH_PORT_VALID(name)) {
+ kr = ipc_port_translate_send(current_space(), name, &kport);
+ if (kr == KERN_SUCCESS) {
+ task = convert_port_to_task_locked(kport, NULL, TRUE);
+ ip_unlock(kport);
+ }
+ }
+ return task;
+}
+
+/*
+ * Routine: port_name_to_task_read
+ * Purpose:
+ * Convert from a port name to a task reference
+ * A name of MACH_PORT_NULL is valid for the null task.
+ * Conditions:
+ * Nothing locked.
+ */
+task_read_t
+port_name_to_task_read(
+ mach_port_name_t name)
+{
+ ipc_port_t kport;
+ kern_return_t kr;
+ task_read_t tr = TASK_READ_NULL;
+
+ if (MACH_PORT_VALID(name)) {
+ kr = ipc_port_translate_send(current_space(), name, &kport);
+ if (kr == KERN_SUCCESS) {
+ tr = convert_port_to_task_read_locked(kport);
+ ip_unlock(kport);
+ }
+ }
+ return tr;
+}
+
+/*
+ * Routine: port_name_to_task_read_no_eval
+ * Purpose:
+ * Convert from a port name to a task reference
+ * A name of MACH_PORT_NULL is valid for the null task.
+ * It doesnt run the task_conversion_eval check if the port
+ * is of type IKOT_TASK_CONTROL.
+ * Conditions:
+ * Nothing locked.
+ */
+task_read_t
+port_name_to_task_read_no_eval(
+ mach_port_name_t name)
+{
+ ipc_port_t kport;
+ kern_return_t kr;
+ task_read_t tr = TASK_READ_NULL;
+
+ if (MACH_PORT_VALID(name)) {
+ kr = ipc_port_translate_send(current_space(), name, &kport);
+ if (kr == KERN_SUCCESS) {
+ switch (ip_kotype(kport)) {
+ case IKOT_TASK_CONTROL:
+ tr = convert_port_to_task_locked(kport, NULL, FALSE);
+ break;
+ case IKOT_TASK_READ:
+ tr = convert_port_to_task_read_locked(kport);
+ break;
+ default:
+ break;
+ }
+ ip_unlock(kport);
+ }
+ }
+ return tr;
+}
+
+/*
+ * Routine: port_name_to_task_inspect
+ * Purpose:
+ * Convert from a port name to a task reference
+ * A name of MACH_PORT_NULL is valid for the null task.
+ * Conditions:
+ * Nothing locked.
+ */
+task_inspect_t
+port_name_to_task_inspect(
+ mach_port_name_t name)
+{
+ ipc_port_t kport;
+ kern_return_t kr;
+ task_inspect_t ti = TASK_INSPECT_NULL;
+
+ if (MACH_PORT_VALID(name)) {
+ kr = ipc_port_translate_send(current_space(), name, &kport);
+ if (kr == KERN_SUCCESS) {
+ ti = convert_port_to_task_inspect_locked(kport);
+ ip_unlock(kport);
+ }
+ }
+ return ti;
+}
+
+/*
+ * Routine: port_name_to_task_name
+ * Purpose:
+ * Convert from a port name to a task reference
+ * A name of MACH_PORT_NULL is valid for the null task.
+ * Conditions:
+ * Nothing locked.
+ */
+task_name_t
+port_name_to_task_name(
+ mach_port_name_t name)
+{
+ ipc_port_t kport;
+ kern_return_t kr;
+ task_name_t tn = TASK_NAME_NULL;
+
+ if (MACH_PORT_VALID(name)) {
+ kr = ipc_port_translate_send(current_space(), name, &kport);
+ if (kr == KERN_SUCCESS) {
+ tn = convert_port_to_task_name_locked(kport);
+ ip_unlock(kport);
+ }
+ }
+ return tn;
+}
+
+/*
+ * Routine: port_name_to_host
+ * Purpose:
+ * Convert from a port name to a host pointer.
+ * NOTE: This does _not_ return a +1 reference to the host_t
+ * Conditions:
+ * Nothing locked.
+ */
+host_t
+port_name_to_host(
+ mach_port_name_t name)
+{
+ host_t host = HOST_NULL;
+ kern_return_t kr;
+ ipc_port_t port;
+
+ if (MACH_PORT_VALID(name)) {
+ kr = ipc_port_translate_send(current_space(), name, &port);
+ if (kr == KERN_SUCCESS) {
+ host = convert_port_to_host(port);
+ ip_unlock(port);
+ }
+ }
+ return host;