#include <mach_ipc_debug.h>
#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>
#if MACH_IPC_DEBUG
#include <kern/host.h>
#include <kern/misc_protos.h>
#include <vm/vm_map.h>
#include <vm/vm_kern.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_table.h>
#include <ipc/ipc_right.h>
#include <security/mac_mach_internal.h>
#endif
#if !MACH_IPC_DEBUG
kern_return_t
mach_port_get_srights(
__unused ipc_space_t space,
__unused mach_port_name_t name,
__unused mach_port_rights_t *srightsp)
{
return KERN_FAILURE;
}
#else
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;
srights = port->ip_srights;
ip_unlock(port);
*srightsp = srights;
return KERN_SUCCESS;
}
#endif
#if !MACH_IPC_DEBUG
kern_return_t
mach_port_space_info(
__unused ipc_space_t space,
__unused ipc_info_space_t *infop,
__unused ipc_info_name_array_t *tablep,
__unused mach_msg_type_number_t *tableCntp,
__unused ipc_info_tree_name_array_t *treep,
__unused mach_msg_type_number_t *treeCntp)
{
return KERN_FAILURE;
}
#else
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)
{
ipc_info_name_t *table_info;
vm_offset_t table_addr;
vm_size_t table_size, table_size_needed;
ipc_entry_t table;
ipc_entry_num_t tsize;
mach_port_index_t index;
kern_return_t kr;
vm_map_copy_t copy;
if (space == IS_NULL)
return KERN_INVALID_TASK;
#if !(DEVELOPMENT || DEBUG) && CONFIG_MACF
const boolean_t dbg_ok = (mac_task_check_expose_task(kernel_task) == 0);
#else
const boolean_t dbg_ok = TRUE;
#endif
table_size = 0;
for (;;) {
is_read_lock(space);
if (!is_active(space)) {
is_read_unlock(space);
if (table_size != 0)
kmem_free(ipc_kernel_map,
table_addr, table_size);
return KERN_INVALID_TASK;
}
table_size_needed =
vm_map_round_page((space->is_table_size
* sizeof(ipc_info_name_t)),
VM_MAP_PAGE_MASK(ipc_kernel_map));
if (table_size_needed == table_size)
break;
is_read_unlock(space);
if (table_size != table_size_needed) {
if (table_size != 0)
kmem_free(ipc_kernel_map, table_addr, table_size);
kr = kmem_alloc(ipc_kernel_map, &table_addr, table_size_needed, VM_KERN_MEMORY_IPC);
if (kr != KERN_SUCCESS) {
return KERN_RESOURCE_SHORTAGE;
}
table_size = table_size_needed;
}
}
infop->iis_genno_mask = MACH_PORT_NGEN(MACH_PORT_DEAD);
infop->iis_table_size = space->is_table_size;
infop->iis_table_next = space->is_table_next->its_size;
table = space->is_table;
tsize = space->is_table_size;
table_info = (ipc_info_name_array_t)table_addr;
for (index = 0; index < tsize; index++) {
ipc_info_name_t *iin = &table_info[index];
ipc_entry_t entry = &table[index];
ipc_entry_bits_t bits;
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 ((entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) != MACH_PORT_TYPE_NONE &&
entry->ie_request != IE_REQ_NONE) {
__IGNORE_WCASTALIGN(ipc_port_t port = (ipc_port_t) entry->ie_object);
assert(IP_VALID(port));
ip_lock(port);
iin->iin_type |= ipc_port_request_type(port, iin->iin_name, entry->ie_request);
ip_unlock(port);
}
iin->iin_urefs = IE_BITS_UREFS(bits);
iin->iin_object = (dbg_ok) ? (natural_t)VM_KERNEL_ADDRPERM((uintptr_t)entry->ie_object) : 0;
iin->iin_next = entry->ie_next;
iin->iin_hash = entry->ie_index;
}
is_read_unlock(space);
if (table_size > 0) {
vm_size_t used_table_size;
used_table_size = infop->iis_table_size * sizeof(ipc_info_name_t);
if (table_size > used_table_size)
bzero((char *)&table_info[infop->iis_table_size],
table_size - used_table_size);
kr = vm_map_unwire(
ipc_kernel_map,
vm_map_trunc_page(table_addr,
VM_MAP_PAGE_MASK(ipc_kernel_map)),
vm_map_round_page(table_addr + table_size,
VM_MAP_PAGE_MASK(ipc_kernel_map)),
FALSE);
assert(kr == KERN_SUCCESS);
kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)table_addr,
(vm_map_size_t)used_table_size, 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;
}
*treep = (ipc_info_tree_name_t *)0;
*treeCntp = 0;
return KERN_SUCCESS;
}
#endif
#if !MACH_IPC_DEBUG
kern_return_t
mach_port_space_basic_info(
__unused ipc_space_t space,
__unused ipc_info_space_basic_t *infop)
{
return KERN_FAILURE;
}
#else
kern_return_t
mach_port_space_basic_info(
ipc_space_t space,
ipc_info_space_basic_t *infop)
{
if (space == IS_NULL)
return KERN_INVALID_TASK;
is_read_lock(space);
if (!is_active(space)) {
is_read_unlock(space);
return KERN_INVALID_TASK;
}
infop->iisb_genno_mask = MACH_PORT_NGEN(MACH_PORT_DEAD);
infop->iisb_table_size = space->is_table_size;
infop->iisb_table_next = space->is_table_next->its_size;
infop->iisb_table_inuse = space->is_table_size - space->is_table_free - 1;
infop->iisb_reserved[0] = 0;
infop->iisb_reserved[1] = 0;
is_read_unlock(space);
return KERN_SUCCESS;
}
#endif
#if !MACH_IPC_DEBUG
kern_return_t
mach_port_dnrequest_info(
__unused ipc_space_t space,
__unused mach_port_name_t name,
__unused unsigned int *totalp,
__unused unsigned int *usedp)
{
return KERN_FAILURE;
}
#else
kern_return_t
mach_port_dnrequest_info(
ipc_space_t space,
mach_port_name_t name,
unsigned int *totalp,
unsigned int *usedp)
{
unsigned int total, used;
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;
if (port->ip_requests == IPR_NULL) {
total = 0;
used = 0;
} else {
ipc_port_request_t requests = port->ip_requests;
ipc_port_request_index_t index;
total = requests->ipr_size->its_size;
for (index = 1, used = 0;
index < total; index++) {
ipc_port_request_t ipr = &requests[index];
if (ipr->ipr_name != MACH_PORT_NULL)
used++;
}
}
ip_unlock(port);
*totalp = total;
*usedp = used;
return KERN_SUCCESS;
}
#endif
#if !MACH_IPC_DEBUG
kern_return_t
mach_port_kobject(
__unused ipc_space_t space,
__unused mach_port_name_t name,
__unused natural_t *typep,
__unused mach_vm_address_t *addrp)
{
return KERN_FAILURE;
}
#else
kern_return_t
mach_port_kobject(
ipc_space_t space,
mach_port_name_t name,
natural_t *typep,
mach_vm_address_t *addrp)
{
ipc_entry_t entry;
ipc_port_t port;
kern_return_t kr;
mach_vm_address_t kaddr;
if (space == IS_NULL)
return KERN_INVALID_TASK;
kr = ipc_right_lookup_read(space, name, &entry);
if (kr != KERN_SUCCESS)
return kr;
if ((entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE) == 0) {
is_read_unlock(space);
return KERN_INVALID_RIGHT;
}
__IGNORE_WCASTALIGN(port = (ipc_port_t) entry->ie_object);
assert(port != IP_NULL);
ip_lock(port);
is_read_unlock(space);
if (!ip_active(port)) {
ip_unlock(port);
return KERN_INVALID_RIGHT;
}
*typep = (unsigned int) ip_kotype(port);
kaddr = (mach_vm_address_t)port->ip_kobject;
ip_unlock(port);
#if (DEVELOPMENT || DEBUG)
if (0 != kaddr && is_ipc_kobject(*typep))
*addrp = VM_KERNEL_UNSLIDE_OR_PERM(kaddr);
else
#endif
*addrp = 0;
return KERN_SUCCESS;
}
#endif
#if !MACH_IPC_DEBUG
kern_return_t
mach_port_kernel_object(
__unused ipc_space_t space,
__unused mach_port_name_t name,
__unused unsigned int *typep,
__unused unsigned int *addrp)
{
return KERN_FAILURE;
}
#else
kern_return_t
mach_port_kernel_object(
ipc_space_t space,
mach_port_name_t name,
unsigned int *typep,
unsigned int *addrp)
{
mach_vm_address_t addr = 0;
kern_return_t kr;
kr = mach_port_kobject(space, name, typep, &addr);
*addrp = (unsigned int) addr;
return kr;
}
#endif
#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_unlock(port);
return KERN_INVALID_ARGUMENT;
}
imq_lock(&port->ip_messages);
*srp_lost_link = (port->ip_srp_lost_link == 1)? TRUE : FALSE;
port->ip_srp_lost_link = 0;
imq_unlock(&port->ip_messages);
ip_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