#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/port.h>
#include <mach/message.h>
#include <kern/assert.h>
#include <kern/misc_protos.h>
#include <ipc/port.h>
#include <ipc/ipc_entry.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_object.h>
#include <ipc/ipc_hash.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_pset.h>
#include <ipc/ipc_right.h>
#include <ipc/ipc_notify.h>
#include <ipc/ipc_table.h>
#include <ipc/ipc_importance.h>
#include <security/mac_mach_internal.h>
extern kern_return_t
mach_port_guard_exception(
mach_port_name_t name,
uint64_t inguard,
uint64_t portguard,
unsigned reason);
kern_return_t
ipc_right_lookup_write(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t *entryp)
{
ipc_entry_t entry;
assert(space != IS_NULL);
is_write_lock(space);
if (!is_active(space)) {
is_write_unlock(space);
return KERN_INVALID_TASK;
}
if ((entry = ipc_entry_lookup(space, name)) == IE_NULL) {
is_write_unlock(space);
return KERN_INVALID_NAME;
}
*entryp = entry;
return KERN_SUCCESS;
}
kern_return_t
ipc_right_lookup_two_write(
ipc_space_t space,
mach_port_name_t name1,
ipc_entry_t *entryp1,
mach_port_name_t name2,
ipc_entry_t *entryp2)
{
ipc_entry_t entry1;
ipc_entry_t entry2;
assert(space != IS_NULL);
is_write_lock(space);
if (!is_active(space)) {
is_write_unlock(space);
return KERN_INVALID_TASK;
}
if ((entry1 = ipc_entry_lookup(space, name1)) == IE_NULL) {
is_write_unlock(space);
return KERN_INVALID_NAME;
}
if ((entry2 = ipc_entry_lookup(space, name2)) == IE_NULL) {
is_write_unlock(space);
return KERN_INVALID_NAME;
}
*entryp1 = entry1;
*entryp2 = entry2;
return KERN_SUCCESS;
}
boolean_t
ipc_right_reverse(
ipc_space_t space,
ipc_object_t object,
mach_port_name_t *namep,
ipc_entry_t *entryp)
{
ipc_port_t port;
mach_port_name_t name;
ipc_entry_t entry;
assert(is_active(space));
assert(io_otype(object) == IOT_PORT);
port = (ipc_port_t) object;
ip_lock(port);
if (!ip_active(port)) {
ip_unlock(port);
return FALSE;
}
if (port->ip_receiver == space) {
name = port->ip_receiver_name;
assert(name != MACH_PORT_NULL);
entry = ipc_entry_lookup(space, name);
assert(entry != IE_NULL);
assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
assert(port == (ipc_port_t) entry->ie_object);
*namep = name;
*entryp = entry;
return TRUE;
}
if (ipc_hash_lookup(space, (ipc_object_t) port, namep, entryp)) {
assert((entry = *entryp) != IE_NULL);
assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND);
assert(port == (ipc_port_t) entry->ie_object);
return TRUE;
}
ip_unlock(port);
return FALSE;
}
kern_return_t
ipc_right_request_alloc(
ipc_space_t space,
mach_port_name_t name,
boolean_t immediate,
boolean_t send_possible,
ipc_port_t notify,
ipc_port_t *previousp)
{
ipc_port_request_index_t prev_request;
ipc_port_t previous = IP_NULL;
ipc_entry_t entry;
kern_return_t kr;
#if IMPORTANCE_INHERITANCE
boolean_t needboost = FALSE;
#endif
for (;;) {
ipc_port_t port = IP_NULL;
kr = ipc_right_lookup_write(space, name, &entry);
if (kr != KERN_SUCCESS)
return kr;
prev_request = entry->ie_request;
if (notify == IP_NULL && prev_request == IE_REQ_NONE) {
is_write_unlock(space);
*previousp = IP_NULL;
return KERN_SUCCESS;
}
if (entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) {
ipc_port_request_index_t new_request;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (!ipc_right_check(space, port, name, entry)) {
if (notify == IP_NULL) {
if (prev_request != IE_REQ_NONE)
previous = ipc_port_request_cancel(port, name, prev_request);
ip_unlock(port);
entry->ie_request = IE_REQ_NONE;
ipc_entry_modified(space, name, entry);
is_write_unlock(space);
break;
}
if (send_possible && immediate &&
((entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE) ||
port->ip_receiver == ipc_space_kernel || !ip_full(port))) {
if (prev_request != IE_REQ_NONE)
previous = ipc_port_request_cancel(port, name, prev_request);
ip_unlock(port);
entry->ie_request = IE_REQ_NONE;
ipc_entry_modified(space, name, entry);
is_write_unlock(space);
ipc_notify_send_possible(notify, name);
break;
}
if (prev_request != IE_REQ_NONE)
previous = ipc_port_request_cancel(port, name, prev_request);
#if IMPORTANCE_INHERITANCE
kr = ipc_port_request_alloc(port, name, notify,
send_possible, immediate,
&new_request, &needboost);
#else
kr = ipc_port_request_alloc(port, name, notify,
send_possible, immediate,
&new_request);
#endif
if (kr != KERN_SUCCESS) {
assert(previous == IP_NULL);
is_write_unlock(space);
kr = ipc_port_request_grow(port, ITS_SIZE_NONE);
if (kr != KERN_SUCCESS)
return kr;
continue;
}
assert(new_request != IE_REQ_NONE);
entry->ie_request = new_request;
ipc_entry_modified(space, name, entry);
is_write_unlock(space);
#if IMPORTANCE_INHERITANCE
if (needboost == TRUE) {
if (ipc_port_importance_delta(port, 1) == FALSE)
ip_unlock(port);
} else
#endif
ip_unlock(port);
break;
}
}
if ((send_possible || immediate) && notify != IP_NULL &&
(entry->ie_bits & MACH_PORT_TYPE_DEAD_NAME)) {
mach_port_urefs_t urefs = IE_BITS_UREFS(entry->ie_bits);
assert(urefs > 0);
if (MACH_PORT_UREFS_OVERFLOW(urefs, 1)) {
is_write_unlock(space);
if (port != IP_NULL)
ip_release(port);
return KERN_UREFS_OVERFLOW;
}
(entry->ie_bits)++;
ipc_entry_modified(space, name, entry);
is_write_unlock(space);
if (port != IP_NULL)
ip_release(port);
ipc_notify_dead_name(notify, name);
previous = IP_NULL;
break;
}
is_write_unlock(space);
if (port != IP_NULL)
ip_release(port);
if (entry->ie_bits & MACH_PORT_TYPE_PORT_OR_DEAD)
return KERN_INVALID_ARGUMENT;
else
return KERN_INVALID_RIGHT;
}
*previousp = previous;
return KERN_SUCCESS;
}
ipc_port_t
ipc_right_request_cancel(
__unused ipc_space_t space,
ipc_port_t port,
mach_port_name_t name,
ipc_entry_t entry)
{
ipc_port_t previous;
assert(ip_active(port));
assert(port == (ipc_port_t) entry->ie_object);
if (entry->ie_request == IE_REQ_NONE)
return IP_NULL;
previous = ipc_port_request_cancel(port, name, entry->ie_request);
entry->ie_request = IE_REQ_NONE;
ipc_entry_modified(space, name, entry);
return previous;
}
boolean_t
ipc_right_inuse(
ipc_space_t space,
__unused mach_port_name_t name,
ipc_entry_t entry)
{
if (IE_BITS_TYPE(entry->ie_bits) != MACH_PORT_TYPE_NONE) {
is_write_unlock(space);
return TRUE;
}
return FALSE;
}
boolean_t
ipc_right_check(
ipc_space_t space,
ipc_port_t port,
mach_port_name_t name,
ipc_entry_t entry)
{
ipc_entry_bits_t bits;
assert(is_active(space));
assert(port == (ipc_port_t) entry->ie_object);
ip_lock(port);
if (ip_active(port))
return FALSE;
bits = entry->ie_bits;
assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
assert(IE_BITS_UREFS(bits) > 0);
if (bits & MACH_PORT_TYPE_SEND) {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
assert(IE_BITS_UREFS(bits) > 0);
assert(port->ip_srights > 0);
port->ip_srights--;
} else {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
assert(IE_BITS_UREFS(bits) == 1);
assert(port->ip_sorights > 0);
port->ip_sorights--;
}
ip_unlock(port);
if ((bits & MACH_PORT_TYPE_SEND) != 0) {
ipc_hash_delete(space, (ipc_object_t)port, name, entry);
}
bits = (bits &~ IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME;
if (entry->ie_request != IE_REQ_NONE) {
if (ipc_port_request_type(port, name, entry->ie_request) != 0) {
assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
bits++;
}
entry->ie_request = IE_REQ_NONE;
}
entry->ie_bits = bits;
entry->ie_object = IO_NULL;
ipc_entry_modified(space, name, entry);
return TRUE;
}
void
ipc_right_terminate(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry)
{
ipc_entry_bits_t bits;
mach_port_type_t type;
bits = entry->ie_bits;
type = IE_BITS_TYPE(bits);
assert(!is_active(space));
switch (type) {
case MACH_PORT_TYPE_DEAD_NAME:
assert(entry->ie_request == IE_REQ_NONE);
assert(entry->ie_object == IO_NULL);
break;
case MACH_PORT_TYPE_PORT_SET: {
ipc_pset_t pset = (ipc_pset_t) entry->ie_object;
assert(entry->ie_request == IE_REQ_NONE);
assert(pset != IPS_NULL);
ips_lock(pset);
assert(ips_active(pset));
ipc_pset_destroy(pset);
break;
}
case MACH_PORT_TYPE_SEND:
case MACH_PORT_TYPE_RECEIVE:
case MACH_PORT_TYPE_SEND_RECEIVE:
case MACH_PORT_TYPE_SEND_ONCE: {
ipc_port_t port = (ipc_port_t) entry->ie_object;
ipc_port_t request;
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount = 0;
assert(port != IP_NULL);
ip_lock(port);
if (!ip_active(port)) {
ip_unlock(port);
ip_release(port);
break;
}
request = ipc_right_request_cancel_macro(space, port,
name, entry);
if (type & MACH_PORT_TYPE_SEND) {
assert(port->ip_srights > 0);
if (--port->ip_srights == 0
) {
nsrequest = port->ip_nsrequest;
if (nsrequest != IP_NULL) {
port->ip_nsrequest = IP_NULL;
mscount = port->ip_mscount;
}
}
}
if (type & MACH_PORT_TYPE_RECEIVE) {
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
ipc_port_clear_receiver(port);
ipc_port_destroy(port);
} else if (type & MACH_PORT_TYPE_SEND_ONCE) {
assert(port->ip_sorights > 0);
ip_unlock(port);
ipc_notify_send_once(port);
} else {
assert(port->ip_receiver != space);
ip_unlock(port);
ip_release(port);
}
if (nsrequest != IP_NULL)
ipc_notify_no_senders(nsrequest, mscount);
if (request != IP_NULL)
ipc_notify_port_deleted(request, name);
break;
}
default:
panic("ipc_right_terminate: strange type - 0x%x", type);
}
}
kern_return_t
ipc_right_destroy(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry,
boolean_t check_guard,
uint64_t guard)
{
ipc_entry_bits_t bits;
mach_port_type_t type;
bits = entry->ie_bits;
entry->ie_bits &= ~IE_BITS_TYPE_MASK;
type = IE_BITS_TYPE(bits);
assert(is_active(space));
switch (type) {
case MACH_PORT_TYPE_DEAD_NAME:
assert(entry->ie_request == IE_REQ_NONE);
assert(entry->ie_object == IO_NULL);
ipc_entry_dealloc(space, name, entry);
is_write_unlock(space);
break;
case MACH_PORT_TYPE_PORT_SET: {
ipc_pset_t pset = (ipc_pset_t) entry->ie_object;
assert(entry->ie_request == IE_REQ_NONE);
assert(pset != IPS_NULL);
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
ips_lock(pset);
is_write_unlock(space);
assert(ips_active(pset));
ipc_pset_destroy(pset);
break;
}
case MACH_PORT_TYPE_SEND:
case MACH_PORT_TYPE_RECEIVE:
case MACH_PORT_TYPE_SEND_RECEIVE:
case MACH_PORT_TYPE_SEND_ONCE: {
ipc_port_t port = (ipc_port_t) entry->ie_object;
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount = 0;
ipc_port_t request;
assert(port != IP_NULL);
if (type == MACH_PORT_TYPE_SEND)
ipc_hash_delete(space, (ipc_object_t) port,
name, entry);
ip_lock(port);
if (!ip_active(port)) {
assert((type & MACH_PORT_TYPE_RECEIVE) == 0);
ip_unlock(port);
entry->ie_request = IE_REQ_NONE;
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
is_write_unlock(space);
ip_release(port);
break;
}
if ((type & MACH_PORT_TYPE_RECEIVE) &&
(check_guard) && (port->ip_guarded) &&
(guard != port->ip_context)) {
uint64_t portguard = port->ip_context;
ip_unlock(port);
is_write_unlock(space);
mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_DESTROY);
return KERN_INVALID_RIGHT;
}
request = ipc_right_request_cancel_macro(space, port, name, entry);
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
is_write_unlock(space);
if (type & MACH_PORT_TYPE_SEND) {
assert(port->ip_srights > 0);
if (--port->ip_srights == 0) {
nsrequest = port->ip_nsrequest;
if (nsrequest != IP_NULL) {
port->ip_nsrequest = IP_NULL;
mscount = port->ip_mscount;
}
}
}
if (type & MACH_PORT_TYPE_RECEIVE) {
assert(ip_active(port));
assert(port->ip_receiver == space);
ipc_port_clear_receiver(port);
ipc_port_destroy(port);
} else if (type & MACH_PORT_TYPE_SEND_ONCE) {
assert(port->ip_sorights > 0);
ip_unlock(port);
ipc_notify_send_once(port);
} else {
assert(port->ip_receiver != space);
ip_unlock(port);
ip_release(port);
}
if (nsrequest != IP_NULL)
ipc_notify_no_senders(nsrequest, mscount);
if (request != IP_NULL)
ipc_notify_port_deleted(request, name);
break;
}
default:
panic("ipc_right_destroy: strange type");
}
return KERN_SUCCESS;
}
kern_return_t
ipc_right_dealloc(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry)
{
ipc_port_t port = IP_NULL;
ipc_entry_bits_t bits;
mach_port_type_t type;
bits = entry->ie_bits;
type = IE_BITS_TYPE(bits);
assert(is_active(space));
switch (type) {
case MACH_PORT_TYPE_DEAD_NAME: {
dead_name:
assert(IE_BITS_UREFS(bits) > 0);
assert(entry->ie_request == IE_REQ_NONE);
assert(entry->ie_object == IO_NULL);
if (IE_BITS_UREFS(bits) == 1) {
ipc_entry_dealloc(space, name, entry);
} else {
entry->ie_bits = bits-1;
ipc_entry_modified(space, name, entry);
}
is_write_unlock(space);
if (port != IP_NULL)
ip_release(port);
break;
}
case MACH_PORT_TYPE_SEND_ONCE: {
ipc_port_t request;
assert(IE_BITS_UREFS(bits) == 1);
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry)) {
bits = entry->ie_bits;
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
goto dead_name;
}
assert(port->ip_sorights > 0);
request = ipc_right_request_cancel_macro(space, port, name, entry);
ip_unlock(port);
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
is_write_unlock(space);
ipc_notify_send_once(port);
if (request != IP_NULL)
ipc_notify_port_deleted(request, name);
break;
}
case MACH_PORT_TYPE_SEND: {
ipc_port_t request = IP_NULL;
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount = 0;
assert(IE_BITS_UREFS(bits) > 0);
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry)) {
bits = entry->ie_bits;
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
goto dead_name;
}
assert(port->ip_srights > 0);
if (IE_BITS_UREFS(bits) == 1) {
if (--port->ip_srights == 0) {
nsrequest = port->ip_nsrequest;
if (nsrequest != IP_NULL) {
port->ip_nsrequest = IP_NULL;
mscount = port->ip_mscount;
}
}
request = ipc_right_request_cancel_macro(space, port,
name, entry);
ipc_hash_delete(space, (ipc_object_t) port,
name, entry);
ip_unlock(port);
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
is_write_unlock(space);
ip_release(port);
} else {
ip_unlock(port);
entry->ie_bits = bits-1;
ipc_entry_modified(space, name, entry);
is_write_unlock(space);
}
if (nsrequest != IP_NULL)
ipc_notify_no_senders(nsrequest, mscount);
if (request != IP_NULL)
ipc_notify_port_deleted(request, name);
break;
}
case MACH_PORT_TYPE_SEND_RECEIVE: {
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount = 0;
assert(IE_BITS_UREFS(bits) > 0);
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
ip_lock(port);
assert(ip_active(port));
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
assert(port->ip_srights > 0);
if (IE_BITS_UREFS(bits) == 1) {
if (--port->ip_srights == 0) {
nsrequest = port->ip_nsrequest;
if (nsrequest != IP_NULL) {
port->ip_nsrequest = IP_NULL;
mscount = port->ip_mscount;
}
}
entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK |
MACH_PORT_TYPE_SEND);
} else
entry->ie_bits = bits-1;
ip_unlock(port);
ipc_entry_modified(space, name, entry);
is_write_unlock(space);
if (nsrequest != IP_NULL)
ipc_notify_no_senders(nsrequest, mscount);
break;
}
default:
is_write_unlock(space);
return KERN_INVALID_RIGHT;
}
return KERN_SUCCESS;
}
kern_return_t
ipc_right_delta(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry,
mach_port_right_t right,
mach_port_delta_t delta)
{
ipc_port_t port = IP_NULL;
ipc_entry_bits_t bits;
bits = entry->ie_bits;
assert(is_active(space));
assert(right < MACH_PORT_RIGHT_NUMBER);
switch (right) {
case MACH_PORT_RIGHT_PORT_SET: {
ipc_pset_t pset;
if ((bits & MACH_PORT_TYPE_PORT_SET) == 0)
goto invalid_right;
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_PORT_SET);
assert(IE_BITS_UREFS(bits) == 0);
assert(entry->ie_request == IE_REQ_NONE);
if (delta == 0)
goto success;
if (delta != -1)
goto invalid_value;
pset = (ipc_pset_t) entry->ie_object;
assert(pset != IPS_NULL);
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
ips_lock(pset);
assert(ips_active(pset));
is_write_unlock(space);
ipc_pset_destroy(pset);
break;
}
case MACH_PORT_RIGHT_RECEIVE: {
ipc_port_t request = IP_NULL;
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
goto invalid_right;
if (delta == 0)
goto success;
if (delta != -1)
goto invalid_value;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
ip_lock(port);
assert(ip_active(port));
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
if(port->ip_guarded) {
uint64_t portguard = port->ip_context;
ip_unlock(port);
is_write_unlock(space);
mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_MOD_REFS);
goto guard_failure;
}
if (bits & MACH_PORT_TYPE_SEND) {
assert(IE_BITS_TYPE(bits) ==
MACH_PORT_TYPE_SEND_RECEIVE);
assert(IE_BITS_UREFS(bits) > 0);
assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
assert(port->ip_srights > 0);
if (port->ip_pdrequest != NULL) {
ipc_entry_modified(space, name, entry);
entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE;
ipc_hash_insert(space, (ipc_object_t) port,
name, entry);
ip_reference(port);
} else {
bits &= ~IE_BITS_TYPE_MASK;
bits |= MACH_PORT_TYPE_DEAD_NAME;
if (entry->ie_request) {
entry->ie_request = IE_REQ_NONE;
bits++;
}
entry->ie_bits = bits;
entry->ie_object = IO_NULL;
ipc_entry_modified(space, name, entry);
}
} else {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
assert(IE_BITS_UREFS(bits) == 0);
request = ipc_right_request_cancel_macro(space, port,
name, entry);
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
}
is_write_unlock(space);
ipc_port_clear_receiver(port);
ipc_port_destroy(port);
if (request != IP_NULL)
ipc_notify_port_deleted(request, name);
break;
}
case MACH_PORT_RIGHT_SEND_ONCE: {
ipc_port_t request;
if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0)
goto invalid_right;
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
assert(IE_BITS_UREFS(bits) == 1);
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry)) {
assert(!(entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE));
goto invalid_right;
}
assert(port->ip_sorights > 0);
if ((delta > 0) || (delta < -1)) {
ip_unlock(port);
goto invalid_value;
}
if (delta == 0) {
ip_unlock(port);
goto success;
}
request = ipc_right_request_cancel_macro(space, port, name, entry);
ip_unlock(port);
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
is_write_unlock(space);
ipc_notify_send_once(port);
if (request != IP_NULL)
ipc_notify_port_deleted(request, name);
break;
}
case MACH_PORT_RIGHT_DEAD_NAME: {
ipc_port_t relport = IP_NULL;
mach_port_urefs_t urefs;
if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (!ipc_right_check(space, port, name, entry)) {
ip_unlock(port);
port = IP_NULL;
goto invalid_right;
}
bits = entry->ie_bits;
relport = port;
port = IP_NULL;
} else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0)
goto invalid_right;
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
assert(IE_BITS_UREFS(bits) > 0);
assert(entry->ie_object == IO_NULL);
assert(entry->ie_request == IE_REQ_NONE);
urefs = IE_BITS_UREFS(bits);
if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta))
goto invalid_value;
if (MACH_PORT_UREFS_OVERFLOW(urefs, delta))
goto urefs_overflow;
if ((urefs + delta) == 0) {
ipc_entry_dealloc(space, name, entry);
} else {
entry->ie_bits = bits + delta;
ipc_entry_modified(space, name, entry);
}
is_write_unlock(space);
if (relport != IP_NULL)
ip_release(relport);
break;
}
case MACH_PORT_RIGHT_SEND: {
mach_port_urefs_t urefs;
ipc_port_t request = IP_NULL;
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount = 0;
if ((bits & MACH_PORT_TYPE_SEND) == 0)
goto invalid_right;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry)) {
assert((entry->ie_bits & MACH_PORT_TYPE_SEND) == 0);
goto invalid_right;
}
assert(port->ip_srights > 0);
urefs = IE_BITS_UREFS(bits);
if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) {
ip_unlock(port);
goto invalid_value;
}
if (MACH_PORT_UREFS_OVERFLOW(urefs+1, delta)) {
ip_unlock(port);
goto urefs_overflow;
}
if ((urefs + delta) == 0) {
if (--port->ip_srights == 0) {
nsrequest = port->ip_nsrequest;
if (nsrequest != IP_NULL) {
port->ip_nsrequest = IP_NULL;
mscount = port->ip_mscount;
}
}
if (bits & MACH_PORT_TYPE_RECEIVE) {
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
ip_unlock(port);
assert(IE_BITS_TYPE(bits) ==
MACH_PORT_TYPE_SEND_RECEIVE);
entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|
MACH_PORT_TYPE_SEND);
ipc_entry_modified(space, name, entry);
} else {
assert(IE_BITS_TYPE(bits) ==
MACH_PORT_TYPE_SEND);
request = ipc_right_request_cancel_macro(space, port,
name, entry);
ipc_hash_delete(space, (ipc_object_t) port,
name, entry);
ip_unlock(port);
ip_release(port);
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
}
} else {
ip_unlock(port);
entry->ie_bits = bits + delta;
ipc_entry_modified(space, name, entry);
}
is_write_unlock(space);
if (nsrequest != IP_NULL)
ipc_notify_no_senders(nsrequest, mscount);
if (request != IP_NULL)
ipc_notify_port_deleted(request, name);
break;
}
default:
panic("ipc_right_delta: strange right");
}
return KERN_SUCCESS;
success:
is_write_unlock(space);
return KERN_SUCCESS;
invalid_right:
is_write_unlock(space);
if (port != IP_NULL)
ip_release(port);
return KERN_INVALID_RIGHT;
invalid_value:
is_write_unlock(space);
return KERN_INVALID_VALUE;
urefs_overflow:
is_write_unlock(space);
return KERN_UREFS_OVERFLOW;
guard_failure:
return KERN_INVALID_RIGHT;
}
kern_return_t
ipc_right_destruct(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry,
mach_port_delta_t srdelta,
uint64_t guard)
{
ipc_port_t port = IP_NULL;
ipc_entry_bits_t bits;
mach_port_urefs_t urefs;
ipc_port_t request = IP_NULL;
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount = 0;
bits = entry->ie_bits;
assert(is_active(space));
if (((bits & MACH_PORT_TYPE_RECEIVE) == 0) ||
(srdelta && ((bits & MACH_PORT_TYPE_SEND) == 0))) {
is_write_unlock(space);
return KERN_INVALID_RIGHT;
}
if (srdelta > 0)
goto invalid_value;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
ip_lock(port);
assert(ip_active(port));
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
if(port->ip_guarded && (guard != port->ip_context)) {
uint64_t portguard = port->ip_context;
ip_unlock(port);
is_write_unlock(space);
mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_DESTROY);
return KERN_INVALID_ARGUMENT;
}
if (srdelta) {
assert(port->ip_srights > 0);
urefs = IE_BITS_UREFS(bits);
if (MACH_PORT_UREFS_UNDERFLOW(urefs, srdelta)) {
ip_unlock(port);
goto invalid_value;
}
if ((urefs + srdelta) == 0) {
if (--port->ip_srights == 0) {
nsrequest = port->ip_nsrequest;
if (nsrequest != IP_NULL) {
port->ip_nsrequest = IP_NULL;
mscount = port->ip_mscount;
}
}
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_RECEIVE);
entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|
MACH_PORT_TYPE_SEND);
} else {
entry->ie_bits = bits + srdelta;
}
}
bits = entry->ie_bits;
if (bits & MACH_PORT_TYPE_SEND) {
assert(IE_BITS_UREFS(bits) > 0);
assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
if (port->ip_pdrequest != NULL) {
ipc_entry_modified(space, name, entry);
entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE;
ipc_hash_insert(space, (ipc_object_t) port,
name, entry);
ip_reference(port);
} else {
bits &= ~IE_BITS_TYPE_MASK;
bits |= MACH_PORT_TYPE_DEAD_NAME;
if (entry->ie_request) {
entry->ie_request = IE_REQ_NONE;
bits++;
}
entry->ie_bits = bits;
entry->ie_object = IO_NULL;
ipc_entry_modified(space, name, entry);
}
} else {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
assert(IE_BITS_UREFS(bits) == 0);
request = ipc_right_request_cancel_macro(space, port,
name, entry);
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
}
is_write_unlock(space);
if (nsrequest != IP_NULL)
ipc_notify_no_senders(nsrequest, mscount);
ipc_port_clear_receiver(port);
ipc_port_destroy(port);
if (request != IP_NULL)
ipc_notify_port_deleted(request, name);
return KERN_SUCCESS;
invalid_value:
is_write_unlock(space);
return KERN_INVALID_VALUE;
}
kern_return_t
ipc_right_info(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry,
mach_port_type_t *typep,
mach_port_urefs_t *urefsp)
{
ipc_port_t port;
ipc_entry_bits_t bits;
mach_port_type_t type = 0;
ipc_port_request_index_t request;
bits = entry->ie_bits;
request = entry->ie_request;
port = (ipc_port_t) entry->ie_object;
if (bits & MACH_PORT_TYPE_RECEIVE) {
assert(IP_VALID(port));
if (request != IE_REQ_NONE) {
ip_lock(port);
assert(ip_active(port));
type |= ipc_port_request_type(port, name, request);
ip_unlock(port);
}
is_write_unlock(space);
} else if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
if (!ipc_right_check(space, port, name, entry)) {
if (request != IE_REQ_NONE)
type |= ipc_port_request_type(port, name, request);
ip_unlock(port);
is_write_unlock(space);
} else {
bits = entry->ie_bits;
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
is_write_unlock(space);
ip_release(port);
}
} else {
is_write_unlock(space);
}
type |= IE_BITS_TYPE(bits);
*typep = type;
*urefsp = IE_BITS_UREFS(bits);
return KERN_SUCCESS;
}
boolean_t
ipc_right_copyin_check(
__assert_only ipc_space_t space,
__unused mach_port_name_t name,
ipc_entry_t entry,
mach_msg_type_name_t msgt_name)
{
ipc_entry_bits_t bits;
ipc_port_t port;
bits= entry->ie_bits;
assert(is_active(space));
switch (msgt_name) {
case MACH_MSG_TYPE_MAKE_SEND:
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
return FALSE;
break;
case MACH_MSG_TYPE_MAKE_SEND_ONCE:
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
return FALSE;
break;
case MACH_MSG_TYPE_MOVE_RECEIVE:
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
return FALSE;
break;
case MACH_MSG_TYPE_COPY_SEND:
case MACH_MSG_TYPE_MOVE_SEND:
case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
if (bits & MACH_PORT_TYPE_DEAD_NAME)
break;
if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
return FALSE;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (!ip_active(port)) {
break;
}
if (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0)
return FALSE;
} else {
if ((bits & MACH_PORT_TYPE_SEND) == 0)
return FALSE;
}
break;
}
default:
panic("ipc_right_copyin_check: strange rights");
}
return TRUE;
}
kern_return_t
ipc_right_copyin(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry,
mach_msg_type_name_t msgt_name,
boolean_t deadok,
ipc_object_t *objectp,
ipc_port_t *sorightp,
ipc_port_t *releasep,
int *assertcntp)
{
ipc_entry_bits_t bits;
ipc_port_t port;
*releasep = IP_NULL;
*assertcntp = 0;
bits = entry->ie_bits;
assert(is_active(space));
switch (msgt_name) {
case MACH_MSG_TYPE_MAKE_SEND: {
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
goto invalid_right;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
ip_lock(port);
assert(ip_active(port));
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
port->ip_mscount++;
port->ip_srights++;
ip_reference(port);
ip_unlock(port);
*objectp = (ipc_object_t) port;
*sorightp = IP_NULL;
break;
}
case MACH_MSG_TYPE_MAKE_SEND_ONCE: {
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
goto invalid_right;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
ip_lock(port);
assert(ip_active(port));
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
port->ip_sorights++;
ip_reference(port);
ip_unlock(port);
*objectp = (ipc_object_t) port;
*sorightp = IP_NULL;
break;
}
case MACH_MSG_TYPE_MOVE_RECEIVE: {
ipc_port_t request = IP_NULL;
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
goto invalid_right;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
ip_lock(port);
assert(ip_active(port));
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
if (bits & MACH_PORT_TYPE_SEND) {
assert(IE_BITS_TYPE(bits) ==
MACH_PORT_TYPE_SEND_RECEIVE);
assert(IE_BITS_UREFS(bits) > 0);
assert(port->ip_srights > 0);
ipc_hash_insert(space, (ipc_object_t) port,
name, entry);
ip_reference(port);
} else {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
assert(IE_BITS_UREFS(bits) == 0);
request = ipc_right_request_cancel_macro(space, port,
name, entry);
entry->ie_object = IO_NULL;
}
entry->ie_bits = bits &~ MACH_PORT_TYPE_RECEIVE;
ipc_entry_modified(space, name, entry);
ipc_port_clear_receiver(port);
port->ip_receiver_name = MACH_PORT_NULL;
port->ip_destination = IP_NULL;
#if IMPORTANCE_INHERITANCE
if (port->ip_tempowner == 0) {
assert(IIT_NULL == port->ip_imp_task);
port->ip_tempowner = 1;
*assertcntp = port->ip_impcount;
}
#endif
ip_unlock(port);
*objectp = (ipc_object_t) port;
*sorightp = request;
break;
}
case MACH_MSG_TYPE_COPY_SEND: {
if (bits & MACH_PORT_TYPE_DEAD_NAME)
goto copy_dead;
if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
goto invalid_right;
assert(IE_BITS_UREFS(bits) > 0);
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry)) {
bits = entry->ie_bits;
*releasep = port;
goto copy_dead;
}
if ((bits & MACH_PORT_TYPE_SEND) == 0) {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
assert(port->ip_sorights > 0);
ip_unlock(port);
goto invalid_right;
}
assert(port->ip_srights > 0);
port->ip_srights++;
ip_reference(port);
ip_unlock(port);
*objectp = (ipc_object_t) port;
*sorightp = IP_NULL;
break;
}
case MACH_MSG_TYPE_MOVE_SEND: {
ipc_port_t request = IP_NULL;
if (bits & MACH_PORT_TYPE_DEAD_NAME)
goto move_dead;
if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
goto invalid_right;
assert(IE_BITS_UREFS(bits) > 0);
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry)) {
bits = entry->ie_bits;
*releasep = port;
goto move_dead;
}
if ((bits & MACH_PORT_TYPE_SEND) == 0) {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
assert(port->ip_sorights > 0);
ip_unlock(port);
goto invalid_right;
}
assert(port->ip_srights > 0);
if (IE_BITS_UREFS(bits) == 1) {
if (bits & MACH_PORT_TYPE_RECEIVE) {
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
assert(IE_BITS_TYPE(bits) ==
MACH_PORT_TYPE_SEND_RECEIVE);
ip_reference(port);
} else {
assert(IE_BITS_TYPE(bits) ==
MACH_PORT_TYPE_SEND);
request = ipc_right_request_cancel_macro(space, port,
name, entry);
ipc_hash_delete(space, (ipc_object_t) port,
name, entry);
entry->ie_object = IO_NULL;
}
entry->ie_bits = bits &~
(IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND);
} else {
port->ip_srights++;
ip_reference(port);
entry->ie_bits = bits-1;
}
ipc_entry_modified(space, name, entry);
ip_unlock(port);
*objectp = (ipc_object_t) port;
*sorightp = request;
break;
}
case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
ipc_port_t request;
if (bits & MACH_PORT_TYPE_DEAD_NAME)
goto move_dead;
if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
goto invalid_right;
assert(IE_BITS_UREFS(bits) > 0);
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry)) {
bits = entry->ie_bits;
*releasep = port;
goto move_dead;
}
if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
assert(bits & MACH_PORT_TYPE_SEND);
assert(port->ip_srights > 0);
ip_unlock(port);
goto invalid_right;
}
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
assert(IE_BITS_UREFS(bits) == 1);
assert(port->ip_sorights > 0);
request = ipc_right_request_cancel_macro(space, port, name, entry);
ip_unlock(port);
entry->ie_object = IO_NULL;
entry->ie_bits = bits &~
(IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND_ONCE);
ipc_entry_modified(space, name, entry);
*objectp = (ipc_object_t) port;
*sorightp = request;
break;
}
default:
invalid_right:
return KERN_INVALID_RIGHT;
}
return KERN_SUCCESS;
copy_dead:
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
assert(IE_BITS_UREFS(bits) > 0);
assert(entry->ie_request == IE_REQ_NONE);
assert(entry->ie_object == 0);
if (!deadok)
goto invalid_right;
*objectp = IO_DEAD;
*sorightp = IP_NULL;
return KERN_SUCCESS;
move_dead:
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
assert(IE_BITS_UREFS(bits) > 0);
assert(entry->ie_request == IE_REQ_NONE);
assert(entry->ie_object == 0);
if (!deadok)
goto invalid_right;
if (IE_BITS_UREFS(bits) == 1) {
bits &= ~MACH_PORT_TYPE_DEAD_NAME;
}
entry->ie_bits = bits-1;
ipc_entry_modified(space, name, entry);
*objectp = IO_DEAD;
*sorightp = IP_NULL;
return KERN_SUCCESS;
}
void
ipc_right_copyin_undo(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry,
mach_msg_type_name_t msgt_name,
ipc_object_t object,
ipc_port_t soright)
{
ipc_entry_bits_t bits;
bits = entry->ie_bits;
assert(is_active(space));
assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
(msgt_name == MACH_MSG_TYPE_COPY_SEND) ||
(msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE));
if (soright != IP_NULL) {
assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
(msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE));
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
assert(object != IO_DEAD);
entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) |
MACH_PORT_TYPE_DEAD_NAME | 2);
} else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE) {
assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
(msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE));
entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) |
MACH_PORT_TYPE_DEAD_NAME | 1);
} else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME) {
assert(object == IO_DEAD);
assert(IE_BITS_UREFS(bits) > 0);
if (msgt_name != MACH_MSG_TYPE_COPY_SEND) {
assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
entry->ie_bits = bits+1;
}
} else {
assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
(msgt_name == MACH_MSG_TYPE_COPY_SEND));
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
assert(object != IO_DEAD);
assert(entry->ie_object == object);
assert(IE_BITS_UREFS(bits) > 0);
if (msgt_name != MACH_MSG_TYPE_COPY_SEND) {
assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX-1);
entry->ie_bits = bits+1;
}
(void) ipc_right_check(space, (ipc_port_t) object,
name, entry);
}
ipc_entry_modified(space, name, entry);
if (object != IO_DEAD)
io_release(object);
}
static
kern_return_t
ipc_right_copyin_two_move_sends(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry,
ipc_object_t *objectp,
ipc_port_t *sorightp,
ipc_port_t *releasep)
{
ipc_entry_bits_t bits;
mach_port_urefs_t urefs;
ipc_port_t port;
ipc_port_t request = IP_NULL;
*releasep = IP_NULL;
assert(is_active(space));
bits = entry->ie_bits;
if ((bits & MACH_PORT_TYPE_SEND) == 0)
goto invalid_right;
urefs = IE_BITS_UREFS(bits);
if (urefs < 2)
goto invalid_right;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry)) {
*releasep = port;
goto invalid_right;
}
assert(port->ip_srights > 0);
if (urefs == 2) {
if (bits & MACH_PORT_TYPE_RECEIVE) {
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
assert(IE_BITS_TYPE(bits) ==
MACH_PORT_TYPE_SEND_RECEIVE);
port->ip_srights++;
ip_reference(port);
ip_reference(port);
} else {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
request = ipc_right_request_cancel_macro(space, port,
name, entry);
port->ip_srights++;
ip_reference(port);
ipc_hash_delete(space, (ipc_object_t) port,
name, entry);
entry->ie_object = IO_NULL;
}
entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND);
} else {
port->ip_srights += 2;
ip_reference(port);
ip_reference(port);
entry->ie_bits = bits-2;
}
ipc_entry_modified(space, name, entry);
ip_unlock(port);
*objectp = (ipc_object_t) port;
*sorightp = request;
return KERN_SUCCESS;
invalid_right:
return KERN_INVALID_RIGHT;
}
kern_return_t
ipc_right_copyin_two(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry,
mach_msg_type_name_t msgt_one,
mach_msg_type_name_t msgt_two,
ipc_object_t *objectp,
ipc_port_t *sorightp,
ipc_port_t *releasep)
{
kern_return_t kr;
int assertcnt = 0;
assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_one));
assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_two));
if (!ipc_right_copyin_check(space, name, entry, msgt_two)) {
return KERN_INVALID_CAPABILITY;
}
if (msgt_one == MACH_MSG_TYPE_MOVE_SEND_ONCE ||
msgt_two == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
return KERN_INVALID_RIGHT;
}
if ((msgt_one == MACH_MSG_TYPE_MAKE_SEND) ||
(msgt_one == MACH_MSG_TYPE_MAKE_SEND_ONCE) ||
(msgt_two == MACH_MSG_TYPE_MAKE_SEND) ||
(msgt_two == MACH_MSG_TYPE_MAKE_SEND_ONCE)) {
ipc_object_t object_two;
kr = ipc_right_copyin(space, name, entry,
msgt_one, FALSE,
objectp, sorightp, releasep,
&assertcnt);
assert(assertcnt == 0);
if (kr != KERN_SUCCESS) {
return kr;
}
assert(IO_VALID(*objectp));
assert(*sorightp == IP_NULL);
assert(*releasep == IP_NULL);
kr = ipc_right_copyin(space, name, entry,
msgt_two, FALSE,
&object_two, sorightp, releasep,
&assertcnt);
assert(assertcnt == 0);
assert(kr == KERN_SUCCESS);
assert(*sorightp == IP_NULL);
assert(*releasep == IP_NULL);
assert(object_two == *objectp);
assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
} else if ((msgt_one == MACH_MSG_TYPE_MOVE_SEND) &&
(msgt_two == MACH_MSG_TYPE_MOVE_SEND)) {
kr = ipc_right_copyin_two_move_sends(space, name, entry,
objectp, sorightp,
releasep);
if (kr != KERN_SUCCESS) {
return kr;
}
} else {
mach_msg_type_name_t msgt_name;
if (msgt_one == MACH_MSG_TYPE_MOVE_SEND ||
msgt_two == MACH_MSG_TYPE_MOVE_SEND) {
msgt_name = MACH_MSG_TYPE_MOVE_SEND;
} else {
msgt_name = MACH_MSG_TYPE_COPY_SEND;
}
kr = ipc_right_copyin(space, name, entry,
msgt_name, FALSE,
objectp, sorightp, releasep,
&assertcnt);
assert(assertcnt == 0);
if (kr != KERN_SUCCESS) {
return kr;
}
(void)ipc_port_copy_send((ipc_port_t)*objectp);
}
return KERN_SUCCESS;
}
kern_return_t
ipc_right_copyout(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry,
mach_msg_type_name_t msgt_name,
boolean_t overflow,
ipc_object_t object)
{
ipc_entry_bits_t bits;
ipc_port_t port;
bits = entry->ie_bits;
assert(IO_VALID(object));
assert(io_otype(object) == IOT_PORT);
assert(io_active(object));
assert(entry->ie_object == object);
port = (ipc_port_t) object;
switch (msgt_name) {
case MACH_MSG_TYPE_PORT_SEND_ONCE:
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
assert(port->ip_sorights > 0);
ip_unlock(port);
entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1);
ipc_entry_modified(space, name, entry);
break;
case MACH_MSG_TYPE_PORT_SEND:
assert(port->ip_srights > 0);
if (bits & MACH_PORT_TYPE_SEND) {
mach_port_urefs_t urefs = IE_BITS_UREFS(bits);
assert(port->ip_srights > 1);
assert(urefs > 0);
assert(urefs < MACH_PORT_UREFS_MAX);
if (urefs+1 == MACH_PORT_UREFS_MAX) {
if (overflow) {
port->ip_srights--;
ip_unlock(port);
ip_release(port);
return KERN_SUCCESS;
}
ip_unlock(port);
return KERN_UREFS_OVERFLOW;
}
port->ip_srights--;
ip_unlock(port);
ip_release(port);
} else if (bits & MACH_PORT_TYPE_RECEIVE) {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
assert(IE_BITS_UREFS(bits) == 0);
ip_unlock(port);
ip_release(port);
} else {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
assert(IE_BITS_UREFS(bits) == 0);
ip_unlock(port);
ipc_hash_insert(space, (ipc_object_t) port,
name, entry);
}
entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1;
ipc_entry_modified(space, name, entry);
break;
case MACH_MSG_TYPE_PORT_RECEIVE: {
ipc_port_t dest;
#if IMPORTANCE_INHERITANCE
natural_t assertcnt = port->ip_impcount;
#endif
assert(port->ip_mscount == 0);
assert(port->ip_receiver_name == MACH_PORT_NULL);
dest = port->ip_destination;
port->ip_receiver_name = name;
port->ip_receiver = space;
assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
if (bits & MACH_PORT_TYPE_SEND) {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
assert(IE_BITS_UREFS(bits) > 0);
assert(port->ip_srights > 0);
ip_unlock(port);
ip_release(port);
ipc_hash_delete(space, (ipc_object_t) port,
name, entry);
} else {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
assert(IE_BITS_UREFS(bits) == 0);
ip_unlock(port);
}
entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE;
ipc_entry_modified(space, name, entry);
if (dest != IP_NULL) {
#if IMPORTANCE_INHERITANCE
ip_lock(dest);
ipc_port_impcount_delta(dest, 0 - assertcnt, IP_NULL);
ip_unlock(dest);
#endif
ip_release(dest);
}
break;
}
default:
panic("ipc_right_copyout: strange rights");
}
return KERN_SUCCESS;
}
kern_return_t
ipc_right_rename(
ipc_space_t space,
mach_port_name_t oname,
ipc_entry_t oentry,
mach_port_name_t nname,
ipc_entry_t nentry)
{
ipc_port_request_index_t request = oentry->ie_request;
ipc_entry_bits_t bits = oentry->ie_bits;
ipc_object_t object = oentry->ie_object;
ipc_port_t release_port = IP_NULL;
assert(is_active(space));
assert(oname != nname);
if (request != IE_REQ_NONE) {
ipc_port_t port;
assert(bits & MACH_PORT_TYPE_PORT_RIGHTS);
port = (ipc_port_t) object;
assert(port != IP_NULL);
if (ipc_right_check(space, port, oname, oentry)) {
request = IE_REQ_NONE;
object = IO_NULL;
bits = oentry->ie_bits;
release_port = port;
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
assert(oentry->ie_request == IE_REQ_NONE);
} else {
ipc_port_request_rename(port, request, oname, nname);
ip_unlock(port);
oentry->ie_request = IE_REQ_NONE;
}
}
assert((nentry->ie_bits & IE_BITS_RIGHT_MASK) == 0);
nentry->ie_bits |= bits & IE_BITS_RIGHT_MASK;
nentry->ie_request = request;
nentry->ie_object = object;
switch (IE_BITS_TYPE(bits)) {
case MACH_PORT_TYPE_SEND: {
ipc_port_t port;
port = (ipc_port_t) object;
assert(port != IP_NULL);
ipc_hash_delete(space, (ipc_object_t) port, oname, oentry);
ipc_hash_insert(space, (ipc_object_t) port, nname, nentry);
break;
}
case MACH_PORT_TYPE_RECEIVE:
case MACH_PORT_TYPE_SEND_RECEIVE: {
ipc_port_t port;
port = (ipc_port_t) object;
assert(port != IP_NULL);
ip_lock(port);
assert(ip_active(port));
assert(port->ip_receiver_name == oname);
assert(port->ip_receiver == space);
port->ip_receiver_name = nname;
ip_unlock(port);
break;
}
case MACH_PORT_TYPE_PORT_SET: {
ipc_pset_t pset;
pset = (ipc_pset_t) object;
assert(pset != IPS_NULL);
ips_lock(pset);
assert(ips_active(pset));
assert(pset->ips_local_name == oname);
pset->ips_local_name = nname;
ips_unlock(pset);
break;
}
case MACH_PORT_TYPE_SEND_ONCE:
case MACH_PORT_TYPE_DEAD_NAME:
break;
default:
panic("ipc_right_rename: strange rights");
}
assert(oentry->ie_request == IE_REQ_NONE);
oentry->ie_object = IO_NULL;
ipc_entry_dealloc(space, oname, oentry);
ipc_entry_modified(space, nname, nentry);
is_write_unlock(space);
if (release_port != IP_NULL)
ip_release(release_port);
return KERN_SUCCESS;
}