X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/39236c6e673c41db228275375ab7fdb0f837b292..4d15aeb193b2c68f1d38666c317f8d3734f5f083:/osfmk/ipc/ipc_right.c diff --git a/osfmk/ipc/ipc_right.c b/osfmk/ipc/ipc_right.c index f4e102b47..7a937f5be 100644 --- a/osfmk/ipc/ipc_right.c +++ b/osfmk/ipc/ipc_right.c @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,7 @@ #include #include #include +#include #include /* Allow IPC to generate mach port guard exceptions */ @@ -260,8 +262,6 @@ ipc_right_reverse( * 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. */ @@ -377,7 +377,7 @@ ipc_right_request_alloc( #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 */ @@ -396,15 +396,12 @@ ipc_right_request_alloc( 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) @@ -415,15 +412,15 @@ ipc_right_request_alloc( 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; @@ -562,8 +559,9 @@ ipc_right_check( */ 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; } @@ -658,20 +656,10 @@ ipc_right_terminate( } 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); @@ -812,22 +800,10 @@ ipc_right_destroy( } 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); @@ -899,7 +875,9 @@ ipc_right_dealloc( 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); @@ -984,12 +962,13 @@ ipc_right_dealloc( 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); @@ -1025,9 +1004,12 @@ ipc_right_dealloc( 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); @@ -1058,7 +1040,6 @@ ipc_right_dealloc( * 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 @@ -1121,9 +1102,6 @@ ipc_right_delta( 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; @@ -1162,7 +1140,6 @@ ipc_right_delta( 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) { @@ -1193,7 +1170,9 @@ ipc_right_delta( 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; @@ -1210,14 +1189,7 @@ ipc_right_delta( } 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); @@ -1287,26 +1259,46 @@ ipc_right_delta( 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) @@ -1319,12 +1311,13 @@ ipc_right_delta( 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); @@ -1337,14 +1330,31 @@ ipc_right_delta( 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) { @@ -1359,7 +1369,7 @@ ipc_right_delta( 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); @@ -1376,19 +1386,24 @@ ipc_right_delta( 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); @@ -1417,12 +1432,8 @@ ipc_right_delta( 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; } /* @@ -1450,10 +1461,6 @@ ipc_right_destruct( 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; @@ -1474,7 +1481,7 @@ ipc_right_destruct( port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); - + ip_lock(port); assert(ip_active(port)); assert(port->ip_receiver_name == name); @@ -1497,10 +1504,11 @@ ipc_right_destruct( */ 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. @@ -1509,6 +1517,16 @@ ipc_right_destruct( 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; @@ -1533,7 +1551,7 @@ ipc_right_destruct( 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) { /* @@ -1563,7 +1581,8 @@ ipc_right_destruct( 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; @@ -1584,14 +1603,7 @@ ipc_right_destruct( 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); @@ -1689,10 +1701,6 @@ ipc_right_copyin_check( { 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)); @@ -1701,54 +1709,23 @@ ipc_right_copyin_check( 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; @@ -1759,35 +1736,12 @@ ipc_right_copyin_check( 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; } @@ -1842,23 +1796,13 @@ ipc_right_copyin( 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; @@ -1878,16 +1822,6 @@ ipc_right_copyin( 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); @@ -1911,16 +1845,6 @@ ipc_right_copyin( 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); @@ -1936,6 +1860,23 @@ ipc_right_copyin( 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); @@ -1944,17 +1885,6 @@ ipc_right_copyin( 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); @@ -1975,7 +1905,7 @@ ipc_right_copyin( 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; @@ -1990,7 +1920,7 @@ ipc_right_copyin( * 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; @@ -2027,16 +1957,6 @@ ipc_right_copyin( } /* 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); @@ -2079,17 +1999,6 @@ ipc_right_copyin( } /* 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); @@ -2117,14 +2026,18 @@ ipc_right_copyin( 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); @@ -2151,21 +2064,11 @@ ipc_right_copyin( 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); @@ -2222,7 +2125,10 @@ ipc_right_copyin( 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; @@ -2279,8 +2185,10 @@ ipc_right_copyin_undo( 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) || @@ -2291,8 +2199,10 @@ ipc_right_copyin_undo( 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 */ } /* @@ -2312,7 +2222,7 @@ ipc_right_copyin_undo( } /* - * 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 @@ -2324,9 +2234,9 @@ ipc_right_copyin_undo( * 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, @@ -2338,10 +2248,6 @@ ipc_right_copyin_two( 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; @@ -2365,16 +2271,6 @@ ipc_right_copyin_two( } /* 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) { @@ -2404,7 +2300,9 @@ ipc_right_copyin_two( 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); @@ -2418,6 +2316,158 @@ ipc_right_copyin_two( 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: @@ -2437,9 +2487,6 @@ ipc_right_copyin_two( * 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 @@ -2448,14 +2495,11 @@ ipc_right_copyout( 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; @@ -2468,79 +2512,53 @@ ipc_right_copyout( 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); @@ -2554,7 +2572,7 @@ ipc_right_copyout( 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; @@ -2569,20 +2587,6 @@ ipc_right_copyout( 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; @@ -2619,8 +2623,7 @@ ipc_right_copyout( * 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); @@ -2740,9 +2743,7 @@ ipc_right_rename( ips_lock(pset); assert(ips_active(pset)); - assert(pset->ips_local_name == oname); - pset->ips_local_name = nname; ips_unlock(pset); break; }