X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/d190cdc3f5544636abb56dc1874be391d3e1b148..4ba76501152d51ccb5647018f3192c6096367d48:/osfmk/ipc/ipc_right.c diff --git a/osfmk/ipc/ipc_right.c b/osfmk/ipc/ipc_right.c index c5f5cec0c..00a256e17 100644 --- a/osfmk/ipc/ipc_right.c +++ b/osfmk/ipc/ipc_right.c @@ -2,7 +2,7 @@ * Copyright (c) 2000-2007 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * 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 @@ -11,10 +11,10 @@ * 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. - * + * * 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, @@ -22,34 +22,34 @@ * 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_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_FREE_COPYRIGHT@ */ -/* +/* * Mach Operating System * Copyright (c) 1991,1990,1989 Carnegie Mellon University * All Rights Reserved. - * + * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. - * + * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. - * + * * Carnegie Mellon requests users of this software to return to - * + * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 - * + * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include @@ -89,13 +90,6 @@ #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: @@ -110,9 +104,9 @@ mach_port_guard_exception( kern_return_t ipc_right_lookup_write( - ipc_space_t space, - mach_port_name_t name, - ipc_entry_t *entryp) + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t *entryp) { ipc_entry_t entry; @@ -149,11 +143,11 @@ ipc_right_lookup_write( kern_return_t ipc_right_lookup_two_write( - ipc_space_t space, - mach_port_name_t name1, - ipc_entry_t *entryp1, - mach_port_name_t name2, - ipc_entry_t *entryp2) + ipc_space_t space, + mach_port_name_t name1, + ipc_entry_t *entryp1, + mach_port_name_t name2, + ipc_entry_t *entryp2) { ipc_entry_t entry1; ipc_entry_t entry2; @@ -169,10 +163,12 @@ ipc_right_lookup_two_write( if ((entry1 = ipc_entry_lookup(space, name1)) == IE_NULL) { is_write_unlock(space); + mach_port_guard_exception(name1, 0, 0, kGUARD_EXC_INVALID_NAME); return KERN_INVALID_NAME; } if ((entry2 = ipc_entry_lookup(space, name2)) == IE_NULL) { is_write_unlock(space); + mach_port_guard_exception(name2, 0, 0, kGUARD_EXC_INVALID_NAME); return KERN_INVALID_NAME; } *entryp1 = entry1; @@ -194,10 +190,10 @@ ipc_right_lookup_two_write( boolean_t ipc_right_reverse( - ipc_space_t space, - ipc_object_t object, - mach_port_name_t *namep, - ipc_entry_t *entryp) + ipc_space_t space, + ipc_object_t object, + mach_port_name_t *namep, + ipc_entry_t *entryp) { ipc_port_t port; mach_port_name_t name; @@ -208,7 +204,7 @@ ipc_right_reverse( assert(is_active(space)); assert(io_otype(object) == IOT_PORT); - port = (ipc_port_t) object; + port = ip_object_to_port(object); ip_lock(port); if (!ip_active(port)) { @@ -225,17 +221,17 @@ ipc_right_reverse( assert(entry != IE_NULL); assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE); - assert(port == (ipc_port_t) entry->ie_object); + assert(port == ip_object_to_port(entry->ie_object)); *namep = name; *entryp = entry; return TRUE; } - if (ipc_hash_lookup(space, (ipc_object_t) port, namep, entryp)) { + if (ipc_hash_lookup(space, ip_to_object(port), namep, entryp)) { assert((entry = *entryp) != IE_NULL); assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND); - assert(port == (ipc_port_t) entry->ie_object); + assert(port == ip_object_to_port(entry->ie_object)); return TRUE; } @@ -266,12 +262,12 @@ ipc_right_reverse( kern_return_t ipc_right_request_alloc( - ipc_space_t space, - mach_port_name_t name, - boolean_t immediate, - boolean_t send_possible, - ipc_port_t notify, - ipc_port_t *previousp) + ipc_space_t space, + mach_port_name_t name, + boolean_t immediate, + boolean_t send_possible, + ipc_port_t notify, + ipc_port_t *previousp) { ipc_port_request_index_t prev_request; ipc_port_t previous = IP_NULL; @@ -286,11 +282,12 @@ ipc_right_request_alloc( ipc_port_t port = IP_NULL; kr = ipc_right_lookup_write(space, name, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* space is write-locked and active */ - + prev_request = entry->ie_request; /* if nothing to do or undo, we're done */ @@ -304,16 +301,17 @@ ipc_right_request_alloc( if (entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) { ipc_port_request_index_t new_request; - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); - if (!ipc_right_check(space, port, name, entry)) { + if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) { /* port is locked and active */ /* if no new request, just cancel previous */ if (notify == IP_NULL) { - if (prev_request != IE_REQ_NONE) + 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); @@ -327,9 +325,10 @@ ipc_right_request_alloc( */ 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) + 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); @@ -343,17 +342,18 @@ ipc_right_request_alloc( * If there is a previous request, free it. Any subsequent * allocation cannot fail, thus assuring an atomic swap. */ - if (prev_request != IE_REQ_NONE) + 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); + send_possible, immediate, + &new_request, &needboost); #else kr = ipc_port_request_alloc(port, name, notify, - send_possible, immediate, - &new_request); + send_possible, immediate, + &new_request); #endif /* IMPORTANCE_INHERITANCE */ if (kr != KERN_SUCCESS) { assert(previous == IP_NULL); @@ -362,8 +362,9 @@ ipc_right_request_alloc( kr = ipc_port_request_grow(port, ITS_SIZE_NONE); /* port is unlocked */ - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } continue; } @@ -376,16 +377,16 @@ ipc_right_request_alloc( #if IMPORTANCE_INHERITANCE if (needboost == TRUE) { - if (ipc_port_importance_delta(port, IPID_OPTION_SENDPOSSIBLE, 1) == FALSE) + if (ipc_port_importance_delta(port, IPID_OPTION_SENDPOSSIBLE, 1) == FALSE) { ip_unlock(port); + } } else #endif /* IMPORTANCE_INHERITANCE */ - ip_unlock(port); + ip_unlock(port); break; } /* entry may have changed to dead-name by ipc_right_check() */ - } /* treat send_possible requests as immediate w.r.t. dead-name */ @@ -396,15 +397,16 @@ ipc_right_request_alloc( assert(urefs > 0); /* leave urefs pegged to maximum if it overflowed */ - if (urefs < MACH_PORT_UREFS_MAX) + if (urefs < MACH_PORT_UREFS_MAX) { (entry->ie_bits)++; /* increment urefs */ - + } ipc_entry_modified(space, name, entry); is_write_unlock(space); - if (port != IP_NULL) + if (port != IP_NULL) { ip_release(port); + } ipc_notify_dead_name(notify, name); previous = IP_NULL; @@ -416,8 +418,9 @@ ipc_right_request_alloc( is_write_unlock(space); - if (port != IP_NULL) + if (port != IP_NULL) { ip_release(port); + } return kr; } @@ -438,18 +441,19 @@ ipc_right_request_alloc( ipc_port_t ipc_right_request_cancel( - __unused ipc_space_t space, - ipc_port_t port, - mach_port_name_t name, - ipc_entry_t entry) + __unused ipc_space_t space, + ipc_port_t port, + mach_port_name_t name, + ipc_entry_t entry) { ipc_port_t previous; - assert(ip_active(port)); - assert(port == (ipc_port_t) entry->ie_object); + require_ip_active(port); + assert(port == ip_object_to_port(entry->ie_object)); - if (entry->ie_request == IE_REQ_NONE) + if (entry->ie_request == IE_REQ_NONE) { return IP_NULL; + } previous = ipc_port_request_cancel(port, name, entry->ie_request); entry->ie_request = IE_REQ_NONE; @@ -469,9 +473,9 @@ ipc_right_request_cancel( boolean_t ipc_right_inuse( - ipc_space_t space, - __unused mach_port_name_t name, - ipc_entry_t entry) + ipc_space_t space, + __unused mach_port_name_t name, + ipc_entry_t entry) { if (IE_BITS_TYPE(entry->ie_bits) != MACH_PORT_TYPE_NONE) { is_write_unlock(space); @@ -484,10 +488,12 @@ ipc_right_inuse( * Routine: ipc_right_check * Purpose: * Check if the port has died. If it has, + * and IPC_RIGHT_COPYIN_FLAGS_ALLOW_DEAD_SEND_ONCE is not + * passed and it is not a send once right then * clean up the entry and return TRUE. * Conditions: * The space is write-locked; the port is not locked. - * If returns FALSE, the port is also locked and active. + * If returns FALSE, the port is also locked. * Otherwise, entry is converted to a dead name. * * Caller is responsible for a reference to port if it @@ -496,19 +502,24 @@ ipc_right_inuse( boolean_t ipc_right_check( - ipc_space_t space, - ipc_port_t port, - mach_port_name_t name, - ipc_entry_t entry) + ipc_space_t space, + ipc_port_t port, + mach_port_name_t name, + ipc_entry_t entry, + ipc_right_copyin_flags_t flags) { ipc_entry_bits_t bits; assert(is_active(space)); - assert(port == (ipc_port_t) entry->ie_object); + assert(port == ip_object_to_port(entry->ie_object)); ip_lock(port); - if (ip_active(port)) + if (ip_active(port) || + ((flags & IPC_RIGHT_COPYIN_FLAGS_ALLOW_DEAD_SEND_ONCE) && + entry->ie_request == IE_REQ_NONE && + (entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE))) { return FALSE; + } /* this was either a pure send right or a send-once right */ @@ -517,16 +528,16 @@ ipc_right_check( assert(IE_BITS_UREFS(bits) > 0); if (bits & MACH_PORT_TYPE_SEND) { - assert(IE_BITS_TYPE(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); + } 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); /* @@ -534,12 +545,12 @@ ipc_right_check( */ if ((bits & MACH_PORT_TYPE_SEND) != 0) { - ipc_hash_delete(space, (ipc_object_t)port, name, entry); + ipc_hash_delete(space, ip_to_object(port), name, entry); } /* convert entry to dead name */ - bits = (bits &~ IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME; - + bits = (bits & ~IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME; + /* * If there was a notification request outstanding on this * name, and the port went dead, that notification @@ -559,10 +570,11 @@ ipc_right_check( if (entry->ie_request != IE_REQ_NONE) { if (ipc_port_request_type(port, name, entry->ie_request) != 0) { /* if urefs are pegged due to overflow, leave them pegged */ - if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { bits++; /* increment urefs */ + } } - entry->ie_request = IE_REQ_NONE; + entry->ie_request = IE_REQ_NONE; } entry->ie_bits = bits; entry->ie_object = IO_NULL; @@ -582,9 +594,9 @@ ipc_right_check( void ipc_right_terminate( - ipc_space_t space, - mach_port_name_t name, - ipc_entry_t entry) + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry) { ipc_entry_bits_t bits; mach_port_type_t type; @@ -604,28 +616,28 @@ ipc_right_terminate( */ switch (type) { - case MACH_PORT_TYPE_DEAD_NAME: + case MACH_PORT_TYPE_DEAD_NAME: assert(entry->ie_request == IE_REQ_NONE); assert(entry->ie_object == IO_NULL); break; - case MACH_PORT_TYPE_PORT_SET: { - ipc_pset_t pset = (ipc_pset_t) entry->ie_object; + case MACH_PORT_TYPE_PORT_SET: { + ipc_pset_t pset = ips_object_to_pset(entry->ie_object); assert(entry->ie_request == IE_REQ_NONE); assert(pset != IPS_NULL); ips_lock(pset); assert(ips_active(pset)); - ipc_pset_destroy(pset); /* consumes ref, unlocks */ + ipc_pset_destroy(space, pset); /* consumes ref, unlocks */ break; - } + } - case MACH_PORT_TYPE_SEND: - case MACH_PORT_TYPE_RECEIVE: - case MACH_PORT_TYPE_SEND_RECEIVE: - case MACH_PORT_TYPE_SEND_ONCE: { - ipc_port_t port = (ipc_port_t) entry->ie_object; + case MACH_PORT_TYPE_SEND: + case MACH_PORT_TYPE_RECEIVE: + case MACH_PORT_TYPE_SEND_RECEIVE: + case MACH_PORT_TYPE_SEND_ONCE: { + ipc_port_t port = ip_object_to_port(entry->ie_object); ipc_port_t request; ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; @@ -639,8 +651,8 @@ ipc_right_terminate( break; } - request = ipc_right_request_cancel_macro(space, port, - name, entry); + request = ipc_right_request_cancel_macro(space, port, + name, entry); if (type & MACH_PORT_TYPE_SEND) { assert(port->ip_srights > 0); @@ -659,9 +671,9 @@ ipc_right_terminate( assert(port->ip_receiver == space); ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */ - } else if (type & MACH_PORT_TYPE_SEND_ONCE) { assert(port->ip_sorights > 0); + port->ip_reply_context = 0; ip_unlock(port); ipc_notify_send_once(port); /* consumes our ref */ @@ -669,18 +681,20 @@ ipc_right_terminate( assert(port->ip_receiver != space); ip_unlock(port); - ip_release(port); + ip_release(port); } - if (nsrequest != IP_NULL) + if (nsrequest != IP_NULL) { ipc_notify_no_senders(nsrequest, mscount); + } - if (request != IP_NULL) + if (request != IP_NULL) { ipc_notify_port_deleted(request, name); + } break; - } + } - default: + default: panic("ipc_right_terminate: strange type - 0x%x", type); } } @@ -698,11 +712,11 @@ ipc_right_terminate( kern_return_t ipc_right_destroy( - ipc_space_t space, - mach_port_name_t name, - ipc_entry_t entry, - boolean_t check_guard, - uint64_t guard) + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + boolean_t check_guard, + uint64_t guard) { ipc_entry_bits_t bits; mach_port_type_t type; @@ -714,7 +728,7 @@ ipc_right_destroy( assert(is_active(space)); switch (type) { - case MACH_PORT_TYPE_DEAD_NAME: + case MACH_PORT_TYPE_DEAD_NAME: assert(entry->ie_request == IE_REQ_NONE); assert(entry->ie_object == IO_NULL); @@ -722,8 +736,8 @@ ipc_right_destroy( is_write_unlock(space); break; - case MACH_PORT_TYPE_PORT_SET: { - ipc_pset_t pset = (ipc_pset_t) entry->ie_object; + case MACH_PORT_TYPE_PORT_SET: { + ipc_pset_t pset = ips_object_to_pset(entry->ie_object); assert(entry->ie_request == IE_REQ_NONE); assert(pset != IPS_NULL); @@ -735,24 +749,24 @@ ipc_right_destroy( is_write_unlock(space); assert(ips_active(pset)); - ipc_pset_destroy(pset); /* consumes ref, unlocks */ + ipc_pset_destroy(space, pset); /* consumes ref, unlocks */ break; - } + } - case MACH_PORT_TYPE_SEND: - case MACH_PORT_TYPE_RECEIVE: - case MACH_PORT_TYPE_SEND_RECEIVE: - case MACH_PORT_TYPE_SEND_ONCE: { - ipc_port_t port = (ipc_port_t) entry->ie_object; + case MACH_PORT_TYPE_SEND: + case MACH_PORT_TYPE_RECEIVE: + case MACH_PORT_TYPE_SEND_RECEIVE: + case MACH_PORT_TYPE_SEND_ONCE: { + ipc_port_t port = ip_object_to_port(entry->ie_object); ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; ipc_port_t request; assert(port != IP_NULL); - if (type == MACH_PORT_TYPE_SEND) - ipc_hash_delete(space, (ipc_object_t) port, - name, entry); + if (type == MACH_PORT_TYPE_SEND) { + ipc_hash_delete(space, ip_to_object(port), name, entry); + } ip_lock(port); @@ -777,7 +791,7 @@ ipc_right_destroy( is_write_unlock(space); /* Raise mach port guard exception */ mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_DESTROY); - return KERN_INVALID_RIGHT; + return KERN_INVALID_RIGHT; } @@ -799,13 +813,13 @@ ipc_right_destroy( } if (type & MACH_PORT_TYPE_RECEIVE) { - assert(ip_active(port)); + require_ip_active(port); assert(port->ip_receiver == space); ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */ - } else if (type & MACH_PORT_TYPE_SEND_ONCE) { assert(port->ip_sorights > 0); + port->ip_reply_context = 0; ip_unlock(port); ipc_notify_send_once(port); /* consumes our ref */ @@ -816,17 +830,19 @@ ipc_right_destroy( ip_release(port); } - if (nsrequest != IP_NULL) + if (nsrequest != IP_NULL) { ipc_notify_no_senders(nsrequest, mscount); + } - if (request != IP_NULL) + if (request != IP_NULL) { ipc_notify_port_deleted(request, name); + } break; - } + } - default: + default: panic("ipc_right_destroy: strange type"); } @@ -836,7 +852,7 @@ ipc_right_destroy( /* * Routine: ipc_right_dealloc * Purpose: - * Releases a send/send-once/dead-name user ref. + * Releases a send/send-once/dead-name/port_set user ref. * Like ipc_right_delta with a delta of -1, * but looks at the entry to determine the right. * Conditions: @@ -849,9 +865,9 @@ ipc_right_destroy( kern_return_t ipc_right_dealloc( - ipc_space_t space, - mach_port_name_t name, - ipc_entry_t entry) + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry) { ipc_port_t port = IP_NULL; ipc_entry_bits_t bits; @@ -864,8 +880,28 @@ ipc_right_dealloc( assert(is_active(space)); switch (type) { - case MACH_PORT_TYPE_DEAD_NAME: { - dead_name: + case MACH_PORT_TYPE_PORT_SET: { + ipc_pset_t pset; + + assert(IE_BITS_UREFS(bits) == 0); + assert(entry->ie_request == IE_REQ_NONE); + + pset = ips_object_to_pset(entry->ie_object); + assert(pset != IPS_NULL); + + entry->ie_object = IO_NULL; + ipc_entry_dealloc(space, name, entry); + + ips_lock(pset); + assert(ips_active(pset)); + is_write_unlock(space); + + ipc_pset_destroy(space, pset); /* consumes ref, unlocks */ + break; + } + + case MACH_PORT_TYPE_DEAD_NAME: { +dead_name: assert(IE_BITS_UREFS(bits) > 0); assert(entry->ie_request == IE_REQ_NONE); @@ -875,28 +911,29 @@ ipc_right_dealloc( ipc_entry_dealloc(space, name, entry); } else { /* if urefs are pegged due to overflow, leave them pegged */ - if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) - entry->ie_bits = bits-1; /* decrement urefs */ + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { + entry->ie_bits = bits - 1; /* decrement urefs */ + } ipc_entry_modified(space, name, entry); } is_write_unlock(space); /* release any port that got converted to dead name below */ - if (port != IP_NULL) + if (port != IP_NULL) { ip_release(port); + } break; - } + } - case MACH_PORT_TYPE_SEND_ONCE: { + case MACH_PORT_TYPE_SEND_ONCE: { ipc_port_t request; assert(IE_BITS_UREFS(bits) == 1); - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); - if (ipc_right_check(space, port, name, entry)) { - + if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) { bits = entry->ie_bits; assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); goto dead_name; /* it will release port */ @@ -905,6 +942,13 @@ ipc_right_dealloc( assert(port->ip_sorights > 0); + /* + * clear any reply context: + * no one will be sending the response b/c we are destroying + * the single, outstanding send once right. + */ + port->ip_reply_context = 0; + request = ipc_right_request_cancel_macro(space, port, name, entry); ip_unlock(port); @@ -915,12 +959,13 @@ ipc_right_dealloc( ipc_notify_send_once(port); - if (request != IP_NULL) + if (request != IP_NULL) { ipc_notify_port_deleted(request, name); + } break; - } + } - case MACH_PORT_TYPE_SEND: { + case MACH_PORT_TYPE_SEND: { ipc_port_t request = IP_NULL; ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; @@ -928,10 +973,10 @@ ipc_right_dealloc( assert(IE_BITS_UREFS(bits) > 0); - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); - if (ipc_right_check(space, port, name, entry)) { + if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) { bits = entry->ie_bits; assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); goto dead_name; /* it will release port */ @@ -950,44 +995,45 @@ ipc_right_dealloc( } request = ipc_right_request_cancel_macro(space, port, - name, entry); - ipc_hash_delete(space, (ipc_object_t) port, - name, entry); + name, entry); + ipc_hash_delete(space, ip_to_object(port), name, entry); ip_unlock(port); entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); is_write_unlock(space); ip_release(port); - } else { ip_unlock(port); /* if urefs are pegged due to overflow, leave them pegged */ - if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) - entry->ie_bits = bits-1; /* decrement urefs */ + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { + entry->ie_bits = bits - 1; /* decrement urefs */ + } ipc_entry_modified(space, name, entry); is_write_unlock(space); } - if (nsrequest != IP_NULL) + if (nsrequest != IP_NULL) { ipc_notify_no_senders(nsrequest, mscount); + } - if (request != IP_NULL) + if (request != IP_NULL) { ipc_notify_port_deleted(request, name); + } break; - } + } - case MACH_PORT_TYPE_SEND_RECEIVE: { + case MACH_PORT_TYPE_SEND_RECEIVE: { ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; assert(IE_BITS_UREFS(bits) > 0); - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); ip_lock(port); - assert(ip_active(port)); + require_ip_active(port); assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); assert(port->ip_srights > 0); @@ -1001,12 +1047,12 @@ ipc_right_dealloc( } } - entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK | - MACH_PORT_TYPE_SEND); + entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK | + MACH_PORT_TYPE_SEND); } else { /* if urefs are pegged due to overflow, leave them pegged */ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { - entry->ie_bits = bits-1; /* decrement urefs */ + entry->ie_bits = bits - 1; /* decrement urefs */ } } ip_unlock(port); @@ -1014,13 +1060,15 @@ ipc_right_dealloc( ipc_entry_modified(space, name, entry); is_write_unlock(space); - if (nsrequest != IP_NULL) + if (nsrequest != IP_NULL) { ipc_notify_no_senders(nsrequest, mscount); + } break; - } + } - default: + default: is_write_unlock(space); + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT); return KERN_INVALID_RIGHT; } @@ -1043,18 +1091,17 @@ ipc_right_dealloc( kern_return_t ipc_right_delta( - ipc_space_t space, - mach_port_name_t name, - ipc_entry_t entry, - mach_port_right_t right, - mach_port_delta_t delta) + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_port_right_t right, + mach_port_delta_t delta) { ipc_port_t port = IP_NULL; ipc_entry_bits_t bits; bits = entry->ie_bits; - /* * The following is used (for case MACH_PORT_RIGHT_DEAD_NAME) in the * switch below. It is used to keep track of those cases (in DIPC) @@ -1069,23 +1116,27 @@ ipc_right_delta( /* Rights-specific restrictions and operations. */ switch (right) { - case MACH_PORT_RIGHT_PORT_SET: { + case MACH_PORT_RIGHT_PORT_SET: { ipc_pset_t pset; - if ((bits & MACH_PORT_TYPE_PORT_SET) == 0) + if ((bits & MACH_PORT_TYPE_PORT_SET) == 0) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT); goto invalid_right; + } assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_PORT_SET); assert(IE_BITS_UREFS(bits) == 0); assert(entry->ie_request == IE_REQ_NONE); - if (delta == 0) + if (delta == 0) { goto success; + } - if (delta != -1) + if (delta != -1) { goto invalid_value; + } - pset = (ipc_pset_t) entry->ie_object; + pset = ips_object_to_pset(entry->ie_object); assert(pset != IPS_NULL); entry->ie_object = IO_NULL; @@ -1095,23 +1146,29 @@ ipc_right_delta( assert(ips_active(pset)); is_write_unlock(space); - ipc_pset_destroy(pset); /* consumes ref, unlocks */ + ipc_pset_destroy(space, pset); /* consumes ref, unlocks */ break; - } + } - case MACH_PORT_RIGHT_RECEIVE: { + case MACH_PORT_RIGHT_RECEIVE: { ipc_port_t request = IP_NULL; - if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) { + if ((bits & MACH_PORT_TYPE_EX_RECEIVE) == 0) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT); + } goto invalid_right; + } - if (delta == 0) + if (delta == 0) { goto success; + } - if (delta != -1) + if (delta != -1) { goto invalid_value; + } - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); /* @@ -1121,12 +1178,12 @@ ipc_right_delta( */ ip_lock(port); - assert(ip_active(port)); + require_ip_active(port); assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); - + /* Mach Port Guard Checking */ - if(port->ip_guarded) { + if (port->ip_guarded) { uint64_t portguard = port->ip_context; ip_unlock(port); is_write_unlock(space); @@ -1134,10 +1191,10 @@ ipc_right_delta( 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); + MACH_PORT_TYPE_SEND_RECEIVE); assert(IE_BITS_UREFS(bits) > 0); assert(port->ip_srights > 0); @@ -1154,7 +1211,8 @@ ipc_right_delta( */ ipc_entry_modified(space, name, entry); entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE; - ipc_hash_insert(space, (ipc_object_t) port, + entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE; + ipc_hash_insert(space, ip_to_object(port), name, entry); ip_reference(port); } else { @@ -1166,12 +1224,13 @@ ipc_right_delta( * port is destroyed "first". */ bits &= ~IE_BITS_TYPE_MASK; - bits |= MACH_PORT_TYPE_DEAD_NAME; + bits |= (MACH_PORT_TYPE_DEAD_NAME | MACH_PORT_TYPE_EX_RECEIVE); if (entry->ie_request) { entry->ie_request = IE_REQ_NONE; /* if urefs are pegged due to overflow, leave them pegged */ - if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { bits++; /* increment urefs */ + } } entry->ie_bits = bits; entry->ie_object = IO_NULL; @@ -1182,33 +1241,36 @@ ipc_right_delta( assert(IE_BITS_UREFS(bits) == 0); request = ipc_right_request_cancel_macro(space, port, - name, entry); + name, entry); entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); } is_write_unlock(space); - ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */ + ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */ - if (request != IP_NULL) + if (request != IP_NULL) { ipc_notify_port_deleted(request, name); + } break; - } + } - case MACH_PORT_RIGHT_SEND_ONCE: { + case MACH_PORT_RIGHT_SEND_ONCE: { ipc_port_t request; - if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) + if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) { goto invalid_right; + } assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); assert(IE_BITS_UREFS(bits) == 1); - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); - if (ipc_right_check(space, port, name, entry)) { + if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) { assert(!(entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE)); + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT); goto invalid_right; } /* port is locked and active */ @@ -1225,6 +1287,13 @@ ipc_right_delta( goto success; } + /* + * clear any reply context: + * no one will be sending the response b/c we are destroying + * the single, outstanding send once right. + */ + port->ip_reply_context = 0; + request = ipc_right_request_cancel_macro(space, port, name, entry); ip_unlock(port); @@ -1235,30 +1304,32 @@ ipc_right_delta( ipc_notify_send_once(port); - if (request != IP_NULL) + if (request != IP_NULL) { ipc_notify_port_deleted(request, name); + } break; - } + } - case MACH_PORT_RIGHT_DEAD_NAME: { + case MACH_PORT_RIGHT_DEAD_NAME: { ipc_port_t relport = IP_NULL; mach_port_urefs_t urefs; if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { - - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); - if (!ipc_right_check(space, port, name, entry)) { + if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) { /* port is locked and active */ ip_unlock(port); port = IP_NULL; + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT); goto invalid_right; } bits = entry->ie_bits; relport = port; port = IP_NULL; } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT); goto invalid_right; } @@ -1267,7 +1338,7 @@ ipc_right_delta( assert(entry->ie_object == IO_NULL); assert(entry->ie_request == IE_REQ_NONE); - if (delta > ((mach_port_delta_t)MACH_PORT_UREFS_MAX) || + if (delta > ((mach_port_delta_t)MACH_PORT_UREFS_MAX) || delta < (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) { goto invalid_value; } @@ -1280,11 +1351,13 @@ ipc_right_delta( * only a delta removing all refs at once can change it */ - if (delta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) + if (delta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) { delta = 0; + } } else { - if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) + if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) { goto invalid_value; + } if (MACH_PORT_UREFS_OVERFLOW(urefs, delta)) { /* leave urefs pegged to maximum if it overflowed */ delta = MACH_PORT_UREFS_MAX - urefs; @@ -1300,27 +1373,42 @@ ipc_right_delta( is_write_unlock(space); - if (relport != IP_NULL) + if (relport != IP_NULL) { ip_release(relport); + } break; - } + } - case MACH_PORT_RIGHT_SEND: { + case MACH_PORT_RIGHT_SEND: { mach_port_urefs_t urefs; ipc_port_t request = IP_NULL; ipc_port_t nsrequest = IP_NULL; + ipc_port_t port_to_release = IP_NULL; mach_port_mscount_t mscount = 0; - if ((bits & MACH_PORT_TYPE_SEND) == 0) + if ((bits & MACH_PORT_TYPE_SEND) == 0) { + /* invalid right exception only when not live/dead confusion */ + if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0 +#if !defined(AE_MAKESENDRIGHT_FIXED) + /* + * AE tries to add single send right without knowing if it already owns one. + * But if it doesn't, it should own the receive right and delta should be 1. + */ + && (((bits & MACH_PORT_TYPE_RECEIVE) == 0) || (delta != 1)) +#endif + ) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT); + } goto invalid_right; + } /* maximum urefs for send is MACH_PORT_UREFS_MAX */ - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); - if (ipc_right_check(space, port, name, entry)) { + if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) { assert((entry->ie_bits & MACH_PORT_TYPE_SEND) == 0); goto invalid_right; } @@ -1328,7 +1416,7 @@ ipc_right_delta( assert(port->ip_srights > 0); - if (delta > ((mach_port_delta_t)MACH_PORT_UREFS_MAX) || + if (delta > ((mach_port_delta_t)MACH_PORT_UREFS_MAX) || delta < (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) { ip_unlock(port); goto invalid_value; @@ -1342,8 +1430,9 @@ ipc_right_delta( * only a delta removing all refs at once can change it */ - if (delta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) + if (delta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) { delta = 0; + } } else { if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) { ip_unlock(port); @@ -1369,22 +1458,22 @@ ipc_right_delta( assert(port->ip_receiver == space); ip_unlock(port); assert(IE_BITS_TYPE(bits) == - MACH_PORT_TYPE_SEND_RECEIVE); + MACH_PORT_TYPE_SEND_RECEIVE); - entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK| - MACH_PORT_TYPE_SEND); + 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); + MACH_PORT_TYPE_SEND); request = ipc_right_request_cancel_macro(space, port, - name, entry); - ipc_hash_delete(space, (ipc_object_t) port, - name, entry); + name, entry); + ipc_hash_delete(space, ip_to_object(port), + name, entry); ip_unlock(port); - ip_release(port); + port_to_release = port; entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); @@ -1399,42 +1488,54 @@ ipc_right_delta( is_write_unlock(space); - if (nsrequest != IP_NULL) + if (port_to_release != IP_NULL) { + ip_release(port_to_release); + } + + if (nsrequest != IP_NULL) { ipc_notify_no_senders(nsrequest, mscount); + } - if (request != IP_NULL) + if (request != IP_NULL) { ipc_notify_port_deleted(request, name); + } break; - } + } + + case MACH_PORT_RIGHT_LABELH: + goto invalid_right; - default: - panic("ipc_right_delta: strange right"); + default: + panic("ipc_right_delta: strange right %d for 0x%x (%p) in space:%p", + right, name, (void *)entry, (void *)space); } return KERN_SUCCESS; - success: +success: is_write_unlock(space); return KERN_SUCCESS; - invalid_right: +invalid_right: is_write_unlock(space); - if (port != IP_NULL) + if (port != IP_NULL) { ip_release(port); + } return KERN_INVALID_RIGHT; - invalid_value: +invalid_value: is_write_unlock(space); + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_VALUE); return KERN_INVALID_VALUE; - guard_failure: +guard_failure: return KERN_INVALID_RIGHT; } /* * Routine: ipc_right_destruct * Purpose: - * Deallocates the receive right and modifies the + * 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. @@ -1447,11 +1548,11 @@ ipc_right_delta( 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_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; @@ -1462,28 +1563,39 @@ ipc_right_destruct( 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))) { + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) { + is_write_unlock(space); + + /* No exception if we used to have receive and held entry since */ + if ((bits & MACH_PORT_TYPE_EX_RECEIVE) == 0) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT); + } + return KERN_INVALID_RIGHT; + } + + if (srdelta && (bits & MACH_PORT_TYPE_SEND) == 0) { is_write_unlock(space); + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT); return KERN_INVALID_RIGHT; } - if (srdelta > 0) + if (srdelta > 0) { goto invalid_value; + } - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); ip_lock(port); - assert(ip_active(port)); + require_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)) { + if (port->ip_guarded && (guard != port->ip_context)) { uint64_t portguard = port->ip_context; ip_unlock(port); is_write_unlock(space); @@ -1499,7 +1611,6 @@ ipc_right_destruct( */ if (srdelta) { - assert(port->ip_srights > 0); urefs = IE_BITS_UREFS(bits); @@ -1518,8 +1629,9 @@ ipc_right_destruct( * urefs are pegged due to an overflow * only a delta removing all refs at once can change it */ - if (srdelta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) + if (srdelta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) { srdelta = 0; + } } if ((urefs + srdelta) == 0) { @@ -1531,8 +1643,8 @@ ipc_right_destruct( } } assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_RECEIVE); - entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK| - MACH_PORT_TYPE_SEND); + entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK | + MACH_PORT_TYPE_SEND); } else { entry->ie_bits = bits + srdelta; } @@ -1561,7 +1673,8 @@ ipc_right_destruct( */ ipc_entry_modified(space, name, entry); entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE; - ipc_hash_insert(space, (ipc_object_t) port, + entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE; + ipc_hash_insert(space, ip_to_object(port), name, entry); ip_reference(port); } else { @@ -1573,11 +1686,12 @@ ipc_right_destruct( * port is destroyed "first". */ bits &= ~IE_BITS_TYPE_MASK; - bits |= MACH_PORT_TYPE_DEAD_NAME; + bits |= (MACH_PORT_TYPE_DEAD_NAME | MACH_PORT_TYPE_EX_RECEIVE); if (entry->ie_request) { entry->ie_request = IE_REQ_NONE; - if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { bits++; /* increment urefs */ + } } entry->ie_bits = bits; entry->ie_object = IO_NULL; @@ -1587,7 +1701,7 @@ ipc_right_destruct( 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); + name, entry); entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); } @@ -1595,20 +1709,22 @@ ipc_right_destruct( /* Unlock space */ is_write_unlock(space); - if (nsrequest != IP_NULL) + if (nsrequest != IP_NULL) { ipc_notify_no_senders(nsrequest, mscount); + } - ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */ + ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */ - if (request != IP_NULL) + if (request != IP_NULL) { ipc_notify_port_deleted(request, name); - + } + return KERN_SUCCESS; - - invalid_value: + +invalid_value: is_write_unlock(space); + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_VALUE); return KERN_INVALID_VALUE; - } @@ -1625,11 +1741,11 @@ ipc_right_destruct( kern_return_t ipc_right_info( - ipc_space_t space, - mach_port_name_t name, - ipc_entry_t entry, - mach_port_type_t *typep, - mach_port_urefs_t *urefsp) + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_port_type_t *typep, + mach_port_urefs_t *urefsp) { ipc_port_t port; ipc_entry_bits_t bits; @@ -1638,28 +1754,28 @@ ipc_right_info( bits = entry->ie_bits; request = entry->ie_request; - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); if (bits & MACH_PORT_TYPE_RECEIVE) { assert(IP_VALID(port)); if (request != IE_REQ_NONE) { ip_lock(port); - assert(ip_active(port)); + require_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) + if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) { + if (request != IE_REQ_NONE) { type |= ipc_port_request_type(port, name, request); + } ip_unlock(port); is_write_unlock(space); } else { @@ -1680,82 +1796,132 @@ ipc_right_info( } /* - * Routine: ipc_right_copyin_check + * Routine: ipc_right_copyin_check_reply * Purpose: - * Check if a subsequent ipc_right_copyin would succeed. + * Check if a subsequent ipc_right_copyin would succeed. Used only + * by ipc_kmsg_copyin_header to check if reply_port can be copied in. + * If the reply port is an immovable send right, it errors out. * Conditions: * The space is locked (read or write) and active. */ boolean_t -ipc_right_copyin_check( - __assert_only ipc_space_t space, - __unused mach_port_name_t name, - ipc_entry_t entry, - mach_msg_type_name_t msgt_name) +ipc_right_copyin_check_reply( + __assert_only ipc_space_t space, + mach_port_name_t reply_name, + ipc_entry_t reply_entry, + mach_msg_type_name_t reply_type) { ipc_entry_bits_t bits; - ipc_port_t port; + ipc_port_t reply_port; - bits= entry->ie_bits; + bits = reply_entry->ie_bits; assert(is_active(space)); - switch (msgt_name) { - case MACH_MSG_TYPE_MAKE_SEND: - if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) - return FALSE; - break; - - case MACH_MSG_TYPE_MAKE_SEND_ONCE: - if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + switch (reply_type) { + case MACH_MSG_TYPE_MAKE_SEND: + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) { return FALSE; + } break; - case MACH_MSG_TYPE_MOVE_RECEIVE: - if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + case MACH_MSG_TYPE_MAKE_SEND_ONCE: + 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: { + case MACH_MSG_TYPE_MOVE_RECEIVE: + /* ipc_kmsg_copyin_header already filters it out */ + return FALSE; - if (bits & MACH_PORT_TYPE_DEAD_NAME) + case MACH_MSG_TYPE_COPY_SEND: + case MACH_MSG_TYPE_MOVE_SEND: + case MACH_MSG_TYPE_MOVE_SEND_ONCE: { + if (bits & MACH_PORT_TYPE_DEAD_NAME) { break; + } - if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) + if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) { return FALSE; + } - port = (ipc_port_t) entry->ie_object; - assert(port != IP_NULL); + reply_port = ip_object_to_port(reply_entry->ie_object); + assert(reply_port != IP_NULL); /* * 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)) { + if (!ip_active(reply_port)) { break; } - if (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE) { - if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) + /* + * Can't copyin a send right that is marked immovable. This bit + * is set only during port creation and never unset. So it can + * be read without a lock. + */ + if (reply_port->ip_immovable_send) { + mach_port_guard_exception(reply_name, 0, 0, kGUARD_EXC_IMMOVABLE); + return FALSE; + } + + if (reply_type == MACH_MSG_TYPE_MOVE_SEND_ONCE) { + if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) { return FALSE; + } } else { - if ((bits & MACH_PORT_TYPE_SEND) == 0) + if ((bits & MACH_PORT_TYPE_SEND) == 0) { return FALSE; + } } break; - } + } - default: + default: panic("ipc_right_copyin_check: strange rights"); } return TRUE; } +/* + * Routine: ipc_right_copyin_check_guard_locked + * Purpose: + * Check if the port is guarded and the guard + * value matches the one passed in the arguments. + * If MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND is set, + * check if the port is unguarded. + * Conditions: + * The port is locked. + * Returns: + * KERN_SUCCESS Port is either unguarded + * or guarded with expected value + * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch. + * This also raises a EXC_GUARD exception. + */ +static kern_return_t +ipc_right_copyin_check_guard_locked( + mach_port_name_t name, + ipc_port_t port, + mach_port_context_t context, + mach_msg_guard_flags_t *guard_flags) +{ + mach_msg_guard_flags_t flags = *guard_flags; + if ((flags & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) && !port->ip_guarded && !context) { + return KERN_SUCCESS; + } else if (port->ip_guarded && (port->ip_context == context)) { + return KERN_SUCCESS; + } + + /* Incorrect guard; Raise exception */ + mach_port_guard_exception(name, context, port->ip_context, kGUARD_EXC_INCORRECT_GUARD); + return KERN_INVALID_ARGUMENT; +} + /* * Routine: ipc_right_copyin * Purpose: @@ -1777,22 +1943,29 @@ ipc_right_copyin_check( * Returns: * KERN_SUCCESS Acquired an object, possibly IO_DEAD. * KERN_INVALID_RIGHT Name doesn't denote correct right. + * KERN_INVALID_CAPABILITY Trying to move an kobject port or an immovable right + * KERN_INVALID_ARGUMENT Port is unguarded or guard mismatch */ kern_return_t ipc_right_copyin( - ipc_space_t space, - mach_port_name_t name, - ipc_entry_t entry, - mach_msg_type_name_t msgt_name, - boolean_t deadok, - ipc_object_t *objectp, - ipc_port_t *sorightp, - ipc_port_t *releasep, - int *assertcntp) + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_msg_type_name_t msgt_name, + ipc_right_copyin_flags_t flags, + ipc_object_t *objectp, + ipc_port_t *sorightp, + ipc_port_t *releasep, + int *assertcntp, + mach_port_context_t context, + mach_msg_guard_flags_t *guard_flags) { ipc_entry_bits_t bits; ipc_port_t port; + kern_return_t kr; + boolean_t deadok = flags & IPC_RIGHT_COPYIN_FLAGS_DEADOK? TRUE : FALSE; + boolean_t allow_imm_send = flags & IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND? TRUE : FALSE; *releasep = IP_NULL; *assertcntp = 0; @@ -1802,88 +1975,123 @@ ipc_right_copyin( assert(is_active(space)); switch (msgt_name) { - case MACH_MSG_TYPE_MAKE_SEND: { - - if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + case MACH_MSG_TYPE_MAKE_SEND: { + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) { goto invalid_right; + } - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); ip_lock(port); - assert(ip_active(port)); assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); - port->ip_mscount++; - port->ip_srights++; - ip_reference(port); + ipc_port_make_send_locked(port); ip_unlock(port); - *objectp = (ipc_object_t) port; + *objectp = ip_to_object(port); *sorightp = IP_NULL; break; - } - - case MACH_MSG_TYPE_MAKE_SEND_ONCE: { + } - if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + case MACH_MSG_TYPE_MAKE_SEND_ONCE: { + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) { goto invalid_right; + } - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); ip_lock(port); - assert(ip_active(port)); + require_ip_active(port); assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); - port->ip_sorights++; - ip_reference(port); + ipc_port_make_sonce_locked(port); ip_unlock(port); - *objectp = (ipc_object_t) port; + *objectp = ip_to_object(port); *sorightp = IP_NULL; break; - } + } - case MACH_MSG_TYPE_MOVE_RECEIVE: { + case MACH_MSG_TYPE_MOVE_RECEIVE: { ipc_port_t request = IP_NULL; - if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) { goto invalid_right; + } - port = (ipc_port_t) entry->ie_object; + /* + * Disallow moving receive-right kobjects, e.g. mk_timer ports + * The ipc_port structure uses the kdata union of kobject and + * imp_task exclusively. Thus, general use of a kobject port as + * a receive right can cause type confusion in the importance + * code. + */ + if (io_kotype(entry->ie_object) != IKOT_NONE) { + /* + * Distinguish an invalid right, e.g., trying to move + * a send right as a receive right, from this + * situation which is, "This is a valid receive right, + * but it's also a kobject and you can't move it." + */ + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE); + return KERN_INVALID_CAPABILITY; + } + + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); ip_lock(port); - assert(ip_active(port)); + require_ip_active(port); assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); + if (port->ip_immovable_receive) { + assert(port->ip_receiver != ipc_space_kernel); + ip_unlock(port); + assert(current_task() != kernel_task); + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE); + return KERN_INVALID_CAPABILITY; + } + + if (guard_flags != NULL) { + kr = ipc_right_copyin_check_guard_locked(name, port, context, guard_flags); + if (kr != KERN_SUCCESS) { + ip_unlock(port); + return kr; + } + } + if (bits & MACH_PORT_TYPE_SEND) { assert(IE_BITS_TYPE(bits) == - MACH_PORT_TYPE_SEND_RECEIVE); + MACH_PORT_TYPE_SEND_RECEIVE); assert(IE_BITS_UREFS(bits) > 0); assert(port->ip_srights > 0); - ipc_hash_insert(space, (ipc_object_t) port, - name, entry); + ipc_hash_insert(space, ip_to_object(port), + name, entry); ip_reference(port); } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); assert(IE_BITS_UREFS(bits) == 0); request = ipc_right_request_cancel_macro(space, port, - name, entry); + name, entry); entry->ie_object = IO_NULL; } - entry->ie_bits = bits &~ MACH_PORT_TYPE_RECEIVE; + entry->ie_bits = bits & ~MACH_PORT_TYPE_RECEIVE; + entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE; ipc_entry_modified(space, name, entry); + /* ipc_port_clear_receiver unguards the port and clears the ip_immovable_receive bit */ (void)ipc_port_clear_receiver(port, FALSE); /* don't destroy the port/mqueue */ - port->ip_receiver_name = MACH_PORT_NULL; - port->ip_destination = IP_NULL; + if (guard_flags != NULL) { + /* this flag will be cleared during copyout */ + *guard_flags = *guard_flags | MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND; + } #if IMPORTANCE_INHERITANCE /* @@ -1906,27 +2114,28 @@ ipc_right_copyin( ip_unlock(port); - *objectp = (ipc_object_t) port; + *objectp = ip_to_object(port); *sorightp = request; break; - } - - case MACH_MSG_TYPE_COPY_SEND: { + } - if (bits & MACH_PORT_TYPE_DEAD_NAME) + case MACH_MSG_TYPE_COPY_SEND: { + if (bits & MACH_PORT_TYPE_DEAD_NAME) { goto copy_dead; + } /* allow for dead send-once rights */ - if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) + if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) { goto invalid_right; + } assert(IE_BITS_UREFS(bits) > 0); - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); - if (ipc_right_check(space, port, name, entry)) { + if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) { bits = entry->ie_bits; *releasep = port; goto copy_dead; @@ -1941,34 +2150,39 @@ ipc_right_copyin( goto invalid_right; } - assert(port->ip_srights > 0); + if (!allow_imm_send && port->ip_immovable_send) { + ip_unlock(port); + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE); + return KERN_INVALID_CAPABILITY; + } - port->ip_srights++; - ip_reference(port); + ipc_port_copy_send_locked(port); ip_unlock(port); - *objectp = (ipc_object_t) port; + *objectp = ip_to_object(port); *sorightp = IP_NULL; break; - } + } - case MACH_MSG_TYPE_MOVE_SEND: { + case MACH_MSG_TYPE_MOVE_SEND: { ipc_port_t request = IP_NULL; - if (bits & MACH_PORT_TYPE_DEAD_NAME) + if (bits & MACH_PORT_TYPE_DEAD_NAME) { goto move_dead; + } /* allow for dead send-once rights */ - if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) + if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) { goto invalid_right; + } assert(IE_BITS_UREFS(bits) > 0); - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); - if (ipc_right_check(space, port, name, entry)) { + if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) { bits = entry->ie_bits; *releasep = port; goto move_dead; @@ -1983,67 +2197,79 @@ ipc_right_copyin( goto invalid_right; } - assert(port->ip_srights > 0); + if (!allow_imm_send && port->ip_immovable_send) { + ip_unlock(port); + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE); + return KERN_INVALID_CAPABILITY; + } if (IE_BITS_UREFS(bits) == 1) { + assert(port->ip_srights > 0); if (bits & MACH_PORT_TYPE_RECEIVE) { assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); assert(IE_BITS_TYPE(bits) == - MACH_PORT_TYPE_SEND_RECEIVE); + MACH_PORT_TYPE_SEND_RECEIVE); ip_reference(port); } else { assert(IE_BITS_TYPE(bits) == - MACH_PORT_TYPE_SEND); + MACH_PORT_TYPE_SEND); request = ipc_right_request_cancel_macro(space, port, - name, entry); - ipc_hash_delete(space, (ipc_object_t) port, - name, entry); + name, entry); + ipc_hash_delete(space, ip_to_object(port), + name, entry); entry->ie_object = IO_NULL; /* transfer entry's reference to caller */ } - entry->ie_bits = bits &~ - (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND); + entry->ie_bits = bits & ~ + (IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND); } else { - port->ip_srights++; - ip_reference(port); + ipc_port_copy_send_locked(port); /* if urefs are pegged due to overflow, leave them pegged */ - if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) - entry->ie_bits = bits-1; /* decrement urefs */ + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { + entry->ie_bits = bits - 1; /* decrement urefs */ + } } ipc_entry_modified(space, name, entry); ip_unlock(port); - *objectp = (ipc_object_t) port; + *objectp = ip_to_object(port); *sorightp = request; break; - } + } - case MACH_MSG_TYPE_MOVE_SEND_ONCE: { + case MACH_MSG_TYPE_MOVE_SEND_ONCE: { ipc_port_t request; - if (bits & MACH_PORT_TYPE_DEAD_NAME) + if (bits & MACH_PORT_TYPE_DEAD_NAME) { goto move_dead; + } /* allow for dead send rights */ - if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) + if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) { goto invalid_right; + } assert(IE_BITS_UREFS(bits) > 0); - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); - if (ipc_right_check(space, port, name, entry)) { + if (ipc_right_check(space, port, name, entry, flags)) { bits = entry->ie_bits; *releasep = port; goto move_dead; } - /* port is locked and active */ + /* + * port is locked, but may not be active: + * Allow copyin of inactive ports with no dead name request and treat it + * as if the copyin of the port was successful and port became inactive + * later. + */ if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) { assert(bits & MACH_PORT_TYPE_SEND); @@ -2053,6 +2279,12 @@ ipc_right_copyin( goto invalid_right; } + if (!allow_imm_send && port->ip_immovable_send) { + ip_unlock(port); + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE); + return KERN_INVALID_CAPABILITY; + } + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); assert(IE_BITS_UREFS(bits) == 1); assert(port->ip_sorights > 0); @@ -2061,140 +2293,56 @@ ipc_right_copyin( ip_unlock(port); entry->ie_object = IO_NULL; - entry->ie_bits = bits &~ - (IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND_ONCE); + entry->ie_bits = bits & ~ + (IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND_ONCE); ipc_entry_modified(space, name, entry); - *objectp = (ipc_object_t) port; + *objectp = ip_to_object(port); *sorightp = request; break; - } + } - default: - invalid_right: + default: +invalid_right: return KERN_INVALID_RIGHT; } return KERN_SUCCESS; - copy_dead: +copy_dead: assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); assert(IE_BITS_UREFS(bits) > 0); assert(entry->ie_request == IE_REQ_NONE); assert(entry->ie_object == 0); - if (!deadok) + if (!deadok) { goto invalid_right; + } *objectp = IO_DEAD; *sorightp = IP_NULL; return KERN_SUCCESS; - move_dead: +move_dead: assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); assert(IE_BITS_UREFS(bits) > 0); assert(entry->ie_request == IE_REQ_NONE); assert(entry->ie_object == 0); - if (!deadok) + if (!deadok) { goto invalid_right; + } if (IE_BITS_UREFS(bits) == 1) { bits &= ~MACH_PORT_TYPE_DEAD_NAME; } /* if urefs are pegged due to overflow, leave them pegged */ - if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) - entry->ie_bits = bits-1; /* decrement urefs */ - + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { + entry->ie_bits = bits - 1; /* decrement urefs */ + } ipc_entry_modified(space, name, entry); *objectp = IO_DEAD; *sorightp = IP_NULL; return KERN_SUCCESS; - -} - -/* - * Routine: ipc_right_copyin_undo - * Purpose: - * Undoes the effects of an ipc_right_copyin - * of a send/send-once right that is dead. - * (Object is either IO_DEAD or a dead port.) - * Conditions: - * The space is write-locked and active. - */ - -void -ipc_right_copyin_undo( - ipc_space_t space, - mach_port_name_t name, - ipc_entry_t entry, - mach_msg_type_name_t msgt_name, - ipc_object_t object, - ipc_port_t soright) -{ - ipc_entry_bits_t bits; - - bits = entry->ie_bits; - - assert(is_active(space)); - - assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || - (msgt_name == MACH_MSG_TYPE_COPY_SEND) || - (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); - - if (soright != IP_NULL) { - assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || - (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); - assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); - assert(object != IO_DEAD); - - entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) | - MACH_PORT_TYPE_DEAD_NAME | 2); - - } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE) { - assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || - (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); - - entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) | - MACH_PORT_TYPE_DEAD_NAME | 1); - } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME) { - assert(object == IO_DEAD); - assert(IE_BITS_UREFS(bits) > 0); - - if (msgt_name != MACH_MSG_TYPE_COPY_SEND) { - assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX); - /* if urefs are pegged due to overflow, leave them pegged */ - if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) - entry->ie_bits = bits+1; /* increment urefs */ - } - } else { - assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || - (msgt_name == MACH_MSG_TYPE_COPY_SEND)); - assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); - assert(object != IO_DEAD); - assert(entry->ie_object == object); - assert(IE_BITS_UREFS(bits) > 0); - - if (msgt_name != MACH_MSG_TYPE_COPY_SEND) { - assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX); - /* if urefs are pegged due to overflow, leave them pegged */ - if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) - entry->ie_bits = bits+1; /* increment urefs */ - } - - /* - * May as well convert the entry to a dead name. - * (Or if it is a compat entry, destroy it.) - */ - - (void) ipc_right_check(space, (ipc_port_t) object, - 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) - io_release(object); } /* @@ -2213,12 +2361,12 @@ ipc_right_copyin_undo( static kern_return_t ipc_right_copyin_two_move_sends( - ipc_space_t space, - mach_port_name_t name, - ipc_entry_t entry, - ipc_object_t *objectp, - ipc_port_t *sorightp, - ipc_port_t *releasep) + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + ipc_object_t *objectp, + ipc_port_t *sorightp, + ipc_port_t *releasep) { ipc_entry_bits_t bits; mach_port_urefs_t urefs; @@ -2231,64 +2379,79 @@ ipc_right_copyin_two_move_sends( bits = entry->ie_bits; - if ((bits & MACH_PORT_TYPE_SEND) == 0) + if ((bits & MACH_PORT_TYPE_SEND) == 0) { goto invalid_right; + } urefs = IE_BITS_UREFS(bits); - if (urefs < 2) + if (urefs < 2) { goto invalid_right; + } - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); - if (ipc_right_check(space, port, name, entry)) { + if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) { *releasep = port; goto invalid_right; } /* port is locked and active */ - assert(port->ip_srights > 0); + if (urefs > 2) { + /* + * We are moving 2 urefs as naked send rights, which is decomposed as: + * - two copy sends (which doesn't affect the make send count) + * - decrementing the local urefs twice. + */ + ipc_port_copy_send_locked(port); + ipc_port_copy_send_locked(port); + /* if urefs are pegged due to overflow, leave them pegged */ + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { + entry->ie_bits = bits - 2; /* decrement urefs */ + } + } else { + /* + * We have exactly 2 send rights for this port in this space, + * which means that we will liberate the naked send right held + * by this entry. + * + * However refcounting rules around entries are that naked send rights + * on behalf of spaces do not have an associated port reference, + * so we need to donate one ... + */ + ipc_port_copy_send_locked(port); - if (urefs == 2) { if (bits & MACH_PORT_TYPE_RECEIVE) { assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); assert(IE_BITS_TYPE(bits) == - MACH_PORT_TYPE_SEND_RECEIVE); + MACH_PORT_TYPE_SEND_RECEIVE); - port->ip_srights++; - ip_reference(port); + /* ... that we inject manually when the entry stays alive */ ip_reference(port); } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); + /* ... that we steal from the entry when it dies */ request = ipc_right_request_cancel_macro(space, port, - name, entry); + name, entry); - port->ip_srights++; - ip_reference(port); - ipc_hash_delete(space, (ipc_object_t) port, - name, entry); + ipc_hash_delete(space, ip_to_object(port), + name, entry); entry->ie_object = IO_NULL; } - entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND); - } else { - port->ip_srights += 2; - ip_reference(port); - ip_reference(port); - /* if urefs are pegged due to overflow, leave them pegged */ - if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) - entry->ie_bits = bits-2; /* decrement urefs */ + + entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND); } ipc_entry_modified(space, name, entry); ip_unlock(port); - *objectp = (ipc_object_t) port; + *objectp = ip_to_object(port); *sorightp = request; return KERN_SUCCESS; - invalid_right: +invalid_right: return KERN_INVALID_RIGHT; } @@ -2302,6 +2465,7 @@ ipc_right_copyin_two_move_sends( * Conditions: * The space is write-locked and active. * The object is returned with two refs/rights. + * Msgt_one refers to the dest_type * Returns: * KERN_SUCCESS Acquired an object. * KERN_INVALID_RIGHT Name doesn't denote correct right(s). @@ -2309,28 +2473,20 @@ ipc_right_copyin_two_move_sends( */ 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) + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_msg_type_name_t msgt_one, + mach_msg_type_name_t msgt_two, + ipc_object_t *objectp, + ipc_port_t *sorightp, + ipc_port_t *releasep) { kern_return_t kr; int assertcnt = 0; assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_one)); assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_two)); - - - /* - * Pre-validate the second disposition is possible all by itself. - */ - if (!ipc_right_copyin_check(space, name, entry, msgt_two)) { - return KERN_INVALID_CAPABILITY; - } /* * This is a little tedious to make atomic, because @@ -2363,9 +2519,9 @@ ipc_right_copyin_two( ipc_object_t object_two; kr = ipc_right_copyin(space, name, entry, - msgt_one, FALSE, - objectp, sorightp, releasep, - &assertcnt); + msgt_one, IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND, + objectp, sorightp, releasep, + &assertcnt, 0, NULL); assert(assertcnt == 0); if (kr != KERN_SUCCESS) { return kr; @@ -2382,30 +2538,28 @@ ipc_right_copyin_two( * receive right. */ kr = ipc_right_copyin(space, name, entry, - msgt_two, FALSE, - &object_two, sorightp, releasep, - &assertcnt); + msgt_two, IPC_RIGHT_COPYIN_FLAGS_NONE, + &object_two, sorightp, releasep, + &assertcnt, 0, NULL); assert(assertcnt == 0); assert(kr == KERN_SUCCESS); assert(*sorightp == IP_NULL); assert(*releasep == IP_NULL); assert(object_two == *objectp); assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE); - } else if ((msgt_one == MACH_MSG_TYPE_MOVE_SEND) && - (msgt_two == MACH_MSG_TYPE_MOVE_SEND)) { + (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); + objectp, sorightp, + releasep); if (kr != KERN_SUCCESS) { return kr; } - } else { mach_msg_type_name_t msgt_name; @@ -2424,9 +2578,9 @@ ipc_right_copyin_two( } kr = ipc_right_copyin(space, name, entry, - msgt_name, FALSE, - objectp, sorightp, releasep, - &assertcnt); + msgt_name, IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND, + objectp, sorightp, releasep, + &assertcnt, 0, NULL); assert(assertcnt == 0); if (kr != KERN_SUCCESS) { return kr; @@ -2437,7 +2591,7 @@ ipc_right_copyin_two( * that's OK. Neither right will be usable to send * a message anyway. */ - (void)ipc_port_copy_send((ipc_port_t)*objectp); + (void)ipc_port_copy_send(ip_object_to_port(*objectp)); } return KERN_SUCCESS; @@ -2454,9 +2608,9 @@ ipc_right_copyin_two( * because user-reference overflow isn't a possibility. * * If copying out the object would cause the user-reference - * count in the entry to overflow, and overflow is TRUE, - * then instead the user-reference count is left pegged - * to its maximum value and the copyout succeeds anyway. + * count in the entry to overflow, then the user-reference + * count is left pegged to its maximum value and the copyout + * succeeds anyway. * Conditions: * The space is write-locked and active. * The object is locked and active. @@ -2467,12 +2621,13 @@ ipc_right_copyin_two( kern_return_t ipc_right_copyout( - ipc_space_t space, - mach_port_name_t name, - ipc_entry_t entry, - mach_msg_type_name_t msgt_name, - __unused boolean_t overflow, - ipc_object_t object) + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_msg_type_name_t msgt_name, + mach_port_context_t *context, + mach_msg_guard_flags_t *guard_flags, + ipc_object_t object) { ipc_entry_bits_t bits; ipc_port_t port; @@ -2484,23 +2639,28 @@ ipc_right_copyout( assert(io_active(object)); assert(entry->ie_object == object); - port = (ipc_port_t) object; + port = ip_object_to_port(object); switch (msgt_name) { - case MACH_MSG_TYPE_PORT_SEND_ONCE: + case MACH_MSG_TYPE_PORT_SEND_ONCE: assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); assert(IE_BITS_UREFS(bits) == 0); assert(port->ip_sorights > 0); - /* transfer send-once right and ref to entry */ - ip_unlock(port); + if (port->ip_specialreply) { + ipc_port_adjust_special_reply_port_locked(port, + current_thread()->ith_knote, IPC_PORT_ADJUST_SR_LINK_WORKLOOP, FALSE); + /* port unlocked on return */ + } else { + ip_unlock(port); + } entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1); /* set urefs to 1 */ ipc_entry_modified(space, name, entry); break; - case MACH_MSG_TYPE_PORT_SEND: + case MACH_MSG_TYPE_PORT_SEND: assert(port->ip_srights > 0); if (bits & MACH_PORT_TYPE_SEND) { @@ -2526,7 +2686,6 @@ ipc_right_copyout( port->ip_srights--; ip_unlock(port); ip_release(port); - } else if (bits & MACH_PORT_TYPE_RECEIVE) { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); assert(IE_BITS_UREFS(bits) == 0); @@ -2534,7 +2693,6 @@ ipc_right_copyout( /* transfer send right to entry, consume ref */ ip_unlock(port); ip_release(port); - } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); assert(IE_BITS_UREFS(bits) == 0); @@ -2544,48 +2702,68 @@ ipc_right_copyout( /* entry is locked holding ref, so can use port */ - ipc_hash_insert(space, (ipc_object_t) port, - name, entry); + ipc_hash_insert(space, ip_to_object(port), name, entry); } entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1; /* increment urefs */ ipc_entry_modified(space, name, entry); break; - case MACH_MSG_TYPE_PORT_RECEIVE: { + 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); + + imq_lock(&port->ip_messages); dest = port->ip_destination; port->ip_receiver_name = name; port->ip_receiver = space; - assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); + struct knote *kn = current_thread()->ith_knote; + + if ((guard_flags != NULL) && ((*guard_flags & MACH_MSG_GUARD_FLAGS_IMMOVABLE_RECEIVE) != 0)) { + assert(port->ip_immovable_receive == 0); + port->ip_guarded = 1; + port->ip_strict_guard = 0; + /* pseudo receive shouldn't set the receive right as immovable in the sender's space */ + if (kn != ITH_KNOTE_PSEUDO) { + port->ip_immovable_receive = 1; + } + port->ip_context = current_thread()->ith_msg_addr; + *context = port->ip_context; + *guard_flags = *guard_flags & ~MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND; + } + assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); if (bits & MACH_PORT_TYPE_SEND) { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); assert(IE_BITS_UREFS(bits) > 0); assert(port->ip_srights > 0); - - ip_unlock(port); - ip_release(port); - - /* entry is locked holding ref, so can use port */ - - ipc_hash_delete(space, (ipc_object_t) port, - name, entry); } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); assert(IE_BITS_UREFS(bits) == 0); + } - /* transfer ref to entry */ - ip_unlock(port); + boolean_t sync_bootstrap_checkin = FALSE; + if (kn != ITH_KNOTE_PSEUDO && port->ip_sync_bootstrap_checkin) { + sync_bootstrap_checkin = TRUE; + } + if (!ITH_KNOTE_VALID(kn, MACH_MSG_TYPE_PORT_RECEIVE)) { + kn = NULL; + } + ipc_port_adjust_port_locked(port, kn, sync_bootstrap_checkin); + /* port & message queue are unlocked */ + + if (bits & MACH_PORT_TYPE_SEND) { + ip_release(port); + + /* entry is locked holding ref, so can use port */ + ipc_hash_delete(space, ip_to_object(port), name, entry); } entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE; ipc_entry_modified(space, name, entry); @@ -2602,144 +2780,16 @@ ipc_right_copyout( ipc_port_impcount_delta(dest, 0 - assertcnt, IP_NULL); ip_unlock(dest); #endif /* IMPORTANCE_INHERITANCE */ + + /* Drop turnstile ref on dest */ + ipc_port_send_turnstile_complete(dest); ip_release(dest); } break; - } - - default: - panic("ipc_right_copyout: strange rights"); } - return KERN_SUCCESS; -} - -/* - * Routine: ipc_right_rename - * Purpose: - * Transfer an entry from one name to another. - * The old entry is deallocated. - * Conditions: - * The space is write-locked and active. - * The new entry is unused. Upon return, - * the space is unlocked. - * Returns: - * KERN_SUCCESS Moved entry to new name. - */ - -kern_return_t -ipc_right_rename( - ipc_space_t space, - mach_port_name_t oname, - ipc_entry_t oentry, - mach_port_name_t nname, - ipc_entry_t nentry) -{ - ipc_port_request_index_t request = oentry->ie_request; - ipc_entry_bits_t bits = oentry->ie_bits; - ipc_object_t object = oentry->ie_object; - ipc_port_t release_port = IP_NULL; - assert(is_active(space)); - assert(oname != nname); - - /* - * If IE_BITS_COMPAT, we can't allow the entry to be renamed - * if the port is dead. (This would foil ipc_port_destroy.) - * Instead we should fail because oentry shouldn't exist. - * Note IE_BITS_COMPAT implies ie_request != 0. - */ - - if (request != IE_REQ_NONE) { - ipc_port_t port; - - assert(bits & MACH_PORT_TYPE_PORT_RIGHTS); - port = (ipc_port_t) object; - assert(port != IP_NULL); - - if (ipc_right_check(space, port, oname, oentry)) { - request = IE_REQ_NONE; - object = IO_NULL; - bits = oentry->ie_bits; - release_port = port; - assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); - assert(oentry->ie_request == IE_REQ_NONE); - } else { - /* port is locked and active */ - - ipc_port_request_rename(port, request, oname, nname); - ip_unlock(port); - oentry->ie_request = IE_REQ_NONE; - } - } - - /* initialize nentry before letting ipc_hash_insert see it */ - - assert((nentry->ie_bits & IE_BITS_RIGHT_MASK) == 0); - nentry->ie_bits |= bits & IE_BITS_RIGHT_MASK; - nentry->ie_request = request; - nentry->ie_object = object; - - switch (IE_BITS_TYPE(bits)) { - case MACH_PORT_TYPE_SEND: { - ipc_port_t port; - - port = (ipc_port_t) object; - assert(port != IP_NULL); - - /* remember, there are no other share entries possible */ - /* or we can't do the rename. Therefore we do not need */ - /* to check the other subspaces */ - ipc_hash_delete(space, (ipc_object_t) port, oname, oentry); - ipc_hash_insert(space, (ipc_object_t) port, nname, nentry); - break; - } - - case MACH_PORT_TYPE_RECEIVE: - case MACH_PORT_TYPE_SEND_RECEIVE: { - ipc_port_t port; - - port = (ipc_port_t) object; - assert(port != IP_NULL); - - ip_lock(port); - assert(ip_active(port)); - assert(port->ip_receiver_name == oname); - assert(port->ip_receiver == space); - - port->ip_receiver_name = nname; - ip_unlock(port); - break; - } - - case MACH_PORT_TYPE_PORT_SET: { - ipc_pset_t pset; - - pset = (ipc_pset_t) object; - assert(pset != IPS_NULL); - - ips_lock(pset); - assert(ips_active(pset)); - - ips_unlock(pset); - break; - } - - case MACH_PORT_TYPE_SEND_ONCE: - case MACH_PORT_TYPE_DEAD_NAME: - break; - - default: - panic("ipc_right_rename: strange rights"); + default: + panic("ipc_right_copyout: strange rights"); } - - assert(oentry->ie_request == IE_REQ_NONE); - oentry->ie_object = IO_NULL; - ipc_entry_dealloc(space, oname, oentry); - ipc_entry_modified(space, nname, nentry); - is_write_unlock(space); - - if (release_port != IP_NULL) - ip_release(release_port); - return KERN_SUCCESS; }