X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/378393581903b274cb7a4d18e0d978071a6b592d..15129b1c8dbb3650c63b70adb1cad9af601c6c17:/osfmk/ipc/ipc_right.c diff --git a/osfmk/ipc/ipc_right.c b/osfmk/ipc/ipc_right.c index 9f228a43a..f4e102b47 100644 --- a/osfmk/ipc/ipc_right.c +++ b/osfmk/ipc/ipc_right.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2007 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_FREE_COPYRIGHT@ @@ -47,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. + */ /* */ /* @@ -73,7 +86,15 @@ #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: @@ -98,7 +119,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; } @@ -140,7 +161,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; } @@ -183,7 +204,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; @@ -229,12 +250,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. @@ -251,28 +266,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); @@ -280,87 +311,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; @@ -371,9 +431,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. @@ -381,21 +441,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; } /* @@ -429,8 +492,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 @@ -442,13 +507,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 */ @@ -456,53 +520,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. + * + * 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. * - * 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) + * 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: @@ -510,7 +584,7 @@ ipc_right_check( */ void -ipc_right_clean( +ipc_right_terminate( ipc_space_t space, mach_port_name_t name, ipc_entry_t entry) @@ -521,7 +595,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 @@ -534,19 +608,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; } @@ -556,7 +629,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; @@ -564,12 +637,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) { @@ -585,11 +658,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); @@ -598,20 +681,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); } } @@ -620,7 +703,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. @@ -630,7 +713,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; @@ -639,28 +724,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; } @@ -672,7 +759,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); @@ -684,20 +771,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); @@ -711,11 +812,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); @@ -724,15 +837,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; } @@ -763,7 +878,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; @@ -771,28 +886,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); @@ -803,13 +922,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; @@ -819,14 +938,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; @@ -839,7 +957,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 */ @@ -854,32 +972,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; @@ -909,6 +1029,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) @@ -947,8 +1069,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; @@ -960,7 +1083,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. */ @@ -974,7 +1097,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; @@ -985,12 +1108,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); @@ -1000,8 +1120,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; @@ -1025,7 +1147,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); @@ -1033,42 +1165,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; @@ -1097,7 +1254,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; @@ -1107,16 +1264,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); @@ -1124,16 +1281,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)) @@ -1143,19 +1303,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; @@ -1197,37 +1359,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; } @@ -1243,6 +1409,8 @@ ipc_right_delta( invalid_right: is_write_unlock(space); + if (port != IP_NULL) + ip_release(port); return KERN_INVALID_RIGHT; invalid_value: @@ -1252,17 +1420,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 @@ -1273,27 +1624,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); @@ -1316,23 +1688,66 @@ ipc_right_copyin_check( mach_msg_type_name_t msgt_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(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; + +#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 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) @@ -1346,6 +1761,30 @@ ipc_right_copyin_check( 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) { @@ -1401,17 +1840,32 @@ 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; +#if CONFIG_MACF_MACH + task_t self = current_task(); + int rc; +#endif + *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; @@ -1424,6 +1878,16 @@ 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); @@ -1435,7 +1899,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; @@ -1448,6 +1911,16 @@ 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); @@ -1458,8 +1931,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; @@ -1472,6 +1944,17 @@ 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); @@ -1485,25 +1968,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(port->ip_taskptr == 0); + + /* 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; @@ -1520,10 +2022,21 @@ 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 */ +#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); @@ -1544,8 +2057,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; @@ -1562,10 +2074,22 @@ 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) == 0) { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); assert(port->ip_sorights > 0); @@ -1588,7 +2112,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); @@ -1601,17 +2125,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; @@ -1632,6 +2155,17 @@ 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_ONCE) == 0) { assert(bits & MACH_PORT_TYPE_SEND); assert(port->ip_srights > 0); @@ -1644,15 +2178,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; } @@ -1666,7 +2200,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) @@ -1679,7 +2213,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) @@ -1689,7 +2223,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; @@ -1719,7 +2253,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) || @@ -1770,11 +2304,11 @@ 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); } /* @@ -1797,14 +2331,21 @@ ipc_right_copyin_two( 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; +#if CONFIG_MACF_MACH + task_t self = current_task(); + int rc; +#endif + + *releasep = IP_NULL; - assert(space->is_active); + assert(is_active(space)); bits = entry->ie_bits; @@ -1819,10 +2360,21 @@ 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 */ +#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) { @@ -1838,7 +2390,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++; @@ -1854,10 +2406,12 @@ 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: @@ -1899,6 +2453,9 @@ ipc_right_copyout( { ipc_entry_bits_t bits; ipc_port_t port; +#if CONFIG_MACF_MACH + int rc; +#endif bits = entry->ie_bits; @@ -1915,15 +2472,43 @@ ipc_right_copyout( assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); 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); + 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); @@ -1936,25 +2521,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); @@ -1969,15 +2555,34 @@ 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; +#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; @@ -1988,8 +2593,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 */ @@ -2003,16 +2608,29 @@ 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); + assert(dest->ip_impcount >= assertcnt); + dest->ip_impcount -= assertcnt; + ip_unlock(dest); +#endif /* IMPORTANCE_INHERITANCE */ + ip_release(dest); + } break; } default: panic("ipc_right_copyout: strange rights"); } - return KERN_SUCCESS; } @@ -2040,8 +2658,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); /* @@ -2051,7 +2670,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); @@ -2059,17 +2678,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; } } @@ -2135,10 +2755,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; }