#include <mach/mach_types.h>
#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/port.h>
#include <mach/message.h>
#include <kern/kern_types.h>
#include <kern/misc_protos.h>
#include <kern/ipc_kobject.h>
#include <ipc/ipc_types.h>
#include <ipc/ipc_importance.h>
#include <ipc/port.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_entry.h>
#include <ipc/ipc_object.h>
#include <ipc/ipc_hash.h>
#include <ipc/ipc_right.h>
#include <ipc/ipc_notify.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_pset.h>
#include <security/mac_mach_internal.h>
SECURITY_READ_ONLY_LATE(zone_t) ipc_object_zones[IOT_NUMBER];
ZONE_INIT(&ipc_object_zones[IOT_PORT], "ipc ports", sizeof(struct ipc_port),
ZC_NOENCRYPT | ZC_CACHING | ZC_ZFREE_CLEARMEM | ZC_NOSEQUESTER,
ZONE_ID_IPC_PORT, NULL);
ZONE_INIT(&ipc_object_zones[IOT_PORT_SET], "ipc port sets",
sizeof(struct ipc_pset),
ZC_NOENCRYPT | ZC_ZFREE_CLEARMEM | ZC_NOSEQUESTER,
ZONE_ID_IPC_PORT_SET, NULL);
void
ipc_object_reference(
ipc_object_t object)
{
io_reference(object);
}
void
ipc_object_release(
ipc_object_t object)
{
io_release(object);
}
kern_return_t
ipc_object_translate(
ipc_space_t space,
mach_port_name_t name,
mach_port_right_t right,
ipc_object_t *objectp)
{
ipc_entry_t entry;
ipc_object_t object;
kern_return_t kr;
if (!MACH_PORT_RIGHT_VALID_TRANSLATE(right)) {
return KERN_INVALID_RIGHT;
}
kr = ipc_right_lookup_read(space, name, &entry);
if (kr != KERN_SUCCESS) {
return kr;
}
if ((entry->ie_bits & MACH_PORT_TYPE(right)) == MACH_PORT_TYPE_NONE) {
is_read_unlock(space);
return KERN_INVALID_RIGHT;
}
object = entry->ie_object;
assert(object != IO_NULL);
io_lock(object);
is_read_unlock(space);
if (!io_active(object)) {
io_unlock(object);
return KERN_INVALID_NAME;
}
*objectp = object;
return KERN_SUCCESS;
}
kern_return_t
ipc_object_translate_two(
ipc_space_t space,
mach_port_name_t name1,
mach_port_right_t right1,
ipc_object_t *objectp1,
mach_port_name_t name2,
mach_port_right_t right2,
ipc_object_t *objectp2)
{
ipc_entry_t entry1;
ipc_entry_t entry2;
ipc_object_t object1, object2;
kern_return_t kr;
boolean_t doguard = TRUE;
kr = ipc_right_lookup_two_read(space, name1, &entry1, name2, &entry2);
if (kr != KERN_SUCCESS) {
return kr;
}
if ((entry1->ie_bits & MACH_PORT_TYPE(right1)) == MACH_PORT_TYPE_NONE) {
if ((right1 & MACH_PORT_RIGHT_RECEIVE) == MACH_PORT_RIGHT_RECEIVE &&
(entry1->ie_bits & MACH_PORT_TYPE_EX_RECEIVE) == MACH_PORT_TYPE_EX_RECEIVE) {
doguard = FALSE;
}
is_read_unlock(space);
if (doguard) {
mach_port_guard_exception(name1, 0, 0, kGUARD_EXC_INVALID_RIGHT);
}
return KERN_INVALID_RIGHT;
}
if ((entry2->ie_bits & MACH_PORT_TYPE(right2)) == MACH_PORT_TYPE_NONE) {
if ((right2 & MACH_PORT_RIGHT_RECEIVE) == MACH_PORT_RIGHT_RECEIVE &&
(entry2->ie_bits & MACH_PORT_TYPE_EX_RECEIVE) == MACH_PORT_TYPE_EX_RECEIVE) {
doguard = FALSE;
}
is_read_unlock(space);
if (doguard) {
mach_port_guard_exception(name2, 0, 0, kGUARD_EXC_INVALID_RIGHT);
}
return KERN_INVALID_RIGHT;
}
object1 = entry1->ie_object;
assert(object1 != IO_NULL);
io_lock(object1);
if (!io_active(object1)) {
io_unlock(object1);
is_read_unlock(space);
return KERN_INVALID_NAME;
}
object2 = entry2->ie_object;
assert(object2 != IO_NULL);
io_lock(object2);
if (!io_active(object2)) {
io_unlock(object1);
io_unlock(object2);
is_read_unlock(space);
return KERN_INVALID_NAME;
}
*objectp1 = object1;
*objectp2 = object2;
is_read_unlock(space);
return KERN_SUCCESS;
}
kern_return_t
ipc_object_alloc_dead(
ipc_space_t space,
mach_port_name_t *namep)
{
ipc_entry_t entry;
kern_return_t kr;
kr = ipc_entry_alloc(space, namep, &entry);
if (kr != KERN_SUCCESS) {
return kr;
}
assert(entry->ie_object == IO_NULL);
entry->ie_bits |= MACH_PORT_TYPE_DEAD_NAME | 1;
ipc_entry_modified(space, *namep, entry);
is_write_unlock(space);
return KERN_SUCCESS;
}
kern_return_t
ipc_object_alloc_dead_name(
ipc_space_t space,
mach_port_name_t name)
{
ipc_entry_t entry;
kern_return_t kr;
kr = ipc_entry_alloc_name(space, name, &entry);
if (kr != KERN_SUCCESS) {
return kr;
}
if (ipc_right_inuse(space, name, entry)) {
return KERN_NAME_EXISTS;
}
assert(entry->ie_object == IO_NULL);
entry->ie_bits |= MACH_PORT_TYPE_DEAD_NAME | 1;
ipc_entry_modified(space, name, entry);
is_write_unlock(space);
return KERN_SUCCESS;
}
kern_return_t
ipc_object_alloc(
ipc_space_t space,
ipc_object_type_t otype,
mach_port_type_t type,
mach_port_urefs_t urefs,
mach_port_name_t *namep,
ipc_object_t *objectp)
{
ipc_object_t object;
ipc_entry_t entry;
kern_return_t kr;
assert(otype < IOT_NUMBER);
assert((type & MACH_PORT_TYPE_ALL_RIGHTS) == type);
assert(type != MACH_PORT_TYPE_NONE);
assert(urefs <= MACH_PORT_UREFS_MAX);
object = io_alloc(otype);
if (object == IO_NULL) {
return KERN_RESOURCE_SHORTAGE;
}
if (otype == IOT_PORT) {
ipc_port_t port = ip_object_to_port(object);
bzero((char *)port, sizeof(*port));
} else if (otype == IOT_PORT_SET) {
ipc_pset_t pset = ips_object_to_pset(object);
bzero((char *)pset, sizeof(*pset));
}
io_lock_init(object);
*namep = CAST_MACH_PORT_TO_NAME(object);
kr = ipc_entry_alloc(space, namep, &entry);
if (kr != KERN_SUCCESS) {
io_free(otype, object);
return kr;
}
entry->ie_bits |= type | urefs;
entry->ie_object = object;
ipc_entry_modified(space, *namep, entry);
object->io_bits = io_makebits(TRUE, otype, 0);
io_lock(object);
object->io_references = 1;
*objectp = object;
return KERN_SUCCESS;
}
kern_return_t
ipc_object_alloc_name(
ipc_space_t space,
ipc_object_type_t otype,
mach_port_type_t type,
mach_port_urefs_t urefs,
mach_port_name_t name,
ipc_object_t *objectp)
{
ipc_object_t object;
ipc_entry_t entry;
kern_return_t kr;
assert(otype < IOT_NUMBER);
assert((type & MACH_PORT_TYPE_ALL_RIGHTS) == type);
assert(type != MACH_PORT_TYPE_NONE);
assert(urefs <= MACH_PORT_UREFS_MAX);
object = io_alloc(otype);
if (object == IO_NULL) {
return KERN_RESOURCE_SHORTAGE;
}
if (otype == IOT_PORT) {
ipc_port_t port = ip_object_to_port(object);
bzero((char *)port, sizeof(*port));
} else if (otype == IOT_PORT_SET) {
ipc_pset_t pset = ips_object_to_pset(object);
bzero((char *)pset, sizeof(*pset));
}
io_lock_init(object);
kr = ipc_entry_alloc_name(space, name, &entry);
if (kr != KERN_SUCCESS) {
io_free(otype, object);
return kr;
}
if (ipc_right_inuse(space, name, entry)) {
io_free(otype, object);
return KERN_NAME_EXISTS;
}
entry->ie_bits |= type | urefs;
entry->ie_object = object;
ipc_entry_modified(space, name, entry);
object->io_bits = io_makebits(TRUE, otype, 0);
io_lock(object);
is_write_unlock(space);
object->io_references = 1;
*objectp = object;
return KERN_SUCCESS;
}
void
ipc_object_validate(
ipc_object_t object)
{
if (io_otype(object) != IOT_PORT_SET) {
zone_id_require(ZONE_ID_IPC_PORT,
sizeof(struct ipc_port), object);
} else {
zone_id_require(ZONE_ID_IPC_PORT_SET,
sizeof(struct ipc_pset), object);
}
}
mach_msg_type_name_t
ipc_object_copyin_type(
mach_msg_type_name_t msgt_name)
{
switch (msgt_name) {
case MACH_MSG_TYPE_MOVE_RECEIVE:
return MACH_MSG_TYPE_PORT_RECEIVE;
case MACH_MSG_TYPE_MOVE_SEND_ONCE:
case MACH_MSG_TYPE_MAKE_SEND_ONCE:
return MACH_MSG_TYPE_PORT_SEND_ONCE;
case MACH_MSG_TYPE_MOVE_SEND:
case MACH_MSG_TYPE_MAKE_SEND:
case MACH_MSG_TYPE_COPY_SEND:
return MACH_MSG_TYPE_PORT_SEND;
case MACH_MSG_TYPE_DISPOSE_RECEIVE:
case MACH_MSG_TYPE_DISPOSE_SEND:
case MACH_MSG_TYPE_DISPOSE_SEND_ONCE:
default:
return MACH_MSG_TYPE_PORT_NONE;
}
}
kern_return_t
ipc_object_copyin(
ipc_space_t space,
mach_port_name_t name,
mach_msg_type_name_t msgt_name,
ipc_object_t *objectp,
mach_port_context_t context,
mach_msg_guard_flags_t *guard_flags,
ipc_kmsg_flags_t kmsg_flags)
{
ipc_entry_t entry;
ipc_port_t soright;
ipc_port_t release_port;
kern_return_t kr;
int assertcnt = 0;
ipc_right_copyin_flags_t irc_flags = IPC_RIGHT_COPYIN_FLAGS_DEADOK;
if (kmsg_flags & IPC_KMSG_FLAGS_ALLOW_IMMOVABLE_SEND) {
irc_flags |= IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND;
}
kr = ipc_right_lookup_write(space, name, &entry);
if (kr != KERN_SUCCESS) {
return kr;
}
release_port = IP_NULL;
kr = ipc_right_copyin(space, name, entry,
msgt_name, irc_flags,
objectp, &soright,
&release_port,
&assertcnt,
context,
guard_flags);
if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) {
ipc_entry_dealloc(space, name, entry);
}
is_write_unlock(space);
#if IMPORTANCE_INHERITANCE
if (0 < assertcnt && ipc_importance_task_is_any_receiver_type(current_task()->task_imp_base)) {
ipc_importance_task_drop_internal_assertion(current_task()->task_imp_base, assertcnt);
}
#endif
if (release_port != IP_NULL) {
ip_release(release_port);
}
if ((kr == KERN_SUCCESS) && (soright != IP_NULL)) {
ipc_notify_port_deleted(soright, name);
}
return kr;
}
void
ipc_object_copyin_from_kernel(
ipc_object_t object,
mach_msg_type_name_t msgt_name)
{
assert(IO_VALID(object));
switch (msgt_name) {
case MACH_MSG_TYPE_MOVE_RECEIVE: {
ipc_port_t port = ip_object_to_port(object);
ip_lock(port);
imq_lock(&port->ip_messages);
require_ip_active(port);
if (port->ip_destination != IP_NULL) {
assert(port->ip_receiver == ipc_space_kernel);
assert(port->ip_immovable_receive == 0);
port->ip_mscount = 0;
port->ip_receiver_name = MACH_PORT_NULL;
port->ip_destination = IP_NULL;
}
imq_unlock(&port->ip_messages);
ip_unlock(port);
break;
}
case MACH_MSG_TYPE_COPY_SEND: {
ipc_port_t port = ip_object_to_port(object);
ip_lock(port);
if (ip_active(port)) {
assert(port->ip_srights > 0);
port->ip_srights++;
}
ip_reference(port);
ip_unlock(port);
break;
}
case MACH_MSG_TYPE_MAKE_SEND: {
ipc_port_t port = ip_object_to_port(object);
ip_lock(port);
if (ip_active(port)) {
assert(port->ip_receiver_name != MACH_PORT_NULL);
assert((port->ip_receiver == ipc_space_kernel) ||
(port->ip_receiver->is_node_id != HOST_LOCAL_NODE));
port->ip_mscount++;
}
port->ip_srights++;
ip_reference(port);
ip_unlock(port);
break;
}
case MACH_MSG_TYPE_MOVE_SEND: {
assert(ip_object_to_port(object)->ip_srights);
break;
}
case MACH_MSG_TYPE_MAKE_SEND_ONCE: {
ipc_port_t port = ip_object_to_port(object);
ip_lock(port);
if (ip_active(port)) {
assert(port->ip_receiver_name != MACH_PORT_NULL);
}
ipc_port_make_sonce_locked(port);
ip_unlock(port);
break;
}
case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
assert(ip_object_to_port(object)->ip_sorights);
break;
}
default:
panic("ipc_object_copyin_from_kernel: strange rights");
}
}
void
ipc_object_destroy(
ipc_object_t object,
mach_msg_type_name_t msgt_name)
{
assert(IO_VALID(object));
assert(io_otype(object) == IOT_PORT);
switch (msgt_name) {
case MACH_MSG_TYPE_PORT_SEND:
ipc_port_release_send(ip_object_to_port(object));
break;
case MACH_MSG_TYPE_PORT_SEND_ONCE:
ipc_notify_send_once(ip_object_to_port(object));
break;
case MACH_MSG_TYPE_PORT_RECEIVE:
ipc_port_release_receive(ip_object_to_port(object));
break;
default:
panic("ipc_object_destroy: strange rights");
}
}
void
ipc_object_destroy_dest(
ipc_object_t object,
mach_msg_type_name_t msgt_name)
{
assert(IO_VALID(object));
assert(io_otype(object) == IOT_PORT);
switch (msgt_name) {
case MACH_MSG_TYPE_PORT_SEND:
ipc_port_release_send(ip_object_to_port(object));
break;
case MACH_MSG_TYPE_PORT_SEND_ONCE:
if (io_active(object) &&
!ip_full_kernel(ip_object_to_port(object))) {
ipc_notify_send_once(ip_object_to_port(object));
} else {
ipc_port_release_sonce(ip_object_to_port(object));
}
break;
default:
panic("ipc_object_destroy_dest: strange rights");
}
}
kern_return_t
ipc_object_insert_send_right(
ipc_space_t space,
mach_port_name_t name,
mach_msg_type_name_t msgt_name)
{
ipc_entry_bits_t bits;
ipc_object_t object;
ipc_entry_t entry;
kern_return_t kr;
assert(msgt_name == MACH_MSG_TYPE_MAKE_SEND ||
msgt_name == MACH_MSG_TYPE_COPY_SEND);
kr = ipc_right_lookup_write(space, name, &entry);
if (kr != KERN_SUCCESS) {
return kr;
}
if (!IO_VALID(entry->ie_object)) {
is_write_unlock(space);
return KERN_INVALID_CAPABILITY;
}
bits = entry->ie_bits;
object = entry->ie_object;
io_lock(object);
if (!io_active(object)) {
kr = KERN_INVALID_CAPABILITY;
} else if (msgt_name == MACH_MSG_TYPE_MAKE_SEND) {
if (bits & MACH_PORT_TYPE_RECEIVE) {
ipc_port_t port = ip_object_to_port(object);
port->ip_mscount++;
if ((bits & MACH_PORT_TYPE_SEND) == 0) {
port->ip_srights++;
bits |= MACH_PORT_TYPE_SEND;
}
if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
bits += 1;
}
entry->ie_bits = bits;
ipc_entry_modified(space, name, entry);
kr = KERN_SUCCESS;
} else {
kr = KERN_INVALID_RIGHT;
}
} else { if (bits & MACH_PORT_TYPE_SEND) {
if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
entry->ie_bits = bits + 1;
}
ipc_entry_modified(space, name, entry);
kr = KERN_SUCCESS;
} else {
kr = KERN_INVALID_RIGHT;
}
}
io_unlock(object);
is_write_unlock(space);
return kr;
}
kern_return_t
ipc_object_copyout(
ipc_space_t space,
ipc_object_t object,
mach_msg_type_name_t msgt_name,
mach_port_context_t *context,
mach_msg_guard_flags_t *guard_flags,
mach_port_name_t *namep)
{
struct knote *kn = current_thread()->ith_knote;
mach_port_name_t name;
ipc_entry_t entry;
kern_return_t kr;
assert(IO_VALID(object));
assert(io_otype(object) == IOT_PORT);
if (ITH_KNOTE_VALID(kn, msgt_name)) {
filt_machport_turnstile_prepare_lazily(kn,
msgt_name, ip_object_to_port(object));
}
is_write_lock(space);
for (;;) {
if (!is_active(space)) {
is_write_unlock(space);
return KERN_INVALID_TASK;
}
if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
ipc_right_reverse(space, object, &name, &entry)) {
assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
break;
}
name = CAST_MACH_PORT_TO_NAME(object);
kr = ipc_entry_get(space, &name, &entry);
if (kr != KERN_SUCCESS) {
kr = ipc_entry_grow_table(space, ITS_SIZE_NONE);
if (kr != KERN_SUCCESS) {
return kr;
}
continue;
}
assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
assert(entry->ie_object == IO_NULL);
io_lock(object);
if (!io_active(object)) {
io_unlock(object);
ipc_entry_dealloc(space, name, entry);
is_write_unlock(space);
return KERN_INVALID_CAPABILITY;
}
if (!ip_label_check(space, ip_object_to_port(object), msgt_name)) {
io_unlock(object);
ipc_entry_dealloc(space, name, entry);
is_write_unlock(space);
return KERN_INVALID_CAPABILITY;
}
entry->ie_object = object;
break;
}
kr = ipc_right_copyout(space, name, entry,
msgt_name, context, guard_flags, object);
is_write_unlock(space);
if (kr == KERN_SUCCESS) {
*namep = name;
}
return kr;
}
kern_return_t
ipc_object_copyout_name(
ipc_space_t space,
ipc_object_t object,
mach_msg_type_name_t msgt_name,
mach_port_name_t name)
{
mach_port_name_t oname;
ipc_entry_t oentry;
ipc_entry_t entry;
kern_return_t kr;
#if IMPORTANCE_INHERITANCE
int assertcnt = 0;
ipc_importance_task_t task_imp = IIT_NULL;
#endif
assert(IO_VALID(object));
assert(io_otype(object) == IOT_PORT);
kr = ipc_entry_alloc_name(space, name, &entry);
if (kr != KERN_SUCCESS) {
return kr;
}
if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
ipc_right_reverse(space, object, &oname, &oentry)) {
if (name != oname) {
io_unlock(object);
if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) {
ipc_entry_dealloc(space, name, entry);
}
is_write_unlock(space);
return KERN_RIGHT_EXISTS;
}
assert(entry == oentry);
assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
} else {
if (ipc_right_inuse(space, name, entry)) {
return KERN_NAME_EXISTS;
}
assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
assert(entry->ie_object == IO_NULL);
io_lock(object);
if (!io_active(object)) {
io_unlock(object);
ipc_entry_dealloc(space, name, entry);
is_write_unlock(space);
return KERN_INVALID_CAPABILITY;
}
if (!ip_label_check(space, ip_object_to_port(object), msgt_name)) {
io_unlock(object);
ipc_entry_dealloc(space, name, entry);
is_write_unlock(space);
return KERN_INVALID_CAPABILITY;
}
entry->ie_object = object;
}
#if IMPORTANCE_INHERITANCE
if (msgt_name == MACH_MSG_TYPE_PORT_RECEIVE) {
ipc_port_t port = ip_object_to_port(object);
if (space->is_task != TASK_NULL) {
task_imp = space->is_task->task_imp_base;
if (ipc_importance_task_is_any_receiver_type(task_imp)) {
assertcnt = port->ip_impcount;
ipc_importance_task_reference(task_imp);
} else {
task_imp = IIT_NULL;
}
}
assert(port->ip_tempowner != 0);
port->ip_tempowner = 0;
}
#endif
kr = ipc_right_copyout(space, name, entry,
msgt_name, NULL, NULL, object);
is_write_unlock(space);
#if IMPORTANCE_INHERITANCE
if (task_imp != IIT_NULL) {
ipc_importance_task_hold_internal_assertion(task_imp, assertcnt);
ipc_importance_task_release(task_imp);
}
#endif
return kr;
}
void
ipc_object_copyout_dest(
ipc_space_t space,
ipc_object_t object,
mach_msg_type_name_t msgt_name,
mach_port_name_t *namep)
{
mach_port_name_t name;
assert(IO_VALID(object));
assert(io_active(object));
switch (msgt_name) {
case MACH_MSG_TYPE_PORT_SEND: {
ipc_port_t port = ip_object_to_port(object);
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount;
if (port->ip_receiver == space) {
name = port->ip_receiver_name;
} else {
name = MACH_PORT_NULL;
}
assert(port->ip_srights > 0);
if (--port->ip_srights == 0 &&
port->ip_nsrequest != IP_NULL) {
nsrequest = port->ip_nsrequest;
port->ip_nsrequest = IP_NULL;
mscount = port->ip_mscount;
ipc_port_clear_sync_rcv_thread_boost_locked(port);
ipc_notify_no_senders(nsrequest, mscount);
} else {
ipc_port_clear_sync_rcv_thread_boost_locked(port);
}
ip_release(port);
break;
}
case MACH_MSG_TYPE_PORT_SEND_ONCE: {
ipc_port_t port = ip_object_to_port(object);
assert(port->ip_sorights > 0);
if (port->ip_receiver == space) {
port->ip_sorights--;
name = port->ip_receiver_name;
ipc_port_clear_sync_rcv_thread_boost_locked(port);
ip_release(port);
} else {
ip_unlock(port);
ipc_notify_send_once(port);
name = MACH_PORT_NULL;
}
break;
}
default:
panic("ipc_object_copyout_dest: strange rights");
name = MACH_PORT_DEAD;
}
*namep = name;
}
void
io_lock(ipc_object_t io)
{
ipc_object_validate(io);
lck_spin_lock_grp(&(io)->io_lock_data, &ipc_lck_grp);
}
boolean_t
io_lock_try(ipc_object_t io)
{
ipc_object_validate(io);
return lck_spin_try_lock_grp(&(io)->io_lock_data, &ipc_lck_grp);
}
void
io_free(
unsigned int otype,
ipc_object_t object)
{
if (otype == IOT_PORT) {
ipc_port_finalize(ip_object_to_port(object));
}
io_lock_destroy(object);
zfree(ipc_object_zones[otype], object);
}