X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/8f6c56a50524aa785f7e596d52dddfb331e18961..a1c7dba18ef36983396c282fe85292db066e39db:/osfmk/ipc/ipc_right.c diff --git a/osfmk/ipc/ipc_right.c b/osfmk/ipc/ipc_right.c index a39e804c3..205f91ca7 100644 --- a/osfmk/ipc/ipc_right.c +++ b/osfmk/ipc/ipc_right.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2007 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -53,6 +53,13 @@ * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ +/* + * NOTICE: This file was modified by McAfee Research in 2004 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + * Copyright (c) 2005-2006 SPARTA, Inc. + */ /* */ /* @@ -79,7 +86,16 @@ #include #include #include +#include +#include +/* Allow IPC to generate mach port guard exceptions */ +extern kern_return_t +mach_port_guard_exception( + mach_port_name_t name, + uint64_t inguard, + uint64_t portguard, + unsigned reason); /* * Routine: ipc_right_lookup_write * Purpose: @@ -104,7 +120,7 @@ ipc_right_lookup_write( is_write_lock(space); - if (!space->is_active) { + if (!is_active(space)) { is_write_unlock(space); return KERN_INVALID_TASK; } @@ -146,7 +162,7 @@ ipc_right_lookup_two_write( is_write_lock(space); - if (!space->is_active) { + if (!is_active(space)) { is_write_unlock(space); return KERN_INVALID_TASK; } @@ -189,7 +205,7 @@ ipc_right_reverse( /* would switch on io_otype to handle multiple types of object */ - assert(space->is_active); + assert(is_active(space)); assert(io_otype(object) == IOT_PORT); port = (ipc_port_t) object; @@ -235,12 +251,6 @@ ipc_right_reverse( * registered send-once right. If notify is IP_NULL, * just cancels the previously registered request. * - * This interacts with the IE_BITS_COMPAT, because they - * both use ie_request. If this is a compat entry, then - * previous always gets IP_NULL. If notify is IP_NULL, - * then the entry remains a compat entry. Otherwise - * the real dead-name request is registered and the entry - * is no longer a compat entry. * Conditions: * Nothing locked. May allocate memory. * Only consumes/returns refs if successful. @@ -257,28 +267,44 @@ ipc_right_reverse( */ kern_return_t -ipc_right_dnrequest( +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_t previous; + 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 /* IMPORTANCE_INHERITANCE */ for (;;) { - ipc_entry_t entry; - ipc_entry_bits_t bits; - kern_return_t kr; + ipc_port_t port = IP_NULL; kr = ipc_right_lookup_write(space, name, &entry); if (kr != KERN_SUCCESS) return kr; + /* space is write-locked and active */ - bits = entry->ie_bits; - if (bits & MACH_PORT_TYPE_PORT_RIGHTS) { - ipc_port_t port; - ipc_port_request_index_t request; + + prev_request = entry->ie_request; + + /* if nothing to do or undo, we're done */ + if (notify == IP_NULL && prev_request == IE_REQ_NONE) { + is_write_unlock(space); + *previousp = IP_NULL; + return KERN_SUCCESS; + } + + /* see if the entry is of proper type for requests */ + 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); @@ -286,87 +312,116 @@ ipc_right_dnrequest( if (!ipc_right_check(space, port, name, entry)) { /* port is locked and active */ + /* if no new request, just cancel previous */ if (notify == IP_NULL) { - previous = ipc_right_dncancel_macro( - space, port, name, entry); - + 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 a registered soright exists, - * want to atomically switch with it. - * If ipc_port_dncancel finds us a - * soright, then the following - * ipc_port_dnrequest will reuse - * that slot, so we are guaranteed - * not to unlock and retry. + * send-once rights, kernel objects, and non-full other queues + * fire immediately (if immediate specified). */ + 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); - previous = ipc_right_dncancel_macro(space, - port, name, entry); + ipc_notify_send_possible(notify, name); + break; + } - kr = ipc_port_dnrequest(port, name, notify, - &request); + /* + * If there is a previous request, free it. Any subsequent + * allocation cannot fail, thus assuring an atomic swap. + */ + 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 /* IMPORTANCE_INHERITANCE */ if (kr != KERN_SUCCESS) { assert(previous == IP_NULL); is_write_unlock(space); - kr = ipc_port_dngrow(port, - ITS_SIZE_NONE); + kr = ipc_port_request_grow(port, ITS_SIZE_NONE); /* port is unlocked */ + if (kr != KERN_SUCCESS) return kr; continue; } - assert(request != 0); - ip_unlock(port); - entry->ie_request = request; + assert(new_request != IE_REQ_NONE); + entry->ie_request = new_request; + ipc_entry_modified(space, name, entry); is_write_unlock(space); - break; - } else { - /* - * Our capability bits were changed by ipc_right_check - * because it found an inactive port and removed our - * references to it (converting our entry into a dead - * one). Reload the bits (and obviously we can't use - * the port name anymore). - */ - bits = entry->ie_bits; +#if IMPORTANCE_INHERITANCE + if (needboost == TRUE) { + if (ipc_port_importance_delta(port, 1) == FALSE) + ip_unlock(port); + } else +#endif /* IMPORTANCE_INHERITANCE */ + ip_unlock(port); + break; } + /* entry may have changed to dead-name by ipc_right_check() */ - assert(bits & MACH_PORT_TYPE_DEAD_NAME); } - if ((bits & MACH_PORT_TYPE_DEAD_NAME) && - immediate && (notify != IP_NULL)) { - mach_port_urefs_t urefs = IE_BITS_UREFS(bits); + /* treat send_possible requests as immediate w.r.t. dead-name */ + 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(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); 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)++; /* increment urefs */ + 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 (bits & MACH_PORT_TYPE_PORT_OR_DEAD) + + 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; @@ -377,9 +432,9 @@ ipc_right_dnrequest( } /* - * Routine: ipc_right_dncancel + * Routine: ipc_right_request_cancel * Purpose: - * Cancel a dead-name request and return the send-once right. + * Cancel a notification request and return the send-once right. * Afterwards, entry->ie_request == 0. * Conditions: * The space must be write-locked; the port must be locked. @@ -387,21 +442,24 @@ ipc_right_dnrequest( */ ipc_port_t -ipc_right_dncancel( +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 dnrequest; + ipc_port_t previous; assert(ip_active(port)); assert(port == (ipc_port_t) entry->ie_object); - dnrequest = ipc_port_dncancel(port, name, entry->ie_request); - entry->ie_request = 0; + if (entry->ie_request == IE_REQ_NONE) + return IP_NULL; - return dnrequest; + previous = ipc_port_request_cancel(port, name, entry->ie_request); + entry->ie_request = IE_REQ_NONE; + ipc_entry_modified(space, name, entry); + return previous; } /* @@ -435,8 +493,10 @@ ipc_right_inuse( * Conditions: * The space is write-locked; the port is not locked. * If returns FALSE, the port is also locked and active. - * Otherwise, entry is converted to a dead name, freeing - * a reference to port. + * Otherwise, entry is converted to a dead name. + * + * Caller is responsible for a reference to port if it + * had died (returns TRUE). */ boolean_t @@ -448,13 +508,12 @@ ipc_right_check( { ipc_entry_bits_t bits; - assert(space->is_active); + assert(is_active(space)); assert(port == (ipc_port_t) entry->ie_object); ip_lock(port); if (ip_active(port)) return FALSE; - ip_unlock(port); /* this was either a pure send right or a send-once right */ @@ -462,53 +521,63 @@ ipc_right_check( assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); assert(IE_BITS_UREFS(bits) > 0); - if (bits & MACH_PORT_TYPE_SEND) { + 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); + /* + * delete SEND rights from ipc hash. + */ - ipc_port_release(port); - - /* convert entry to dead name */ - - if ((bits & MACH_PORT_TYPE_SEND) && !(bits & MACH_PORT_TYPE_RECEIVE)) + if ((bits & MACH_PORT_TYPE_SEND) != 0) { ipc_hash_delete(space, (ipc_object_t)port, name, entry); + } + /* convert entry to dead name */ bits = (bits &~ IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME; /* * If there was a notification request outstanding on this - * name, and since the port went dead, that notification - * must already be on its way up from the port layer. We - * don't need the index of the notification port anymore. + * name, and the port went dead, that notification + * must already be on its way up from the port layer. * - * JMM - We also add a reference to the entry since the - * notification only carries the name and NOT a reference - * (or right). This makes for pretty loose reference - * counting, since it is only happenstance that we - * detected the notification in progress like this. - * But most (all?) calls that try to deal with this entry - * will also come through here, so the reference gets added - * before the entry gets used eventually (I would rather it - * be explicit in the notification generation, though) + * Add the reference that the notification carries. It + * is done here, and not in the notification delivery, + * because the latter doesn't have a space reference and + * trying to actually move a send-right reference would + * get short-circuited into a MACH_PORT_DEAD by IPC. Since + * all calls that deal with the right eventually come + * through here, it has the same result. + * + * Once done, clear the request index so we only account + * for it once. */ - if (entry->ie_request != 0) { - assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); - entry->ie_request = 0; - bits++; + 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; } /* - * Routine: ipc_right_clean + * Routine: ipc_right_terminate * Purpose: - * Cleans up an entry in a dead space. + * Cleans up an entry in a terminated space. * The entry isn't deallocated or removed * from reverse hash tables. * Conditions: @@ -516,7 +585,7 @@ ipc_right_check( */ void -ipc_right_clean( +ipc_right_terminate( ipc_space_t space, mach_port_name_t name, ipc_entry_t entry) @@ -527,7 +596,7 @@ ipc_right_clean( bits = entry->ie_bits; type = IE_BITS_TYPE(bits); - assert(!space->is_active); + assert(!is_active(space)); /* * IE_BITS_COMPAT/ipc_right_dncancel doesn't have this @@ -540,19 +609,18 @@ ipc_right_clean( switch (type) { case MACH_PORT_TYPE_DEAD_NAME: - assert(entry->ie_request == 0); + 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 == 0); + assert(entry->ie_request == IE_REQ_NONE); assert(pset != IPS_NULL); ips_lock(pset); assert(ips_active(pset)); - ipc_pset_destroy(pset); /* consumes ref, unlocks */ break; } @@ -562,7 +630,7 @@ ipc_right_clean( 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 dnrequest; + ipc_port_t request; ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; @@ -570,12 +638,12 @@ ipc_right_clean( ip_lock(port); if (!ip_active(port)) { + ip_unlock(port); ip_release(port); - ip_check_unlock(port); break; } - dnrequest = ipc_right_dncancel_macro(space, port, + request = ipc_right_request_cancel_macro(space, port, name, entry); if (type & MACH_PORT_TYPE_SEND) { @@ -591,11 +659,21 @@ ipc_right_clean( } 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); - ipc_port_clear_receiver(port); + 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); + } + } else if (type & MACH_PORT_TYPE_SEND_ONCE) { assert(port->ip_sorights > 0); ip_unlock(port); @@ -604,20 +682,20 @@ ipc_right_clean( } else { assert(port->ip_receiver != space); - ip_release(port); - ip_unlock(port); /* port is active */ + ip_unlock(port); + ip_release(port); } if (nsrequest != IP_NULL) ipc_notify_no_senders(nsrequest, mscount); - if (dnrequest != IP_NULL) - ipc_notify_port_deleted(dnrequest, name); + if (request != IP_NULL) + ipc_notify_port_deleted(request, name); break; } default: - panic("ipc_right_clean: strange type"); + panic("ipc_right_terminate: strange type - 0x%x", type); } } @@ -626,7 +704,7 @@ ipc_right_clean( * Purpose: * Destroys an entry in a space. * Conditions: - * The space is write-locked. + * The space is write-locked (returns unlocked). * The space must be active. * Returns: * KERN_SUCCESS The entry was destroyed. @@ -636,7 +714,9 @@ kern_return_t ipc_right_destroy( ipc_space_t space, mach_port_name_t name, - ipc_entry_t entry) + ipc_entry_t entry, + boolean_t check_guard, + uint64_t guard) { ipc_entry_bits_t bits; mach_port_type_t type; @@ -645,28 +725,30 @@ ipc_right_destroy( entry->ie_bits &= ~IE_BITS_TYPE_MASK; type = IE_BITS_TYPE(bits); - assert(space->is_active); + assert(is_active(space)); switch (type) { case MACH_PORT_TYPE_DEAD_NAME: - assert(entry->ie_request == 0); + 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 == 0); + 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); - assert(ips_active(pset)); + is_write_unlock(space); + assert(ips_active(pset)); ipc_pset_destroy(pset); /* consumes ref, unlocks */ break; } @@ -678,7 +760,7 @@ ipc_right_destroy( 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 dnrequest; + ipc_port_t request; assert(port != IP_NULL); @@ -690,20 +772,34 @@ ipc_right_destroy( if (!ip_active(port)) { assert((type & MACH_PORT_TYPE_RECEIVE) == 0); - ip_release(port); - ip_check_unlock(port); - - entry->ie_request = 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; } - dnrequest = ipc_right_dncancel_macro(space, port, name, entry); + /* For receive rights, check for guarding */ + if ((type & MACH_PORT_TYPE_RECEIVE) && + (check_guard) && (port->ip_guarded) && + (guard != port->ip_context)) { + /* Guard Violation */ + uint64_t portguard = port->ip_context; + ip_unlock(port); + is_write_unlock(space); + /* Raise mach port guard exception */ + 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); @@ -717,11 +813,23 @@ 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); - ipc_port_clear_receiver(port); + 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); + } + } else if (type & MACH_PORT_TYPE_SEND_ONCE) { assert(port->ip_sorights > 0); ip_unlock(port); @@ -730,15 +838,17 @@ ipc_right_destroy( } else { assert(port->ip_receiver != space); - ip_release(port); ip_unlock(port); + ip_release(port); } if (nsrequest != IP_NULL) ipc_notify_no_senders(nsrequest, mscount); - if (dnrequest != IP_NULL) - ipc_notify_port_deleted(dnrequest, name); + if (request != IP_NULL) + ipc_notify_port_deleted(request, name); + + break; } @@ -769,7 +879,7 @@ ipc_right_dealloc( 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; @@ -777,28 +887,32 @@ ipc_right_dealloc( type = IE_BITS_TYPE(bits); - assert(space->is_active); + assert(is_active(space)); switch (type) { case MACH_PORT_TYPE_DEAD_NAME: { dead_name: assert(IE_BITS_UREFS(bits) > 0); - assert(entry->ie_request == 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 + } else { entry->ie_bits = bits-1; /* decrement urefs */ - + ipc_entry_modified(space, name, entry); + } is_write_unlock(space); + + /* release any port that got converted to dead name below */ + if (port != IP_NULL) + ip_release(port); break; } case MACH_PORT_TYPE_SEND_ONCE: { - ipc_port_t port, dnrequest; + ipc_port_t request; assert(IE_BITS_UREFS(bits) == 1); @@ -809,13 +923,13 @@ ipc_right_dealloc( bits = entry->ie_bits; assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); - goto dead_name; + goto dead_name; /* it will release port */ } /* port is locked and active */ assert(port->ip_sorights > 0); - dnrequest = ipc_right_dncancel_macro(space, port, name, entry); + request = ipc_right_request_cancel_macro(space, port, name, entry); ip_unlock(port); entry->ie_object = IO_NULL; @@ -825,14 +939,13 @@ ipc_right_dealloc( ipc_notify_send_once(port); - if (dnrequest != IP_NULL) - ipc_notify_port_deleted(dnrequest, name); + if (request != IP_NULL) + ipc_notify_port_deleted(request, name); break; } case MACH_PORT_TYPE_SEND: { - ipc_port_t port; - ipc_port_t dnrequest = IP_NULL; + ipc_port_t request = IP_NULL; ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; @@ -845,7 +958,7 @@ ipc_right_dealloc( 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; + goto dead_name; /* it will release port */ } /* port is locked and active */ @@ -860,32 +973,34 @@ ipc_right_dealloc( } } - dnrequest = ipc_right_dncancel_macro(space, port, + request = ipc_right_request_cancel_macro(space, port, name, entry); ipc_hash_delete(space, (ipc_object_t) port, name, entry); - ip_release(port); + ip_unlock(port); entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + ip_release(port); - } else + } else { + ip_unlock(port); entry->ie_bits = bits-1; /* decrement urefs */ - - /* even if dropped a ref, port is active */ - ip_unlock(port); - is_write_unlock(space); + ipc_entry_modified(space, name, entry); + is_write_unlock(space); + } + if (nsrequest != IP_NULL) ipc_notify_no_senders(nsrequest, mscount); - if (dnrequest != IP_NULL) - ipc_notify_port_deleted(dnrequest, name); + if (request != IP_NULL) + ipc_notify_port_deleted(request, name); break; } case MACH_PORT_TYPE_SEND_RECEIVE: { - ipc_port_t port; ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; @@ -915,6 +1030,8 @@ ipc_right_dealloc( entry->ie_bits = bits-1; /* decrement urefs */ ip_unlock(port); + + ipc_entry_modified(space, name, entry); is_write_unlock(space); if (nsrequest != IP_NULL) @@ -953,8 +1070,9 @@ ipc_right_delta( 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; @@ -966,7 +1084,7 @@ ipc_right_delta( * we postpone doing so when we are holding the space lock. */ - assert(space->is_active); + assert(is_active(space)); assert(right < MACH_PORT_RIGHT_NUMBER); /* Rights-specific restrictions and operations. */ @@ -980,7 +1098,7 @@ ipc_right_delta( assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_PORT_SET); assert(IE_BITS_UREFS(bits) == 0); - assert(entry->ie_request == 0); + assert(entry->ie_request == IE_REQ_NONE); if (delta == 0) goto success; @@ -991,12 +1109,9 @@ ipc_right_delta( 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); @@ -1006,8 +1121,10 @@ ipc_right_delta( } case MACH_PORT_RIGHT_RECEIVE: { - ipc_port_t port; - ipc_port_t dnrequest = IP_NULL; + 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; @@ -1031,7 +1148,17 @@ ipc_right_delta( assert(ip_active(port)); assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); - + + /* Mach Port Guard Checking */ + if(port->ip_guarded) { + uint64_t portguard = port->ip_context; + ip_unlock(port); + is_write_unlock(space); + /* Raise mach port guard exception */ + 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); @@ -1039,42 +1166,67 @@ ipc_right_delta( assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); assert(port->ip_srights > 0); - /* - * The remaining send right turns into a - * dead name. Notice we don't decrement - * ip_srights, generate a no-senders notif, - * or use ipc_right_dncancel, because the - * port is destroyed "first". - */ - bits &= ~IE_BITS_TYPE_MASK; - bits |= MACH_PORT_TYPE_DEAD_NAME; - if (entry->ie_request) { - entry->ie_request = 0; - bits++; + if (port->ip_pdrequest != NULL) { + /* + * Since another task has requested a + * destroy notification for this port, it + * isn't actually being destroyed - the receive + * right is just being moved to another task. + * Since we still have one or more send rights, + * we need to record the loss of the receive + * right and enter the remaining send right + * into the hash table. + */ + 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 { + /* + * The remaining send right turns into a + * dead name. Notice we don't decrement + * ip_srights, generate a no-senders notif, + * or use ipc_right_dncancel, because the + * port is destroyed "first". + */ + 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); } - entry->ie_bits = bits; - entry->ie_object = IO_NULL; } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); assert(IE_BITS_UREFS(bits) == 0); - dnrequest = ipc_right_dncancel_macro(space, port, + 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); + queue_init(links); + ipc_port_clear_receiver(port, links); ipc_port_destroy(port); /* consumes ref, unlocks */ - if (dnrequest != IP_NULL) - ipc_notify_port_deleted(dnrequest, name); + while(!queue_empty(links)) { + wql = (wait_queue_link_t) dequeue(links); + wait_queue_link_free(wql); + } + + if (request != IP_NULL) + ipc_notify_port_deleted(request, name); break; } case MACH_PORT_RIGHT_SEND_ONCE: { - ipc_port_t port, dnrequest; + ipc_port_t request; if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) goto invalid_right; @@ -1103,7 +1255,7 @@ ipc_right_delta( goto success; } - dnrequest = ipc_right_dncancel_macro(space, port, name, entry); + request = ipc_right_request_cancel_macro(space, port, name, entry); ip_unlock(port); entry->ie_object = IO_NULL; @@ -1113,16 +1265,16 @@ ipc_right_delta( ipc_notify_send_once(port); - if (dnrequest != IP_NULL) - ipc_notify_port_deleted(dnrequest, name); + 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) { - ipc_port_t port; port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); @@ -1130,16 +1282,19 @@ ipc_right_delta( if (!ipc_right_check(space, port, name, entry)) { /* port is locked and active */ 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 == 0); + assert(entry->ie_request == IE_REQ_NONE); urefs = IE_BITS_UREFS(bits); if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) @@ -1149,19 +1304,21 @@ ipc_right_delta( if ((urefs + delta) == 0) { ipc_entry_dealloc(space, name, entry); - } - else + } 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 port; - ipc_port_t dnrequest = IP_NULL; + ipc_port_t request = IP_NULL; ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; @@ -1203,37 +1360,41 @@ ipc_right_delta( 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); - dnrequest = ipc_right_dncancel_macro(space, port, + 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 + } else { + ip_unlock(port); entry->ie_bits = bits + delta; + ipc_entry_modified(space, name, entry); + } - /* even if dropped a ref, port is active */ - ip_unlock(port); is_write_unlock(space); if (nsrequest != IP_NULL) ipc_notify_no_senders(nsrequest, mscount); - if (dnrequest != IP_NULL) - ipc_notify_port_deleted(dnrequest, name); + if (request != IP_NULL) + ipc_notify_port_deleted(request, name); break; } @@ -1249,6 +1410,8 @@ ipc_right_delta( invalid_right: is_write_unlock(space); + if (port != IP_NULL) + ip_release(port); return KERN_INVALID_RIGHT; invalid_value: @@ -1258,17 +1421,200 @@ ipc_right_delta( urefs_overflow: is_write_unlock(space); return KERN_UREFS_OVERFLOW; + + guard_failure: + return KERN_INVALID_RIGHT; +} + +/* + * Routine: ipc_right_destruct + * Purpose: + * Deallocates the receive right and modifies the + * user-reference count for the send rights as requested. + * Conditions: + * The space is write-locked, and is unlocked upon return. + * The space must be active. + * Returns: + * KERN_SUCCESS Count was modified. + * KERN_INVALID_RIGHT Entry has wrong type. + * KERN_INVALID_VALUE Bad delta for the 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; + + 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; + 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); + + /* Mach Port Guard Checking */ + 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; + } + + /* + * First reduce the send rights as requested and + * adjust the entry->ie_bits accordingly. The + * ipc_entry_modified() call is made once the receive + * right is destroyed too. + */ + + 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. + */ + 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; + } + } + + /* + * Now destroy the receive right. Update space and + * entry accordingly. + */ + + 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) { + /* + * Since another task has requested a + * destroy notification for this port, it + * isn't actually being destroyed - the receive + * right is just being moved to another task. + * Since we still have one or more send rights, + * we need to record the loss of the receive + * right and enter the remaining send right + * into the hash table. + */ + 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 { + /* + * The remaining send right turns into a + * dead name. Notice we don't decrement + * ip_srights, generate a no-senders notif, + * or use ipc_right_dncancel, because the + * port is destroyed "first". + */ + 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); + } + + /* Unlock space */ + is_write_unlock(space); + + 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); + } + + if (request != IP_NULL) + ipc_notify_port_deleted(request, name); + + return KERN_SUCCESS; + + invalid_value: + is_write_unlock(space); + return KERN_INVALID_VALUE; + } + /* * Routine: ipc_right_info * Purpose: * Retrieves information about the right. * Conditions: - * The space is write-locked, and is unlocked upon return - * if the call is unsuccessful. The space must be active. + * The space is active and write-locked. + * The space is unlocked upon return. * Returns: - * KERN_SUCCESS Retrieved info; space still locked. + * KERN_SUCCESS Retrieved info */ kern_return_t @@ -1279,27 +1625,48 @@ ipc_right_info( mach_port_type_t *typep, mach_port_urefs_t *urefsp) { + ipc_port_t port; ipc_entry_bits_t bits; - mach_port_type_t type; + 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_SEND_RIGHTS) { - ipc_port_t port = (ipc_port_t) entry->ie_object; + if (bits & MACH_PORT_TYPE_RECEIVE) { + assert(IP_VALID(port)); - if (ipc_right_check(space, port, name, entry)) { + 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) { + /* + * validate port is still alive - if so, get request + * types while we still have it locked. Otherwise, + * recapture the (now dead) bits. + */ + 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); - } else - ip_unlock(port); + is_write_unlock(space); + ip_release(port); + } + } else { + is_write_unlock(space); } - type = IE_BITS_TYPE(bits); - request = entry->ie_request; - - if (request != 0) - type |= MACH_PORT_TYPE_DNREQUEST; + type |= IE_BITS_TYPE(bits); *typep = type; *urefsp = IE_BITS_UREFS(bits); @@ -1322,24 +1689,30 @@ ipc_right_copyin_check( mach_msg_type_name_t msgt_name) { ipc_entry_bits_t bits; + ipc_port_t port; bits= entry->ie_bits; - assert(space->is_active); + 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: - case MACH_MSG_TYPE_MOVE_RECEIVE: 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: { - ipc_port_t port; - boolean_t active; if (bits & MACH_PORT_TYPE_DEAD_NAME) break; @@ -1350,11 +1723,12 @@ ipc_right_copyin_check( port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); - ip_lock(port); - active = ip_active(port); - 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; } @@ -1407,17 +1781,28 @@ ipc_right_copyin( mach_msg_type_name_t msgt_name, boolean_t deadok, ipc_object_t *objectp, - ipc_port_t *sorightp) + ipc_port_t *sorightp, + ipc_port_t *releasep, +#if IMPORTANCE_INHERITANCE + int *assertcntp, +#endif /* IMPORTANCE_INHERITANCE */ + queue_t links) { ipc_entry_bits_t bits; - + ipc_port_t port; + + *releasep = IP_NULL; + +#if IMPORTANCE_INHERITANCE + *assertcntp = 0; +#endif + bits = entry->ie_bits; - assert(space->is_active); + assert(is_active(space)); switch (msgt_name) { case MACH_MSG_TYPE_MAKE_SEND: { - ipc_port_t port; if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) goto invalid_right; @@ -1441,7 +1826,6 @@ ipc_right_copyin( } case MACH_MSG_TYPE_MAKE_SEND_ONCE: { - ipc_port_t port; if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) goto invalid_right; @@ -1464,8 +1848,7 @@ ipc_right_copyin( } case MACH_MSG_TYPE_MOVE_RECEIVE: { - ipc_port_t port; - ipc_port_t dnrequest = IP_NULL; + ipc_port_t request = IP_NULL; if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) goto invalid_right; @@ -1491,25 +1874,44 @@ ipc_right_copyin( assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); assert(IE_BITS_UREFS(bits) == 0); - dnrequest = ipc_right_dncancel_macro(space, port, + 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); - + ipc_port_clear_receiver(port, links); port->ip_receiver_name = MACH_PORT_NULL; port->ip_destination = IP_NULL; + +#if IMPORTANCE_INHERITANCE + /* + * Account for boosts the current task is going to lose when + * copying this right in. Tempowner ports have either not + * been accounting to any task (and therefore are already in + * "limbo" state w.r.t. assertions) or to some other specific + * task. As we have no way to drop the latter task's assertions + * here, We'll deduct those when we enqueue it on its + * destination port (see ipc_port_check_circularity()). + */ + if (port->ip_tempowner == 0) { + assert(IIT_NULL == port->ip_imp_task); + + /* ports in limbo have to be tempowner */ + port->ip_tempowner = 1; + *assertcntp = port->ip_impcount; + } +#endif /* IMPORTANCE_INHERITANCE */ + ip_unlock(port); *objectp = (ipc_object_t) port; - *sorightp = dnrequest; + *sorightp = request; break; } case MACH_MSG_TYPE_COPY_SEND: { - ipc_port_t port; if (bits & MACH_PORT_TYPE_DEAD_NAME) goto copy_dead; @@ -1526,6 +1928,7 @@ ipc_right_copyin( if (ipc_right_check(space, port, name, entry)) { bits = entry->ie_bits; + *releasep = port; goto copy_dead; } /* port is locked and active */ @@ -1550,8 +1953,7 @@ ipc_right_copyin( } case MACH_MSG_TYPE_MOVE_SEND: { - ipc_port_t port; - ipc_port_t dnrequest = IP_NULL; + ipc_port_t request = IP_NULL; if (bits & MACH_PORT_TYPE_DEAD_NAME) goto move_dead; @@ -1568,6 +1970,7 @@ 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 */ @@ -1594,7 +1997,7 @@ ipc_right_copyin( assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); - dnrequest = ipc_right_dncancel_macro(space, port, + request = ipc_right_request_cancel_macro(space, port, name, entry); ipc_hash_delete(space, (ipc_object_t) port, name, entry); @@ -1607,17 +2010,16 @@ ipc_right_copyin( ip_reference(port); entry->ie_bits = bits-1; /* decrement urefs */ } - + ipc_entry_modified(space, name, entry); ip_unlock(port); *objectp = (ipc_object_t) port; - *sorightp = dnrequest; + *sorightp = request; break; } case MACH_MSG_TYPE_MOVE_SEND_ONCE: { - ipc_port_t port; - ipc_port_t dnrequest; + ipc_port_t request; if (bits & MACH_PORT_TYPE_DEAD_NAME) goto move_dead; @@ -1650,15 +2052,15 @@ ipc_right_copyin( assert(IE_BITS_UREFS(bits) == 1); assert(port->ip_sorights > 0); - dnrequest = ipc_right_dncancel_macro(space, port, name, entry); + 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 = dnrequest; + *sorightp = request; break; } @@ -1672,7 +2074,7 @@ ipc_right_copyin( copy_dead: assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); assert(IE_BITS_UREFS(bits) > 0); - assert(entry->ie_request == 0); + assert(entry->ie_request == IE_REQ_NONE); assert(entry->ie_object == 0); if (!deadok) @@ -1685,7 +2087,7 @@ ipc_right_copyin( move_dead: assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); assert(IE_BITS_UREFS(bits) > 0); - assert(entry->ie_request == 0); + assert(entry->ie_request == IE_REQ_NONE); assert(entry->ie_object == 0); if (!deadok) @@ -1695,7 +2097,7 @@ ipc_right_copyin( bits &= ~MACH_PORT_TYPE_DEAD_NAME; } entry->ie_bits = bits-1; /* decrement urefs */ - + ipc_entry_modified(space, name, entry); *objectp = IO_DEAD; *sorightp = IP_NULL; return KERN_SUCCESS; @@ -1725,7 +2127,7 @@ ipc_right_copyin_undo( bits = entry->ie_bits; - assert(space->is_active); + assert(is_active(space)); assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || (msgt_name == MACH_MSG_TYPE_COPY_SEND) || @@ -1776,15 +2178,15 @@ ipc_right_copyin_undo( name, entry); /* object is dead so it is not locked */ } - + ipc_entry_modified(space, name, entry); /* release the reference acquired by copyin */ if (object != IO_DEAD) - ipc_object_release(object); + io_release(object); } /* - * 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 @@ -1796,21 +2198,24 @@ 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, ipc_object_t *objectp, - ipc_port_t *sorightp) + 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 dnrequest = IP_NULL; + ipc_port_t request = IP_NULL; - assert(space->is_active); + *releasep = IP_NULL; + + assert(is_active(space)); bits = entry->ie_bits; @@ -1825,6 +2230,7 @@ ipc_right_copyin_two( assert(port != IP_NULL); if (ipc_right_check(space, port, name, entry)) { + *releasep = port; goto invalid_right; } /* port is locked and active */ @@ -1844,7 +2250,7 @@ ipc_right_copyin_two( } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); - dnrequest = ipc_right_dncancel_macro(space, port, + request = ipc_right_request_cancel_macro(space, port, name, entry); port->ip_srights++; @@ -1860,16 +2266,200 @@ ipc_right_copyin_two( ip_reference(port); entry->ie_bits = bits-2; /* decrement urefs */ } + ipc_entry_modified(space, name, entry); + ip_unlock(port); *objectp = (ipc_object_t) port; - *sorightp = dnrequest; + *sorightp = request; return KERN_SUCCESS; invalid_right: 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) +{ + queue_head_t links_data; + queue_t links = &links_data; + kern_return_t kr; + +#if IMPORTANCE_INHERITANCE + int assertcnt = 0; +#endif + + queue_init(links); + + 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; + +#if IMPORTANCE_INHERITANCE + kr = ipc_right_copyin(space, name, entry, + msgt_one, FALSE, + objectp, sorightp, releasep, + &assertcnt, links); + assert(assertcnt == 0); +#else + kr = ipc_right_copyin(space, name, entry, + msgt_one, FALSE, + objectp, sorightp, releasep, + links); +#endif /* IMPORTANCE_INHERITANCE */ + 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. + */ +#if IMPORTANCE_INHERITANCE + kr = ipc_right_copyin(space, name, entry, + msgt_two, FALSE, + &object_two, sorightp, releasep, + &assertcnt, links); + assert(assertcnt == 0); +#else + kr = ipc_right_copyin(space, name, entry, + msgt_two, FALSE, + &object_two, sorightp, releasep, + links); +#endif /* IMPORTANCE_INHERITANCE */ + 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; + } + +#if IMPORTANCE_INHERITANCE + kr = ipc_right_copyin(space, name, entry, + msgt_name, FALSE, + objectp, sorightp, releasep, + &assertcnt, links); + assert(assertcnt == 0); +#else + kr = ipc_right_copyin(space, name, entry, + msgt_name, FALSE, + objectp, sorightp, releasep, + links); +#endif /* IMPORTANCE_INHERITANCE */ + 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); + } + + assert(queue_empty(links)); + + return KERN_SUCCESS; +} + + /* * Routine: ipc_right_copyout * Purpose: @@ -1925,6 +2515,7 @@ ipc_right_copyout( 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: @@ -1942,25 +2533,26 @@ ipc_right_copyout( /* leave urefs pegged to maximum */ port->ip_srights--; - ip_release(port); ip_unlock(port); + ip_release(port); return KERN_SUCCESS; } ip_unlock(port); return KERN_UREFS_OVERFLOW; } - port->ip_srights--; - ip_release(port); 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 */ - ip_release(port); ip_unlock(port); + ip_release(port); + } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); assert(IE_BITS_UREFS(bits) == 0); @@ -1975,11 +2567,16 @@ ipc_right_copyout( } 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 /* IMPORTANCE_INHERITANCE */ + assert(port->ip_mscount == 0); assert(port->ip_receiver_name == MACH_PORT_NULL); dest = port->ip_destination; @@ -1994,8 +2591,8 @@ ipc_right_copyout( assert(IE_BITS_UREFS(bits) > 0); assert(port->ip_srights > 0); - ip_release(port); ip_unlock(port); + ip_release(port); /* entry is locked holding ref, so can use port */ @@ -2009,16 +2606,28 @@ ipc_right_copyout( ip_unlock(port); } entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE; + ipc_entry_modified(space, name, entry); - if (dest != IP_NULL) - ipc_port_release(dest); + if (dest != IP_NULL) { +#if IMPORTANCE_INHERITANCE + /* + * Deduct the assertion counts we contributed to + * the old destination port. They've already + * been reflected into the task as a result of + * getting enqueued. + */ + ip_lock(dest); + ipc_port_impcount_delta(dest, 0 - assertcnt, IP_NULL); + ip_unlock(dest); +#endif /* IMPORTANCE_INHERITANCE */ + ip_release(dest); + } break; } default: panic("ipc_right_copyout: strange rights"); } - return KERN_SUCCESS; } @@ -2046,8 +2655,9 @@ ipc_right_rename( 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(space->is_active); + assert(is_active(space)); assert(oname != nname); /* @@ -2057,7 +2667,7 @@ ipc_right_rename( * Note IE_BITS_COMPAT implies ie_request != 0. */ - if (request != 0) { + if (request != IE_REQ_NONE) { ipc_port_t port; assert(bits & MACH_PORT_TYPE_PORT_RIGHTS); @@ -2065,17 +2675,18 @@ ipc_right_rename( assert(port != IP_NULL); if (ipc_right_check(space, port, oname, oentry)) { - request = 0; + 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 == 0); + assert(oentry->ie_request == IE_REQ_NONE); } else { /* port is locked and active */ - ipc_port_dnrename(port, request, oname, nname); + ipc_port_request_rename(port, request, oname, nname); ip_unlock(port); - oentry->ie_request = 0; + oentry->ie_request = IE_REQ_NONE; } } @@ -2141,10 +2752,14 @@ ipc_right_rename( panic("ipc_right_rename: strange rights"); } - assert(oentry->ie_request == 0); + 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; }