This is xnu-12377.1.9. See this file in:
/*
* Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* @OSF_COPYRIGHT@
*/
/*
* Mach Operating System
* Copyright (c) 1991,1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
/*
*/
/*
* File: ipc/mach_debug.c
* Author: Rich Draves
* Date: 1989
*
* Exported IPC debug calls.
*/
#include <mach/vm_param.h>
#include <mach/kern_return.h>
#include <mach/machine/vm_types.h>
#include <mach/mach_host_server.h>
#include <mach/mach_port_server.h>
#include <mach_debug/ipc_info.h>
#include <mach_debug/hash_info.h>
#include <kern/task_ident.h>
#include <kern/host.h>
#include <kern/misc_protos.h>
#include <vm/vm_map_xnu.h>
#include <vm/vm_memory_entry_xnu.h>
#include <vm/vm_kern_xnu.h>
#include <ipc/port.h>
#include <ipc/ipc_types.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_hash.h>
#include <ipc/ipc_right.h>
#include <security/mac_mach_internal.h>
#include <device/device_types.h>
/*
* Routine: mach_port_get_srights [kernel call]
* Purpose:
* Retrieve the number of extant send rights
* that a receive right has.
* Conditions:
* Nothing locked.
* Returns:
* KERN_SUCCESS Retrieved number of send rights.
* KERN_INVALID_TASK The space is null.
* KERN_INVALID_TASK The space is dead.
* KERN_INVALID_NAME The name doesn't denote a right.
* KERN_INVALID_RIGHT Name doesn't denote receive rights.
*/
kern_return_t
mach_port_get_srights(
ipc_space_t space,
mach_port_name_t name,
mach_port_rights_t *srightsp)
{
ipc_port_t port;
kern_return_t kr;
mach_port_rights_t srights;
if (space == IS_NULL) {
return KERN_INVALID_TASK;
}
kr = ipc_port_translate_receive(space, name, &port);
if (kr != KERN_SUCCESS) {
return kr;
}
/* port is locked and active */
srights = port->ip_srights;
ip_mq_unlock(port);
*srightsp = srights;
return KERN_SUCCESS;
}
/*
* Routine: mach_port_space_info
* Purpose:
* Returns information about an IPC space.
* Conditions:
* Nothing locked. Obeys CountInOut protocol.
* Returns:
* KERN_SUCCESS Returned information.
* KERN_INVALID_TASK The space is null.
* KERN_INVALID_TASK The space is dead.
* KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
*/
static kern_return_t
mach_port_space_info(
ipc_space_t space,
ipc_info_space_t *infop,
ipc_info_name_array_t *tablep,
mach_msg_type_number_t *tableCntp,
__unused ipc_info_tree_name_array_t *treep,
__unused mach_msg_type_number_t *treeCntp)
{
const uint32_t BATCH_SIZE = 4 << 10;
ipc_info_name_t *table_info;
vm_offset_t table_addr = 0;
vm_size_t table_size, table_size_needed;
ipc_entry_table_t table;
ipc_entry_num_t tsize;
kern_return_t kr;
vm_map_copy_t copy;
if (space == IS_NULL) {
return KERN_INVALID_TASK;
}
/* start with in-line memory */
table_size = 0;
ipc_object_t *port_pointers = NULL;
ipc_entry_num_t pptrsize = 0;
is_read_lock(space);
allocate_loop:
for (;;) {
if (!is_active(space)) {
is_read_unlock(space);
if (table_size != 0) {
kmem_free(ipc_kernel_map,
table_addr, table_size);
kfree_type(ipc_object_t, pptrsize, port_pointers);
port_pointers = NULL;
}
return KERN_INVALID_TASK;
}
table = is_active_table(space);
tsize = ipc_entry_table_count(table);
table_size_needed =
vm_map_round_page(tsize * sizeof(ipc_info_name_t),
VM_MAP_PAGE_MASK(ipc_kernel_map));
if ((table_size_needed <= table_size) &&
(pptrsize == tsize)) {
break;
}
is_read_unlock(space);
if (table_size != 0) {
kmem_free(ipc_kernel_map, table_addr, table_size);
kfree_type(ipc_object_t, pptrsize, port_pointers);
}
kr = kmem_alloc(ipc_kernel_map, &table_addr, table_size_needed,
KMA_DATA, VM_KERN_MEMORY_IPC);
if (kr != KERN_SUCCESS) {
return KERN_RESOURCE_SHORTAGE;
}
port_pointers = kalloc_type(ipc_object_t, tsize, Z_WAITOK | Z_ZERO);
if (port_pointers == NULL) {
kmem_free(ipc_kernel_map, table_addr, table_size);
return KERN_RESOURCE_SHORTAGE;
}
table_size = table_size_needed;
pptrsize = tsize;
is_read_lock(space);
}
/* space is read-locked and active; we have enough wired memory */
/* walk the table for this space */
table_info = (ipc_info_name_array_t)table_addr;
for (mach_port_index_t index = 0; index < tsize; index++) {
ipc_info_name_t *iin = &table_info[index];
ipc_entry_t entry = ipc_entry_table_get_nocheck(table, index);
ipc_entry_bits_t bits;
if (index == 0) {
bits = IE_BITS_GEN_MASK;
} else {
bits = entry->ie_bits;
}
iin->iin_name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits));
iin->iin_collision = 0;
iin->iin_type = IE_BITS_TYPE(bits);
if ((bits & MACH_PORT_TYPE_PORT_RIGHTS) != MACH_PORT_TYPE_NONE &&
entry->ie_request != IE_REQ_NONE) {
ipc_port_t port = entry->ie_port;
assert(IP_VALID(port));
ip_mq_lock(port);
iin->iin_type |= ipc_port_request_type(port, iin->iin_name, entry->ie_request);
ip_mq_unlock(port);
}
iin->iin_urefs = IE_BITS_UREFS(bits);
port_pointers[index] = entry->ie_object;
iin->iin_next = entry->ie_next;
iin->iin_hash = entry->ie_index;
if (index + 1 < tsize && (index + 1) % BATCH_SIZE == 0) {
/*
* Give the system some breathing room,
* and check if anything changed,
* if yes start over.
*/
is_read_unlock(space);
is_read_lock(space);
if (!is_active(space)) {
goto allocate_loop;
}
table = is_active_table(space);
if (tsize < ipc_entry_table_count(table)) {
goto allocate_loop;
}
tsize = ipc_entry_table_count(table);
}
}
/* get the overall space info */
infop->iis_genno_mask = MACH_PORT_NGEN(MACH_PORT_DEAD);
infop->iis_table_size = tsize;
is_read_unlock(space);
/* prepare the table out-of-line data for return */
if (table_size > 0) {
vm_map_size_t used = tsize * sizeof(ipc_info_name_t);
vm_map_size_t keep = vm_map_round_page(used,
VM_MAP_PAGE_MASK(ipc_kernel_map));
assert(pptrsize >= tsize);
for (int index = 0; index < tsize; index++) {
ipc_info_name_t *iin = &table_info[index];
iin->iin_object = (natural_t)VM_KERNEL_ADDRHASH((uintptr_t)port_pointers[index]);
port_pointers[index] = MACH_PORT_NULL;
}
kfree_type(ipc_object_t, pptrsize, port_pointers);
if (keep < table_size) {
kmem_free(ipc_kernel_map, table_addr + keep,
table_size - keep);
table_size = keep;
}
if (table_size > used) {
bzero(&table_info[infop->iis_table_size],
table_size - used);
}
kr = vm_map_unwire(ipc_kernel_map, table_addr,
table_addr + table_size, FALSE);
assert(kr == KERN_SUCCESS);
kr = vm_map_copyin(ipc_kernel_map, table_addr, used, TRUE, ©);
assert(kr == KERN_SUCCESS);
*tablep = (ipc_info_name_t *)copy;
*tableCntp = infop->iis_table_size;
} else {
*tablep = (ipc_info_name_t *)0;
*tableCntp = 0;
}
/* splay tree is obsolete, no work to do... */
*treep = (ipc_info_tree_name_t *)0;
*treeCntp = 0;
return KERN_SUCCESS;
}
kern_return_t
mach_port_space_info_from_user(
mach_port_t port,
ipc_info_space_t *infop,
ipc_info_name_array_t *tablep,
mach_msg_type_number_t *tableCntp,
__unused ipc_info_tree_name_array_t *treep,
__unused mach_msg_type_number_t *treeCntp)
{
kern_return_t kr;
ipc_space_t space = convert_port_to_space_read_no_eval(port);
if (space == IPC_SPACE_NULL) {
return KERN_INVALID_ARGUMENT;
}
kr = mach_port_space_info(space, infop, tablep, tableCntp, treep, treeCntp);
ipc_space_release(space);
return kr;
}
/*
* Routine: mach_port_space_basic_info
* Purpose:
* Returns basic information about an IPC space.
* Conditions:
* Nothing locked.
* Returns:
* KERN_SUCCESS Returned information.
* KERN_FAILURE The call is not supported.
* KERN_INVALID_TASK The space is dead.
*/
kern_return_t
mach_port_space_basic_info(
ipc_space_t space,
ipc_info_space_basic_t *infop)
{
ipc_entry_num_t tsize;
if (space == IS_NULL) {
return KERN_INVALID_TASK;
}
is_read_lock(space);
if (!is_active(space)) {
is_read_unlock(space);
return KERN_INVALID_TASK;
}
tsize = ipc_entry_table_count(is_active_table(space));
/* get the basic space info */
infop->iisb_genno_mask = MACH_PORT_NGEN(MACH_PORT_DEAD);
infop->iisb_table_size = tsize;
infop->iisb_table_inuse = tsize - space->is_table_free - 1;
infop->iisb_reserved[0] = 0;
infop->iisb_reserved[1] = 0;
is_read_unlock(space);
return KERN_SUCCESS;
}
/*
* Routine: mach_port_dnrequest_info
* Purpose:
* Returns information about the dead-name requests
* registered with the named receive right.
* Conditions:
* Nothing locked.
* Returns:
* KERN_SUCCESS Retrieved information.
* KERN_INVALID_TASK The space is null.
* KERN_INVALID_TASK The space is dead.
* KERN_INVALID_NAME The name doesn't denote a right.
* KERN_INVALID_RIGHT Name doesn't denote receive rights.
*/
kern_return_t
mach_port_dnrequest_info(
ipc_space_t space,
mach_port_name_t name,
unsigned int *totalp,
unsigned int *usedp)
{
ipc_port_request_table_t requests;
unsigned int total = 0, used = 0;
ipc_port_t port;
kern_return_t kr;
if (space == IS_NULL) {
return KERN_INVALID_TASK;
}
kr = ipc_port_translate_receive(space, name, &port);
if (kr != KERN_SUCCESS) {
return kr;
}
/* port is locked and active */
requests = port->ip_requests;
if (requests) {
ipc_port_request_t ipr = ipc_port_request_table_base(requests);
while ((ipr = ipc_port_request_table_next_elem(requests, ipr))) {
if (ipr->ipr_soright != IP_NULL &&
ipr->ipr_name != IPR_HOST_NOTIFY) {
used++;
}
}
total = ipc_port_request_table_count(requests);
}
ip_mq_unlock(port);
*totalp = total;
*usedp = used;
return KERN_SUCCESS;
}
static ipc_info_object_type_t
mach_port_kobject_type(ipc_port_t port)
{
#define MAKE_CASE(name) \
case IKOT_ ## name: return IPC_OTYPE_ ## name
switch (ip_type(port)) {
/* thread ports */
MAKE_CASE(THREAD_CONTROL);
MAKE_CASE(THREAD_READ);
MAKE_CASE(THREAD_INSPECT);
/* task ports */
MAKE_CASE(TASK_CONTROL);
MAKE_CASE(TASK_READ);
MAKE_CASE(TASK_INSPECT);
MAKE_CASE(TASK_NAME);
MAKE_CASE(TASK_RESUME);
MAKE_CASE(TASK_ID_TOKEN);
MAKE_CASE(TASK_FATAL);
/* host services, upcalls, security */
MAKE_CASE(HOST);
MAKE_CASE(HOST_PRIV);
MAKE_CASE(CLOCK);
MAKE_CASE(PROCESSOR);
MAKE_CASE(PROCESSOR_SET);
MAKE_CASE(PROCESSOR_SET_NAME);
/* common userspace used ports */
MAKE_CASE(EVENTLINK);
MAKE_CASE(FILEPORT);
MAKE_CASE(SEMAPHORE);
MAKE_CASE(VOUCHER);
MAKE_CASE(WORK_INTERVAL);
/* VM ports */
MAKE_CASE(MEMORY_OBJECT);
MAKE_CASE(NAMED_ENTRY);
/* IOKit & exclaves ports */
MAKE_CASE(MAIN_DEVICE);
MAKE_CASE(IOKIT_IDENT);
MAKE_CASE(IOKIT_CONNECT);
MAKE_CASE(IOKIT_OBJECT);
MAKE_CASE(UEXT_OBJECT);
MAKE_CASE(EXCLAVES_RESOURCE);
/* misc. */
MAKE_CASE(ARCADE_REG);
MAKE_CASE(AU_SESSIONPORT);
MAKE_CASE(HYPERVISOR);
MAKE_CASE(KCDATA);
MAKE_CASE(UND_REPLY);
MAKE_CASE(UX_HANDLER);
case IOT_TIMER_PORT:
return IPC_OTYPE_TIMER;
default:
return IPC_OTYPE_UNKNOWN;
}
#undef MAKE_CASE
}
/*
* Routine: mach_port_kobject [kernel call]
* Purpose:
* Retrieve the type and address of the kernel object
* represented by a send or receive right. Returns
* the kernel address in a mach_vm_address_t to
* mask potential differences in kernel address space
* size.
* Conditions:
* Nothing locked.
* Returns:
* KERN_SUCCESS Retrieved kernel object info.
* KERN_INVALID_TASK The space is null.
* KERN_INVALID_TASK The space is dead.
* KERN_INVALID_NAME The name doesn't denote a right.
* KERN_INVALID_RIGHT Name doesn't denote
* send or receive rights.
*/
static kern_return_t
mach_port_kobject_description(
ipc_space_t space,
mach_port_name_t name,
ipc_info_object_type_t *typep,
mach_vm_address_t *addrp,
kobject_description_t desc)
{
ipc_entry_bits_t bits;
ipc_object_t ipc_object;
kern_return_t kr;
mach_vm_address_t kaddr = 0;
if (space == IS_NULL) {
return KERN_INVALID_TASK;
}
kr = ipc_right_lookup_read(space, name, &bits, &ipc_object);
if (kr != KERN_SUCCESS) {
return kr;
}
/* object is locked and active */
if ((bits & MACH_PORT_TYPE_SEND_RECEIVE) == 0) {
io_unlock(ipc_object);
return KERN_INVALID_RIGHT;
}
ipc_port_t port = ip_object_to_port(ipc_object);
*typep = mach_port_kobject_type(port);
if (ip_is_kobject(port)) {
kaddr = (mach_vm_address_t)ipc_kobject_get_raw(port, ip_type(port));
}
*addrp = 0;
if (desc) {
*desc = '\0';
switch (ip_type(port)) {
case IKOT_IOKIT_OBJECT:
case IKOT_IOKIT_CONNECT:
case IKOT_IOKIT_IDENT:
case IKOT_UEXT_OBJECT:
{
io_kobject_t io_kobject = (io_kobject_t)kaddr;
if (io_kobject) {
iokit_kobject_retain(io_kobject);
io_unlock(ipc_object);
// IKOT_IOKIT_OBJECT since iokit_remove_reference() follows
io_object_t io_object = iokit_copy_object_for_consumed_kobject(io_kobject);
io_kobject = NULL;
if (io_object) {
iokit_port_object_description(io_object, desc);
iokit_remove_reference(io_object);
io_object = NULL;
}
goto unlocked;
}
break;
}
case IKOT_TASK_ID_TOKEN:
{
task_id_token_t token;
token = (task_id_token_t)ipc_kobject_get_stable(port, IKOT_TASK_ID_TOKEN);
snprintf(desc, KOBJECT_DESCRIPTION_LENGTH, "%d,%llu,%d", token->ident.p_pid, token->ident.p_uniqueid, token->ident.p_idversion);
break;
}
case IKOT_NAMED_ENTRY:
{
vm_named_entry_t named_entry = (vm_named_entry_t)ipc_kobject_get_stable(port, IKOT_NAMED_ENTRY);
mach_memory_entry_describe(named_entry, desc);
break;
}
default:
break;
}
}
io_unlock(ipc_object);
unlocked:
#if (DEVELOPMENT || DEBUG)
*addrp = VM_KERNEL_ADDRHASH(kaddr);
#endif
return KERN_SUCCESS;
}
kern_return_t
mach_port_kobject_description_from_user(
mach_port_t port,
mach_port_name_t name,
ipc_info_object_type_t *typep,
mach_vm_address_t *addrp,
kobject_description_t desc)
{
kern_return_t kr;
ipc_space_t space = convert_port_to_space_read_no_eval(port);
if (space == IPC_SPACE_NULL) {
return KERN_INVALID_ARGUMENT;
}
kr = mach_port_kobject_description(space, name, typep, addrp, desc);
ipc_space_release(space);
return kr;
}
kern_return_t
mach_port_kobject_from_user(
mach_port_t port,
mach_port_name_t name,
ipc_info_object_type_t *typep,
mach_vm_address_t *addrp)
{
return mach_port_kobject_description_from_user(port, name, typep, addrp, NULL);
}
#if (DEVELOPMENT || DEBUG)
kern_return_t
mach_port_special_reply_port_reset_link(
ipc_space_t space,
mach_port_name_t name,
boolean_t *srp_lost_link)
{
ipc_port_t port;
kern_return_t kr;
thread_t thread = current_thread();
if (space != current_space()) {
return KERN_INVALID_TASK;
}
if (!MACH_PORT_VALID(name)) {
return KERN_INVALID_NAME;
}
if (!IP_VALID(thread->ith_special_reply_port)) {
return KERN_INVALID_VALUE;
}
kr = ipc_port_translate_receive(space, name, &port);
if (kr != KERN_SUCCESS) {
return kr;
}
if (thread->ith_special_reply_port != port) {
ip_mq_unlock(port);
return KERN_INVALID_ARGUMENT;
}
*srp_lost_link = (port->ip_srp_lost_link == 1)? TRUE : FALSE;
port->ip_srp_lost_link = 0;
ip_mq_unlock(port);
return KERN_SUCCESS;
}
#else
kern_return_t
mach_port_special_reply_port_reset_link(
__unused ipc_space_t space,
__unused mach_port_name_t name,
__unused boolean_t *srp_lost_link)
{
return KERN_NOT_SUPPORTED;
}
#endif