#include <mach/port.h>
#include <mach/message.h>
#include <kern/assert.h>
+#include <kern/ipc_kobject.h>
#include <kern/misc_protos.h>
#include <ipc/port.h>
#include <ipc/ipc_entry.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>
/* Allow IPC to generate mach port guard exceptions */
* KERN_INVALID_RIGHT Name doesn't denote port/dead rights.
* KERN_INVALID_ARGUMENT Name denotes dead name, but
* immediate is FALSE or notify is IP_NULL.
- * KERN_UREFS_OVERFLOW Name denotes dead name, but
- * generating immediate notif. would overflow urefs.
* KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
*/
#if IMPORTANCE_INHERITANCE
if (needboost == TRUE) {
- if (ipc_port_importance_delta(port, 1) == FALSE)
+ if (ipc_port_importance_delta(port, IPID_OPTION_SENDPOSSIBLE, 1) == FALSE)
ip_unlock(port);
} else
#endif /* IMPORTANCE_INHERITANCE */
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;
- }
+ /* leave urefs pegged to maximum if it overflowed */
+ if (urefs < MACH_PORT_UREFS_MAX)
+ (entry->ie_bits)++; /* increment urefs */
- (entry->ie_bits)++; /* increment urefs */
ipc_entry_modified(space, name, entry);
+
is_write_unlock(space);
if (port != IP_NULL)
break;
}
+ kr = (entry->ie_bits & MACH_PORT_TYPE_PORT_OR_DEAD) ?
+ KERN_INVALID_ARGUMENT : KERN_INVALID_RIGHT;
+
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;
+ return kr;
}
*previousp = previous;
*/
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++;
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ bits++; /* increment urefs */
}
entry->ie_request = IE_REQ_NONE;
}
}
if (type & MACH_PORT_TYPE_RECEIVE) {
- wait_queue_link_t wql;
- queue_head_t links_data;
- queue_t links = &links_data;
-
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
- queue_init(links);
- ipc_port_clear_receiver(port, links);
- ipc_port_destroy(port); /* consumes our ref, unlocks */
- while(!queue_empty(links)) {
- wql = (wait_queue_link_t) dequeue(links);
- wait_queue_link_free(wql);
- }
+ ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */
} else if (type & MACH_PORT_TYPE_SEND_ONCE) {
assert(port->ip_sorights > 0);
}
if (type & MACH_PORT_TYPE_RECEIVE) {
- queue_head_t links_data;
- queue_t links = &links_data;
- wait_queue_link_t wql;
-
assert(ip_active(port));
assert(port->ip_receiver == space);
- queue_init(links);
-
- ipc_port_clear_receiver(port, links);
- ipc_port_destroy(port); /* consumes our ref, unlocks */
-
- while(!queue_empty(links)) {
- wql = (wait_queue_link_t) dequeue(links);
- wait_queue_link_free(wql);
- }
+ ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */
} else if (type & MACH_PORT_TYPE_SEND_ONCE) {
assert(port->ip_sorights > 0);
if (IE_BITS_UREFS(bits) == 1) {
ipc_entry_dealloc(space, name, entry);
} else {
- entry->ie_bits = bits-1; /* decrement urefs */
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ entry->ie_bits = bits-1; /* decrement urefs */
ipc_entry_modified(space, name, entry);
}
is_write_unlock(space);
ip_release(port);
} else {
- ip_unlock(port);
- entry->ie_bits = bits-1; /* decrement urefs */
+ ip_unlock(port);
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ entry->ie_bits = bits-1; /* decrement urefs */
ipc_entry_modified(space, name, entry);
is_write_unlock(space);
}
-
if (nsrequest != IP_NULL)
ipc_notify_no_senders(nsrequest, mscount);
entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK |
MACH_PORT_TYPE_SEND);
- } else
- entry->ie_bits = bits-1; /* decrement urefs */
-
+ } else {
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+ entry->ie_bits = bits-1; /* decrement urefs */
+ }
+ }
ip_unlock(port);
ipc_entry_modified(space, name, entry);
* KERN_SUCCESS Count was modified.
* KERN_INVALID_RIGHT Entry has wrong type.
* KERN_INVALID_VALUE Bad delta for the right.
- * KERN_UREFS_OVERFLOW OK delta, except would overflow.
*/
kern_return_t
case MACH_PORT_RIGHT_RECEIVE: {
ipc_port_t request = IP_NULL;
- queue_head_t links_data;
- queue_t links = &links_data;
- wait_queue_link_t wql;
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
goto invalid_right;
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) {
bits |= MACH_PORT_TYPE_DEAD_NAME;
if (entry->ie_request) {
entry->ie_request = IE_REQ_NONE;
- bits++;
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ bits++; /* increment urefs */
}
entry->ie_bits = bits;
entry->ie_object = IO_NULL;
}
is_write_unlock(space);
- queue_init(links);
- ipc_port_clear_receiver(port, links);
- ipc_port_destroy(port); /* consumes ref, unlocks */
-
- while(!queue_empty(links)) {
- wql = (wait_queue_link_t) dequeue(links);
- wait_queue_link_free(wql);
- }
+ ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */
if (request != IP_NULL)
ipc_notify_port_deleted(request, name);
bits = entry->ie_bits;
relport = port;
port = IP_NULL;
- } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0)
+ } 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))
+ if (delta > ((mach_port_delta_t)MACH_PORT_UREFS_MAX) ||
+ delta < (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
goto invalid_value;
- if (MACH_PORT_UREFS_OVERFLOW(urefs, delta))
- goto urefs_overflow;
+ }
+
+ urefs = IE_BITS_UREFS(bits);
+
+ if (urefs == MACH_PORT_UREFS_MAX) {
+ /*
+ * urefs are pegged due to an overflow
+ * only a delta removing all refs at once can change it
+ */
+
+ if (delta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX)))
+ delta = 0;
+ } else {
+ if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta))
+ goto invalid_value;
+ if (MACH_PORT_UREFS_OVERFLOW(urefs, delta)) {
+ /* leave urefs pegged to maximum if it overflowed */
+ delta = MACH_PORT_UREFS_MAX - urefs;
+ }
+ }
if ((urefs + delta) == 0) {
ipc_entry_dealloc(space, name, entry);
- } else {
+ } else if (delta != 0) {
entry->ie_bits = bits + delta;
ipc_entry_modified(space, name, entry);
}
+
is_write_unlock(space);
if (relport != IP_NULL)
mach_port_urefs_t urefs;
ipc_port_t request = IP_NULL;
ipc_port_t nsrequest = IP_NULL;
+ ipc_port_t port_to_release = IP_NULL;
mach_port_mscount_t mscount = 0;
if ((bits & MACH_PORT_TYPE_SEND) == 0)
goto invalid_right;
- /* maximum urefs for send is MACH_PORT_UREFS_MAX-1 */
+ /* maximum urefs for send is MACH_PORT_UREFS_MAX */
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
assert(port->ip_srights > 0);
- urefs = IE_BITS_UREFS(bits);
- if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) {
+ if (delta > ((mach_port_delta_t)MACH_PORT_UREFS_MAX) ||
+ delta < (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
ip_unlock(port);
goto invalid_value;
}
- if (MACH_PORT_UREFS_OVERFLOW(urefs+1, delta)) {
- ip_unlock(port);
- goto urefs_overflow;
+
+ urefs = IE_BITS_UREFS(bits);
+
+ if (urefs == MACH_PORT_UREFS_MAX) {
+ /*
+ * urefs are pegged due to an overflow
+ * only a delta removing all refs at once can change it
+ */
+
+ if (delta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX)))
+ delta = 0;
+ } else {
+ if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) {
+ ip_unlock(port);
+ goto invalid_value;
+ }
+ if (MACH_PORT_UREFS_OVERFLOW(urefs, delta)) {
+ /* leave urefs pegged to maximum if it overflowed */
+ delta = MACH_PORT_UREFS_MAX - urefs;
+ }
}
if ((urefs + delta) == 0) {
if (bits & MACH_PORT_TYPE_RECEIVE) {
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
- ip_unlock(port);
+ ip_unlock(port);
assert(IE_BITS_TYPE(bits) ==
MACH_PORT_TYPE_SEND_RECEIVE);
name, entry);
ip_unlock(port);
- ip_release(port);
+ port_to_release = port;
entry->ie_object = IO_NULL;
ipc_entry_dealloc(space, name, entry);
}
- } else {
+ } else if (delta != 0) {
ip_unlock(port);
entry->ie_bits = bits + delta;
ipc_entry_modified(space, name, entry);
+ } else {
+ ip_unlock(port);
}
is_write_unlock(space);
+ if (port_to_release != IP_NULL)
+ ip_release(port_to_release);
+
if (nsrequest != IP_NULL)
ipc_notify_no_senders(nsrequest, mscount);
is_write_unlock(space);
return KERN_INVALID_VALUE;
- urefs_overflow:
- is_write_unlock(space);
- return KERN_UREFS_OVERFLOW;
-
guard_failure:
- return KERN_INVALID_RIGHT;
+ return KERN_INVALID_RIGHT;
}
/*
ipc_port_t port = IP_NULL;
ipc_entry_bits_t bits;
- queue_head_t links_data;
- queue_t links = &links_data;
- wait_queue_link_t wql;
-
mach_port_urefs_t urefs;
ipc_port_t request = IP_NULL;
ipc_port_t nsrequest = IP_NULL;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
-
+
ip_lock(port);
assert(ip_active(port));
assert(port->ip_receiver_name == name);
*/
if (srdelta) {
-
+
assert(port->ip_srights > 0);
urefs = IE_BITS_UREFS(bits);
+
/*
* Since we made sure that srdelta is negative,
* the check for urefs overflow is not required.
ip_unlock(port);
goto invalid_value;
}
+
+ if (urefs == MACH_PORT_UREFS_MAX) {
+ /*
+ * urefs are pegged due to an overflow
+ * only a delta removing all refs at once can change it
+ */
+ if (srdelta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX)))
+ srdelta = 0;
+ }
+
if ((urefs + srdelta) == 0) {
if (--port->ip_srights == 0) {
nsrequest = port->ip_nsrequest;
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);
+ assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX);
if (port->ip_pdrequest != NULL) {
/*
bits |= MACH_PORT_TYPE_DEAD_NAME;
if (entry->ie_request) {
entry->ie_request = IE_REQ_NONE;
- bits++;
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ bits++; /* increment urefs */
}
entry->ie_bits = bits;
entry->ie_object = IO_NULL;
if (nsrequest != IP_NULL)
ipc_notify_no_senders(nsrequest, mscount);
- queue_init(links);
- ipc_port_clear_receiver(port, links);
- ipc_port_destroy(port); /* consumes ref, unlocks */
-
- while(!queue_empty(links)) {
- wql = (wait_queue_link_t) dequeue(links);
- wait_queue_link_free(wql);
- }
+ ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */
if (request != IP_NULL)
ipc_notify_port_deleted(request, name);
{
ipc_entry_bits_t bits;
ipc_port_t port;
-#if CONFIG_MACF_MACH
- task_t self = current_task();
- int rc = 0;
-#endif
bits= entry->ie_bits;
assert(is_active(space));
case MACH_MSG_TYPE_MAKE_SEND:
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
return FALSE;
-
-#if CONFIG_MACF_MACH
- port = (ipc_port_t) entry->ie_object;
- ip_lock(port);
- tasklabel_lock(self);
- rc = mac_port_check_make_send(&self->maclabel, &port->ip_label); tasklabel_unlock(self);
- ip_unlock(port);
- if (rc)
- return FALSE;
-#endif
break;
case MACH_MSG_TYPE_MAKE_SEND_ONCE:
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
return FALSE;
-
-#if CONFIG_MACF_MACH
- port = (ipc_port_t) entry->ie_object;
- ip_lock(port);
- tasklabel_lock(self);
- rc = mac_port_check_make_send_once(&self->maclabel, &port->ip_label);
- tasklabel_unlock(self);
- ip_unlock(port);
- if (rc)
- return FALSE;
-#endif
break;
case MACH_MSG_TYPE_MOVE_RECEIVE:
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
return FALSE;
-
-#if CONFIG_MACF_MACH
- port = (ipc_port_t) entry->ie_object;
- ip_lock(port);
- tasklabel_lock(self);
- rc = mac_port_check_move_receive(&self->maclabel, &port->ip_label);
- tasklabel_unlock(self);
- ip_unlock(port);
- if (rc)
- return FALSE;
-#endif
+ if (io_kotype(entry->ie_object) != IKOT_NONE)
+ return FALSE;
break;
case MACH_MSG_TYPE_COPY_SEND:
case MACH_MSG_TYPE_MOVE_SEND:
case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
- boolean_t active;
if (bits & MACH_PORT_TYPE_DEAD_NAME)
break;
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
- ip_lock(port);
- active = ip_active(port);
-#if CONFIG_MACF_MACH
- tasklabel_lock(self);
- switch (msgt_name) {
- case MACH_MSG_TYPE_COPY_SEND:
- rc = mac_port_check_copy_send(&self->maclabel,
- &port->ip_label);
- break;
- case MACH_MSG_TYPE_MOVE_SEND:
- rc = mac_port_check_move_send(&self->maclabel,
- &port->ip_label);
- break;
- case MACH_MSG_TYPE_MOVE_SEND_ONCE:
- rc = mac_port_check_move_send_once(&self->maclabel,
- &port->ip_label);
- break;
- default:
- panic("ipc_right_copyin_check: strange rights");
- }
- tasklabel_unlock(self);
- if (rc) {
- ip_unlock(port);
- return FALSE;
- }
-#endif
- ip_unlock(port);
-
- if (!active) {
+ /*
+ * active status peek to avoid checks that will be skipped
+ * on copyin for dead ports. Lock not held, so will not be
+ * atomic (but once dead, there's no going back).
+ */
+ if (!ip_active(port)) {
break;
}
ipc_object_t *objectp,
ipc_port_t *sorightp,
ipc_port_t *releasep,
-#if IMPORTANCE_INHERITANCE
- int *assertcntp,
-#endif /* IMPORTANCE_INHERITANCE */
- queue_t links)
+ int *assertcntp)
{
ipc_entry_bits_t bits;
ipc_port_t port;
-#if CONFIG_MACF_MACH
- task_t self = current_task();
- int rc;
-#endif
-
- *releasep = IP_NULL;
-#if IMPORTANCE_INHERITANCE
+ *releasep = IP_NULL;
*assertcntp = 0;
-#endif
bits = entry->ie_bits;
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
-#if CONFIG_MACF_MACH
- tasklabel_lock(self);
- rc = mac_port_check_make_send(&self->maclabel, &port->ip_label);
- tasklabel_unlock(self);
- if (rc) {
- ip_unlock(port);
- return KERN_NO_ACCESS;
- }
-#endif
-
port->ip_mscount++;
port->ip_srights++;
ip_reference(port);
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
-#if CONFIG_MACF_MACH
- tasklabel_lock(self);
- rc = mac_port_check_make_send_once(&self->maclabel, &port->ip_label);
- tasklabel_unlock(self);
- if (rc) {
- ip_unlock(port);
- return KERN_NO_ACCESS;
- }
-#endif
-
port->ip_sorights++;
ip_reference(port);
ip_unlock(port);
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
goto invalid_right;
+ /*
+ * Disallow moving receive-right kobjects, e.g. mk_timer ports
+ * The ipc_port structure uses the kdata union of kobject and
+ * imp_task exclusively. Thus, general use of a kobject port as
+ * a receive right can cause type confusion in the importance
+ * code.
+ */
+ if (io_kotype(entry->ie_object) != IKOT_NONE) {
+ /*
+ * Distinguish an invalid right, e.g., trying to move
+ * a send right as a receive right, from this
+ * situation which is, "This is a valid receive right,
+ * but it's also a kobject and you can't move it."
+ */
+ return KERN_INVALID_CAPABILITY;
+ }
+
port = (ipc_port_t) entry->ie_object;
assert(port != IP_NULL);
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
-#if CONFIG_MACF_MACH
- tasklabel_lock(self);
- rc = mac_port_check_move_receive(&self->maclabel,
- &port->ip_label);
- tasklabel_unlock(self);
- if (rc) {
- ip_unlock(port);
- return KERN_NO_ACCESS;
- }
-#endif
-
if (bits & MACH_PORT_TYPE_SEND) {
assert(IE_BITS_TYPE(bits) ==
MACH_PORT_TYPE_SEND_RECEIVE);
entry->ie_bits = bits &~ MACH_PORT_TYPE_RECEIVE;
ipc_entry_modified(space, name, entry);
- ipc_port_clear_receiver(port, links);
+ (void)ipc_port_clear_receiver(port, FALSE); /* don't destroy the port/mqueue */
port->ip_receiver_name = MACH_PORT_NULL;
port->ip_destination = IP_NULL;
* destination port (see ipc_port_check_circularity()).
*/
if (port->ip_tempowner == 0) {
- assert(port->ip_taskptr == 0);
+ assert(IIT_NULL == port->ip_imp_task);
/* ports in limbo have to be tempowner */
port->ip_tempowner = 1;
}
/* port is locked and active */
-#if CONFIG_MACF_MACH
- tasklabel_lock(self);
- rc = mac_port_check_copy_send(&self->maclabel, &port->ip_label);
- tasklabel_unlock(self);
- if (rc) {
- ip_unlock(port);
- return KERN_NO_ACCESS;
- }
-#endif
-
if ((bits & MACH_PORT_TYPE_SEND) == 0) {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
assert(port->ip_sorights > 0);
}
/* port is locked and active */
-#if CONFIG_MACF_MACH
- tasklabel_lock (self);
- rc = mac_port_check_copy_send (&self->maclabel, &port->ip_label);
- tasklabel_unlock (self);
- if (rc)
- {
- ip_unlock (port);
- return KERN_NO_ACCESS;
- }
-#endif
-
if ((bits & MACH_PORT_TYPE_SEND) == 0) {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
assert(port->ip_sorights > 0);
ipc_hash_delete(space, (ipc_object_t) port,
name, entry);
entry->ie_object = IO_NULL;
+ /* transfer entry's reference to caller */
}
entry->ie_bits = bits &~
(IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND);
} else {
port->ip_srights++;
ip_reference(port);
- entry->ie_bits = bits-1; /* decrement urefs */
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ entry->ie_bits = bits-1; /* decrement urefs */
}
+
ipc_entry_modified(space, name, entry);
ip_unlock(port);
if (ipc_right_check(space, port, name, entry)) {
bits = entry->ie_bits;
+ *releasep = port;
goto move_dead;
}
/* port is locked and active */
-#if CONFIG_MACF_MACH
- tasklabel_lock (self);
- rc = mac_port_check_copy_send (&self->maclabel, &port->ip_label);
- tasklabel_unlock (self);
- if (rc)
- {
- ip_unlock (port);
- return KERN_NO_ACCESS;
- }
-#endif
-
if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
assert(bits & MACH_PORT_TYPE_SEND);
assert(port->ip_srights > 0);
if (IE_BITS_UREFS(bits) == 1) {
bits &= ~MACH_PORT_TYPE_DEAD_NAME;
}
- entry->ie_bits = bits-1; /* decrement urefs */
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ entry->ie_bits = bits-1; /* decrement urefs */
+
ipc_entry_modified(space, name, entry);
*objectp = IO_DEAD;
*sorightp = IP_NULL;
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; /* increment urefs */
+ assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX);
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ entry->ie_bits = bits+1; /* increment urefs */
}
} else {
assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
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; /* increment urefs */
+ assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX);
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ entry->ie_bits = bits+1; /* increment urefs */
}
/*
}
/*
- * Routine: ipc_right_copyin_two
+ * Routine: ipc_right_copyin_two_move_sends
* Purpose:
* Like ipc_right_copyin with MACH_MSG_TYPE_MOVE_SEND
* and deadok == FALSE, except that this moves two
* KERN_SUCCESS Acquired an object.
* KERN_INVALID_RIGHT Name doesn't denote correct right.
*/
-
+static
kern_return_t
-ipc_right_copyin_two(
+ipc_right_copyin_two_move_sends(
ipc_space_t space,
mach_port_name_t name,
ipc_entry_t entry,
mach_port_urefs_t urefs;
ipc_port_t port;
ipc_port_t request = IP_NULL;
-#if CONFIG_MACF_MACH
- task_t self = current_task();
- int rc;
-#endif
*releasep = IP_NULL;
}
/* port is locked and active */
-#if CONFIG_MACF_MACH
- tasklabel_lock(self);
- rc = mac_port_check_copy_send(&self->maclabel, &port->ip_label);
- tasklabel_unlock(self);
- if (rc) {
- ip_unlock(port);
- return KERN_NO_ACCESS;
- }
-#endif
-
assert(port->ip_srights > 0);
if (urefs == 2) {
port->ip_srights += 2;
ip_reference(port);
ip_reference(port);
- entry->ie_bits = bits-2; /* decrement urefs */
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ entry->ie_bits = bits-2; /* decrement urefs */
}
ipc_entry_modified(space, name, entry);
return KERN_INVALID_RIGHT;
}
+
+/*
+ * Routine: ipc_right_copyin_two
+ * Purpose:
+ * Like ipc_right_copyin with two dispositions,
+ * each of which results in a send or send-once right,
+ * and deadok = FALSE.
+ * Conditions:
+ * The space is write-locked and active.
+ * The object is returned with two refs/rights.
+ * Returns:
+ * KERN_SUCCESS Acquired an object.
+ * KERN_INVALID_RIGHT Name doesn't denote correct right(s).
+ * KERN_INVALID_CAPABILITY Name doesn't denote correct right for msgt_two.
+ */
+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));
+
+
+ /*
+ * Pre-validate the second disposition is possible all by itself.
+ */
+ if (!ipc_right_copyin_check(space, name, entry, msgt_two)) {
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ /*
+ * This is a little tedious to make atomic, because
+ * there are 25 combinations of valid dispositions.
+ * However, most are easy.
+ */
+
+ /*
+ * If either is move-sonce, then there must be an error.
+ */
+ 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)) {
+ /*
+ * One of the dispositions needs a receive right.
+ *
+ * If the copyin below succeeds, we know the receive
+ * right is there (because the pre-validation of
+ * the second disposition already succeeded in our
+ * caller).
+ *
+ * Hence the port is not in danger of dying.
+ */
+ 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);
+
+ /*
+ * Now copyin the second (previously validated)
+ * disposition. The result can't be a dead port,
+ * as no valid disposition can make us lose our
+ * receive right.
+ */
+ 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)) {
+ /*
+ * This is an easy case. Just use our
+ * handy-dandy special-purpose copyin call
+ * to get two send rights for the price of one.
+ */
+ 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;
+
+ /*
+ * Must be either a single move-send and a
+ * copy-send, or two copy-send dispositions.
+ * Use the disposition with the greatest side
+ * effects for the actual copyin - then just
+ * duplicate the send right you get back.
+ */
+ 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;
+ }
+
+ /*
+ * Copy the right we got back. If it is dead now,
+ * that's OK. Neither right will be usable to send
+ * a message anyway.
+ */
+ (void)ipc_port_copy_send((ipc_port_t)*objectp);
+ }
+
+ return KERN_SUCCESS;
+}
+
+
/*
* Routine: ipc_right_copyout
* Purpose:
* The object is unlocked; the space isn't.
* Returns:
* KERN_SUCCESS Copied out capability.
- * KERN_UREFS_OVERFLOW User-refs would overflow;
- * guaranteed not to happen with a fresh entry
- * or if overflow=TRUE was specified.
*/
kern_return_t
mach_port_name_t name,
ipc_entry_t entry,
mach_msg_type_name_t msgt_name,
- boolean_t overflow,
+ __unused boolean_t overflow,
ipc_object_t object)
{
ipc_entry_bits_t bits;
ipc_port_t port;
-#if CONFIG_MACF_MACH
- int rc;
-#endif
bits = entry->ie_bits;
switch (msgt_name) {
case MACH_MSG_TYPE_PORT_SEND_ONCE:
-
+
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
+ assert(IE_BITS_UREFS(bits) == 0);
assert(port->ip_sorights > 0);
-#if CONFIG_MACF_MACH
- if (space->is_task) {
- tasklabel_lock(space->is_task);
- rc = mac_port_check_hold_send_once(&space->is_task->maclabel,
- &port->ip_label);
- tasklabel_unlock(space->is_task);
-
- if (rc) {
- ip_unlock(port);
- return KERN_NO_ACCESS;
- }
- }
-#endif
/* transfer send-once right and ref to entry */
ip_unlock(port);
- entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1);
+ entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1); /* set urefs to 1 */
ipc_entry_modified(space, name, entry);
break;
case MACH_MSG_TYPE_PORT_SEND:
assert(port->ip_srights > 0);
-#if CONFIG_MACF_MACH
- if (space->is_task) {
- tasklabel_lock(space->is_task);
- rc = mac_port_check_hold_send(&space->is_task->maclabel,
- &port->ip_label);
- tasklabel_unlock(space->is_task);
-
- if (rc) {
- ip_unlock(port);
- return KERN_NO_ACCESS;
- }
- }
-#endif
-
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) {
- /* leave urefs pegged to maximum */
+ assert(urefs <= MACH_PORT_UREFS_MAX);
- port->ip_srights--;
- ip_unlock(port);
- ip_release(port);
- return KERN_SUCCESS;
- }
+ if (urefs == MACH_PORT_UREFS_MAX) {
+ /*
+ * leave urefs pegged to maximum,
+ * consume send right and ref
+ */
+ port->ip_srights--;
ip_unlock(port);
- return KERN_UREFS_OVERFLOW;
+ ip_release(port);
+ return KERN_SUCCESS;
}
+
+ /* consume send right and ref */
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);
- /* transfer send right to entry */
+ /* transfer send right to entry, consume ref */
ip_unlock(port);
ip_release(port);
-
+
} else {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
assert(IE_BITS_UREFS(bits) == 0);
name, entry);
}
- entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1;
+ entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1; /* increment urefs */
ipc_entry_modified(space, name, entry);
break;
assert(port->ip_receiver_name == MACH_PORT_NULL);
dest = port->ip_destination;
-#if CONFIG_MACF_MACH
- if (space->is_task) {
- tasklabel_lock(space->is_task);
- rc = mac_port_check_hold_receive(&space->is_task->maclabel,
- &port->ip_label);
- tasklabel_unlock(space->is_task);
-
- if (rc) {
- ip_unlock(port);
- return KERN_NO_ACCESS;
- }
- }
-#endif
-
port->ip_receiver_name = name;
port->ip_receiver = space;
* getting enqueued.
*/
ip_lock(dest);
- assert(dest->ip_impcount >= assertcnt);
- dest->ip_impcount -= assertcnt;
+ ipc_port_impcount_delta(dest, 0 - assertcnt, IP_NULL);
ip_unlock(dest);
#endif /* IMPORTANCE_INHERITANCE */
ip_release(dest);
ips_lock(pset);
assert(ips_active(pset));
- assert(pset->ips_local_name == oname);
- pset->ips_local_name = nname;
ips_unlock(pset);
break;
}