X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/91447636331957f3d9b5ca5b508f07c526b0074d..f427ee49d309d8fc33ebf3042c3a775f2f530ded:/osfmk/ipc/ipc_object.c diff --git a/osfmk/ipc/ipc_object.c b/osfmk/ipc/ipc_object.c index 0874e7a31..5086568f6 100644 --- a/osfmk/ipc/ipc_object.c +++ b/osfmk/ipc/ipc_object.c @@ -1,52 +1,65 @@ /* - * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2020 Apple Inc. All rights reserved. * - * @APPLE_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 Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * @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 + * 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. + * + * 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. - * - * @APPLE_LICENSE_HEADER_END@ + * 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_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. */ +/* + * 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. + */ /* */ /* @@ -57,8 +70,6 @@ * Functions to manipulate IPC objects. */ -#include - #include #include #include @@ -67,8 +78,10 @@ #include #include +#include #include +#include #include #include #include @@ -76,9 +89,21 @@ #include #include #include +#include #include -zone_t ipc_object_zones[IOT_NUMBER]; +#include + +SECURITY_READ_ONLY_LATE(zone_t) ipc_object_zones[IOT_NUMBER]; + +ZONE_INIT(&ipc_object_zones[IOT_PORT], "ipc ports", sizeof(struct ipc_port), + ZC_NOENCRYPT | ZC_CACHING | ZC_ZFREE_CLEARMEM | ZC_NOSEQUESTER, + ZONE_ID_IPC_PORT, NULL); + +ZONE_INIT(&ipc_object_zones[IOT_PORT_SET], "ipc port sets", + sizeof(struct ipc_pset), + ZC_NOENCRYPT | ZC_ZFREE_CLEARMEM | ZC_NOSEQUESTER, + ZONE_ID_IPC_PORT_SET, NULL); /* * Routine: ipc_object_reference @@ -88,12 +113,9 @@ zone_t ipc_object_zones[IOT_NUMBER]; void ipc_object_reference( - ipc_object_t object) + ipc_object_t object) { - io_lock(object); - assert(object->io_references > 0); io_reference(object); - io_unlock(object); } /* @@ -104,12 +126,9 @@ ipc_object_reference( void ipc_object_release( - ipc_object_t object) + ipc_object_t object) { - io_lock(object); - assert(object->io_references > 0); io_release(object); - io_check_unlock(object); } /* @@ -118,28 +137,32 @@ ipc_object_release( * Look up an object in a space. * Conditions: * Nothing locked before. If successful, the object - * is returned locked. The caller doesn't get a ref. + * is returned active and locked. The caller doesn't get a ref. * Returns: * KERN_SUCCESS Object returned locked. * KERN_INVALID_TASK The space is dead. - * KERN_INVALID_NAME The name doesn't denote a right. - * KERN_INVALID_RIGHT Name doesn't denote the correct right. + * KERN_INVALID_NAME The name doesn't denote a right + * KERN_INVALID_RIGHT Name doesn't denote the correct right */ - kern_return_t ipc_object_translate( - ipc_space_t space, - mach_port_name_t name, - mach_port_right_t right, - ipc_object_t *objectp) + ipc_space_t space, + mach_port_name_t name, + mach_port_right_t right, + ipc_object_t *objectp) { ipc_entry_t entry; ipc_object_t object; kern_return_t kr; + if (!MACH_PORT_RIGHT_VALID_TRANSLATE(right)) { + return KERN_INVALID_RIGHT; + } + kr = ipc_right_lookup_read(space, name, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* space is read-locked and active */ if ((entry->ie_bits & MACH_PORT_TYPE(right)) == MACH_PORT_TYPE_NONE) { @@ -153,6 +176,11 @@ ipc_object_translate( io_lock(object); is_read_unlock(space); + if (!io_active(object)) { + io_unlock(object); + return KERN_INVALID_NAME; + } + *objectp = object; return KERN_SUCCESS; } @@ -173,43 +201,73 @@ ipc_object_translate( kern_return_t ipc_object_translate_two( - ipc_space_t space, - mach_port_name_t name1, - mach_port_right_t right1, - ipc_object_t *objectp1, - mach_port_name_t name2, - mach_port_right_t right2, - ipc_object_t *objectp2) + ipc_space_t space, + mach_port_name_t name1, + mach_port_right_t right1, + ipc_object_t *objectp1, + mach_port_name_t name2, + mach_port_right_t right2, + ipc_object_t *objectp2) { ipc_entry_t entry1; ipc_entry_t entry2; - ipc_object_t object; + ipc_object_t object1, object2; kern_return_t kr; + boolean_t doguard = TRUE; kr = ipc_right_lookup_two_read(space, name1, &entry1, name2, &entry2); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* space is read-locked and active */ if ((entry1->ie_bits & MACH_PORT_TYPE(right1)) == MACH_PORT_TYPE_NONE) { + /* If looking for receive, and the entry used to hold one, give a pass on EXC_GUARD */ + if ((right1 & MACH_PORT_RIGHT_RECEIVE) == MACH_PORT_RIGHT_RECEIVE && + (entry1->ie_bits & MACH_PORT_TYPE_EX_RECEIVE) == MACH_PORT_TYPE_EX_RECEIVE) { + doguard = FALSE; + } is_read_unlock(space); + if (doguard) { + mach_port_guard_exception(name1, 0, 0, kGUARD_EXC_INVALID_RIGHT); + } return KERN_INVALID_RIGHT; } if ((entry2->ie_bits & MACH_PORT_TYPE(right2)) == MACH_PORT_TYPE_NONE) { + /* If looking for receive, and the entry used to hold one, give a pass on EXC_GUARD */ + if ((right2 & MACH_PORT_RIGHT_RECEIVE) == MACH_PORT_RIGHT_RECEIVE && + (entry2->ie_bits & MACH_PORT_TYPE_EX_RECEIVE) == MACH_PORT_TYPE_EX_RECEIVE) { + doguard = FALSE; + } is_read_unlock(space); + if (doguard) { + mach_port_guard_exception(name2, 0, 0, kGUARD_EXC_INVALID_RIGHT); + } return KERN_INVALID_RIGHT; } - object = entry1->ie_object; - assert(object != IO_NULL); - io_lock(object); - *objectp1 = object; + object1 = entry1->ie_object; + assert(object1 != IO_NULL); + io_lock(object1); + if (!io_active(object1)) { + io_unlock(object1); + is_read_unlock(space); + return KERN_INVALID_NAME; + } - object = entry2->ie_object; - assert(object != IO_NULL); - io_lock(object); - *objectp2 = object; + object2 = entry2->ie_object; + assert(object2 != IO_NULL); + io_lock(object2); + if (!io_active(object2)) { + io_unlock(object1); + io_unlock(object2); + is_read_unlock(space); + return KERN_INVALID_NAME; + } + + *objectp1 = object1; + *objectp2 = object2; is_read_unlock(space); return KERN_SUCCESS; @@ -230,22 +288,23 @@ ipc_object_translate_two( kern_return_t ipc_object_alloc_dead( - ipc_space_t space, - mach_port_name_t *namep) + ipc_space_t space, + mach_port_name_t *namep) { ipc_entry_t entry; kern_return_t kr; kr = ipc_entry_alloc(space, namep, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* space is write-locked */ /* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */ assert(entry->ie_object == IO_NULL); entry->ie_bits |= MACH_PORT_TYPE_DEAD_NAME | 1; - + ipc_entry_modified(space, *namep, entry); is_write_unlock(space); return KERN_SUCCESS; } @@ -265,25 +324,27 @@ ipc_object_alloc_dead( kern_return_t ipc_object_alloc_dead_name( - ipc_space_t space, - mach_port_name_t name) + ipc_space_t space, + mach_port_name_t name) { ipc_entry_t entry; kern_return_t kr; kr = ipc_entry_alloc_name(space, name, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* space is write-locked */ - if (ipc_right_inuse(space, name, entry)) + if (ipc_right_inuse(space, name, entry)) { return KERN_NAME_EXISTS; + } /* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */ assert(entry->ie_object == IO_NULL); entry->ie_bits |= MACH_PORT_TYPE_DEAD_NAME | 1; - + ipc_entry_modified(space, name, entry); is_write_unlock(space); return KERN_SUCCESS; } @@ -294,6 +355,7 @@ ipc_object_alloc_dead_name( * Allocate an object. * Conditions: * Nothing locked. If successful, the object is returned locked. + * The space is write locked on successful return. * The caller doesn't get a reference for the object. * Returns: * KERN_SUCCESS The object is allocated. @@ -304,12 +366,12 @@ ipc_object_alloc_dead_name( kern_return_t ipc_object_alloc( - ipc_space_t space, - ipc_object_type_t otype, - mach_port_type_t type, - mach_port_urefs_t urefs, - mach_port_name_t *namep, - ipc_object_t *objectp) + ipc_space_t space, + ipc_object_type_t otype, + mach_port_type_t type, + mach_port_urefs_t urefs, + mach_port_name_t *namep, + ipc_object_t *objectp) { ipc_object_t object; ipc_entry_t entry; @@ -321,21 +383,22 @@ ipc_object_alloc( assert(urefs <= MACH_PORT_UREFS_MAX); object = io_alloc(otype); - if (object == IO_NULL) + if (object == IO_NULL) { return KERN_RESOURCE_SHORTAGE; + } if (otype == IOT_PORT) { - ipc_port_t port = (ipc_port_t)object; + ipc_port_t port = ip_object_to_port(object); bzero((char *)port, sizeof(*port)); } else if (otype == IOT_PORT_SET) { - ipc_pset_t pset = (ipc_pset_t)object; + ipc_pset_t pset = ips_object_to_pset(object); bzero((char *)pset, sizeof(*pset)); } io_lock_init(object); - *namep = (mach_port_name_t)object; + *namep = CAST_MACH_PORT_TO_NAME(object); kr = ipc_entry_alloc(space, namep, &entry); if (kr != KERN_SUCCESS) { io_free(otype, object); @@ -345,12 +408,12 @@ ipc_object_alloc( entry->ie_bits |= type | urefs; entry->ie_object = object; + ipc_entry_modified(space, *namep, entry); + object->io_bits = io_makebits(TRUE, otype, 0); io_lock(object); - is_write_unlock(space); object->io_references = 1; /* for entry, not caller */ - object->io_bits = io_makebits(TRUE, otype, 0); *objectp = object; return KERN_SUCCESS; @@ -372,12 +435,12 @@ ipc_object_alloc( kern_return_t ipc_object_alloc_name( - ipc_space_t space, - ipc_object_type_t otype, - mach_port_type_t type, - mach_port_urefs_t urefs, - mach_port_name_t name, - ipc_object_t *objectp) + ipc_space_t space, + ipc_object_type_t otype, + mach_port_type_t type, + mach_port_urefs_t urefs, + mach_port_name_t name, + ipc_object_t *objectp) { ipc_object_t object; ipc_entry_t entry; @@ -389,15 +452,16 @@ ipc_object_alloc_name( assert(urefs <= MACH_PORT_UREFS_MAX); object = io_alloc(otype); - if (object == IO_NULL) + if (object == IO_NULL) { return KERN_RESOURCE_SHORTAGE; + } if (otype == IOT_PORT) { - ipc_port_t port = (ipc_port_t)object; + ipc_port_t port = ip_object_to_port(object); bzero((char *)port, sizeof(*port)); } else if (otype == IOT_PORT_SET) { - ipc_pset_t pset = (ipc_pset_t)object; + ipc_pset_t pset = ips_object_to_pset(object); bzero((char *)pset, sizeof(*pset)); } @@ -417,17 +481,38 @@ ipc_object_alloc_name( entry->ie_bits |= type | urefs; entry->ie_object = object; + ipc_entry_modified(space, name, entry); + + object->io_bits = io_makebits(TRUE, otype, 0); io_lock(object); is_write_unlock(space); object->io_references = 1; /* for entry, not caller */ - object->io_bits = io_makebits(TRUE, otype, 0); *objectp = object; return KERN_SUCCESS; } +/* Routine: ipc_object_validate + * Purpose: + * Validates an ipc port or port set as belonging to the correct + * zone. + */ + +void +ipc_object_validate( + ipc_object_t object) +{ + if (io_otype(object) != IOT_PORT_SET) { + zone_id_require(ZONE_ID_IPC_PORT, + sizeof(struct ipc_port), object); + } else { + zone_id_require(ZONE_ID_IPC_PORT_SET, + sizeof(struct ipc_pset), object); + } +} + /* * Routine: ipc_object_copyin_type * Purpose: @@ -436,24 +521,26 @@ ipc_object_alloc_name( mach_msg_type_name_t ipc_object_copyin_type( - mach_msg_type_name_t msgt_name) + mach_msg_type_name_t msgt_name) { switch (msgt_name) { - - case MACH_MSG_TYPE_MOVE_RECEIVE: - case MACH_MSG_TYPE_COPY_RECEIVE: + case MACH_MSG_TYPE_MOVE_RECEIVE: return MACH_MSG_TYPE_PORT_RECEIVE; - case MACH_MSG_TYPE_MOVE_SEND_ONCE: - case MACH_MSG_TYPE_MAKE_SEND_ONCE: + case MACH_MSG_TYPE_MOVE_SEND_ONCE: + case MACH_MSG_TYPE_MAKE_SEND_ONCE: return MACH_MSG_TYPE_PORT_SEND_ONCE; - case MACH_MSG_TYPE_MOVE_SEND: - case MACH_MSG_TYPE_MAKE_SEND: - case MACH_MSG_TYPE_COPY_SEND: + case MACH_MSG_TYPE_MOVE_SEND: + case MACH_MSG_TYPE_MAKE_SEND: + case MACH_MSG_TYPE_COPY_SEND: return MACH_MSG_TYPE_PORT_SEND; - default: + case MACH_MSG_TYPE_DISPOSE_RECEIVE: + case MACH_MSG_TYPE_DISPOSE_SEND: + case MACH_MSG_TYPE_DISPOSE_SEND_ONCE: + /* fall thru */ + default: return MACH_MSG_TYPE_PORT_NONE; } } @@ -475,14 +562,24 @@ ipc_object_copyin_type( kern_return_t ipc_object_copyin( - ipc_space_t space, - mach_port_name_t name, - mach_msg_type_name_t msgt_name, - ipc_object_t *objectp) + ipc_space_t space, + mach_port_name_t name, + mach_msg_type_name_t msgt_name, + ipc_object_t *objectp, + mach_port_context_t context, + mach_msg_guard_flags_t *guard_flags, + ipc_kmsg_flags_t kmsg_flags) { ipc_entry_t entry; ipc_port_t soright; + ipc_port_t release_port; kern_return_t kr; + int assertcnt = 0; + + ipc_right_copyin_flags_t irc_flags = IPC_RIGHT_COPYIN_FLAGS_DEADOK; + if (kmsg_flags & IPC_KMSG_FLAGS_ALLOW_IMMOVABLE_SEND) { + irc_flags |= IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND; + } /* * Could first try a read lock when doing @@ -491,19 +588,37 @@ ipc_object_copyin( */ kr = ipc_right_lookup_write(space, name, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* space is write-locked and active */ + release_port = IP_NULL; kr = ipc_right_copyin(space, name, entry, - msgt_name, TRUE, - objectp, &soright); - if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) + msgt_name, irc_flags, + objectp, &soright, + &release_port, + &assertcnt, + context, + guard_flags); + if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) { ipc_entry_dealloc(space, name, entry); + } is_write_unlock(space); - if ((kr == KERN_SUCCESS) && (soright != IP_NULL)) +#if IMPORTANCE_INHERITANCE + if (0 < assertcnt && ipc_importance_task_is_any_receiver_type(current_task()->task_imp_base)) { + ipc_importance_task_drop_internal_assertion(current_task()->task_imp_base, assertcnt); + } +#endif /* IMPORTANCE_INHERITANCE */ + + if (release_port != IP_NULL) { + ip_release(release_port); + } + + if ((kr == KERN_SUCCESS) && (soright != IP_NULL)) { ipc_notify_port_deleted(soright, name); + } return kr; } @@ -538,32 +653,34 @@ ipc_object_copyin( void ipc_object_copyin_from_kernel( - ipc_object_t object, - mach_msg_type_name_t msgt_name) + ipc_object_t object, + mach_msg_type_name_t msgt_name) { assert(IO_VALID(object)); switch (msgt_name) { - case MACH_MSG_TYPE_MOVE_RECEIVE: { - ipc_port_t port = (ipc_port_t) object; + case MACH_MSG_TYPE_MOVE_RECEIVE: { + ipc_port_t port = ip_object_to_port(object); ip_lock(port); - assert(ip_active(port)); + imq_lock(&port->ip_messages); + require_ip_active(port); if (port->ip_destination != IP_NULL) { assert(port->ip_receiver == ipc_space_kernel); + assert(port->ip_immovable_receive == 0); /* relevant part of ipc_port_clear_receiver */ - ipc_port_set_mscount(port, 0); - + port->ip_mscount = 0; port->ip_receiver_name = MACH_PORT_NULL; port->ip_destination = IP_NULL; } + imq_unlock(&port->ip_messages); ip_unlock(port); break; - } + } - case MACH_MSG_TYPE_COPY_SEND: { - ipc_port_t port = (ipc_port_t) object; + case MACH_MSG_TYPE_COPY_SEND: { + ipc_port_t port = ip_object_to_port(object); ip_lock(port); if (ip_active(port)) { @@ -573,49 +690,50 @@ ipc_object_copyin_from_kernel( ip_reference(port); ip_unlock(port); break; - } + } - case MACH_MSG_TYPE_MAKE_SEND: { - ipc_port_t port = (ipc_port_t) object; + case MACH_MSG_TYPE_MAKE_SEND: { + ipc_port_t port = ip_object_to_port(object); ip_lock(port); - assert(ip_active(port)); - assert(port->ip_receiver_name != MACH_PORT_NULL); - assert(port->ip_receiver == ipc_space_kernel); + if (ip_active(port)) { + assert(port->ip_receiver_name != MACH_PORT_NULL); + assert((port->ip_receiver == ipc_space_kernel) || + (port->ip_receiver->is_node_id != HOST_LOCAL_NODE)); + port->ip_mscount++; + } - ip_reference(port); - port->ip_mscount++; port->ip_srights++; + ip_reference(port); ip_unlock(port); break; - } + } - case MACH_MSG_TYPE_MOVE_SEND: { + case MACH_MSG_TYPE_MOVE_SEND: { /* move naked send right into the message */ - assert(((ipc_port_t)object)->ip_srights); + assert(ip_object_to_port(object)->ip_srights); break; - } + } - case MACH_MSG_TYPE_MAKE_SEND_ONCE: { - ipc_port_t port = (ipc_port_t) object; + case MACH_MSG_TYPE_MAKE_SEND_ONCE: { + ipc_port_t port = ip_object_to_port(object); ip_lock(port); - assert(ip_active(port)); - assert(port->ip_receiver_name != MACH_PORT_NULL); - - ip_reference(port); - port->ip_sorights++; + if (ip_active(port)) { + assert(port->ip_receiver_name != MACH_PORT_NULL); + } + ipc_port_make_sonce_locked(port); ip_unlock(port); break; - } + } - case MACH_MSG_TYPE_MOVE_SEND_ONCE: { + case MACH_MSG_TYPE_MOVE_SEND_ONCE: { /* move naked send-once right into the message */ - assert(((ipc_port_t)object)->ip_sorights); + assert(ip_object_to_port(object)->ip_sorights); break; - } + } - default: + default: panic("ipc_object_copyin_from_kernel: strange rights"); } } @@ -633,30 +751,159 @@ ipc_object_copyin_from_kernel( void ipc_object_destroy( - ipc_object_t object, - mach_msg_type_name_t msgt_name) + ipc_object_t object, + mach_msg_type_name_t msgt_name) { assert(IO_VALID(object)); assert(io_otype(object) == IOT_PORT); switch (msgt_name) { - case MACH_MSG_TYPE_PORT_SEND: - ipc_port_release_send((ipc_port_t) object); + case MACH_MSG_TYPE_PORT_SEND: + ipc_port_release_send(ip_object_to_port(object)); break; - case MACH_MSG_TYPE_PORT_SEND_ONCE: - ipc_notify_send_once((ipc_port_t) object); + case MACH_MSG_TYPE_PORT_SEND_ONCE: + ipc_notify_send_once(ip_object_to_port(object)); break; - case MACH_MSG_TYPE_PORT_RECEIVE: - ipc_port_release_receive((ipc_port_t) object); + case MACH_MSG_TYPE_PORT_RECEIVE: + ipc_port_release_receive(ip_object_to_port(object)); break; - default: + default: panic("ipc_object_destroy: strange rights"); } } +/* + * Routine: ipc_object_destroy_dest + * Purpose: + * Destroys a naked capability for the destination of + * of a message. Consumes a ref for the object. + * + * Conditions: + * Nothing locked. + */ + +void +ipc_object_destroy_dest( + ipc_object_t object, + mach_msg_type_name_t msgt_name) +{ + assert(IO_VALID(object)); + assert(io_otype(object) == IOT_PORT); + + switch (msgt_name) { + case MACH_MSG_TYPE_PORT_SEND: + ipc_port_release_send(ip_object_to_port(object)); + break; + + case MACH_MSG_TYPE_PORT_SEND_ONCE: + if (io_active(object) && + !ip_full_kernel(ip_object_to_port(object))) { + ipc_notify_send_once(ip_object_to_port(object)); + } else { + ipc_port_release_sonce(ip_object_to_port(object)); + } + break; + + default: + panic("ipc_object_destroy_dest: strange rights"); + } +} + +/* + * Routine: ipc_object_insert_send_right + * Purpose: + * Insert a send right into an object already in the space. + * The specified name must already point to a valid object. + * + * Note: This really is a combined copyin()/copyout(), + * that avoids most of the overhead of being implemented that way. + * + * This is the fastpath for mach_port_insert_right. + * + * Conditions: + * Nothing locked. + * + * msgt_name must be MACH_MSG_TYPE_MAKE_SEND_ONCE or + * MACH_MSG_TYPE_MOVE_SEND_ONCE. + * + * Returns: + * KERN_SUCCESS Copied out object, consumed ref. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME Name doesn't exist in space. + * KERN_INVALID_CAPABILITY The object is dead. + * KERN_RIGHT_EXISTS Space has rights under another name. + */ +kern_return_t +ipc_object_insert_send_right( + ipc_space_t space, + mach_port_name_t name, + mach_msg_type_name_t msgt_name) +{ + ipc_entry_bits_t bits; + ipc_object_t object; + ipc_entry_t entry; + kern_return_t kr; + + assert(msgt_name == MACH_MSG_TYPE_MAKE_SEND || + msgt_name == MACH_MSG_TYPE_COPY_SEND); + + kr = ipc_right_lookup_write(space, name, &entry); + if (kr != KERN_SUCCESS) { + return kr; + } + /* space is write-locked and active */ + + if (!IO_VALID(entry->ie_object)) { + is_write_unlock(space); + return KERN_INVALID_CAPABILITY; + } + + bits = entry->ie_bits; + object = entry->ie_object; + + io_lock(object); + if (!io_active(object)) { + kr = KERN_INVALID_CAPABILITY; + } else if (msgt_name == MACH_MSG_TYPE_MAKE_SEND) { + if (bits & MACH_PORT_TYPE_RECEIVE) { + ipc_port_t port = ip_object_to_port(object); + port->ip_mscount++; + if ((bits & MACH_PORT_TYPE_SEND) == 0) { + port->ip_srights++; + bits |= MACH_PORT_TYPE_SEND; + } + /* leave urefs pegged to maximum if it overflowed */ + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { + bits += 1; /* increment urefs */ + } + entry->ie_bits = bits; + ipc_entry_modified(space, name, entry); + kr = KERN_SUCCESS; + } else { + kr = KERN_INVALID_RIGHT; + } + } else { // MACH_MSG_TYPE_COPY_SEND + if (bits & MACH_PORT_TYPE_SEND) { + /* leave urefs pegged to maximum if it overflowed */ + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) { + entry->ie_bits = bits + 1; /* increment urefs */ + } + ipc_entry_modified(space, name, entry); + kr = KERN_SUCCESS; + } else { + kr = KERN_INVALID_RIGHT; + } + } + + io_unlock(object); + is_write_unlock(space); + + return kr; +} + /* * Routine: ipc_object_copyout * Purpose: @@ -676,12 +923,14 @@ ipc_object_destroy( kern_return_t ipc_object_copyout( - ipc_space_t space, - ipc_object_t object, - mach_msg_type_name_t msgt_name, - boolean_t overflow, - mach_port_name_t *namep) + ipc_space_t space, + ipc_object_t object, + mach_msg_type_name_t msgt_name, + mach_port_context_t *context, + mach_msg_guard_flags_t *guard_flags, + mach_port_name_t *namep) { + struct knote *kn = current_thread()->ith_knote; mach_port_name_t name; ipc_entry_t entry; kern_return_t kr; @@ -689,31 +938,37 @@ ipc_object_copyout( assert(IO_VALID(object)); assert(io_otype(object) == IOT_PORT); + if (ITH_KNOTE_VALID(kn, msgt_name)) { + filt_machport_turnstile_prepare_lazily(kn, + msgt_name, ip_object_to_port(object)); + } + is_write_lock(space); for (;;) { - if (!space->is_active) { + if (!is_active(space)) { is_write_unlock(space); return KERN_INVALID_TASK; } if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) && - ipc_right_reverse(space, object, &name, &entry)) { + ipc_right_reverse(space, object, &name, &entry)) { /* object is locked and active */ assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE); break; } - name = (mach_port_name_t)object; + + name = CAST_MACH_PORT_TO_NAME(object); kr = ipc_entry_get(space, &name, &entry); if (kr != KERN_SUCCESS) { /* unlocks/locks space, so must start again */ kr = ipc_entry_grow_table(space, ITS_SIZE_NONE); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; /* space is unlocked */ - + } continue; } @@ -728,6 +983,14 @@ ipc_object_copyout( return KERN_INVALID_CAPABILITY; } + /* Don't actually copyout rights we aren't allowed to */ + if (!ip_label_check(space, ip_object_to_port(object), msgt_name)) { + io_unlock(object); + ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + return KERN_INVALID_CAPABILITY; + } + entry->ie_object = object; break; } @@ -735,12 +998,14 @@ ipc_object_copyout( /* space is write-locked and active, object is locked and active */ kr = ipc_right_copyout(space, name, entry, - msgt_name, overflow, object); + msgt_name, context, guard_flags, object); + /* object is unlocked */ is_write_unlock(space); - if (kr == KERN_SUCCESS) + if (kr == KERN_SUCCESS) { *namep = name; + } return kr; } @@ -765,23 +1030,28 @@ ipc_object_copyout( kern_return_t ipc_object_copyout_name( - ipc_space_t space, - ipc_object_t object, - mach_msg_type_name_t msgt_name, - boolean_t overflow, - mach_port_name_t name) + ipc_space_t space, + ipc_object_t object, + mach_msg_type_name_t msgt_name, + mach_port_name_t name) { mach_port_name_t oname; ipc_entry_t oentry; ipc_entry_t entry; kern_return_t kr; +#if IMPORTANCE_INHERITANCE + int assertcnt = 0; + ipc_importance_task_t task_imp = IIT_NULL; +#endif /* IMPORTANCE_INHERITANCE */ + assert(IO_VALID(object)); assert(io_otype(object) == IOT_PORT); kr = ipc_entry_alloc_name(space, name, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* space is write-locked and active */ if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) && @@ -791,8 +1061,9 @@ ipc_object_copyout_name( if (name != oname) { io_unlock(object); - if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) + if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) { ipc_entry_dealloc(space, name, entry); + } is_write_unlock(space); return KERN_RIGHT_EXISTS; @@ -801,8 +1072,9 @@ ipc_object_copyout_name( assert(entry == oentry); assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE); } else { - if (ipc_right_inuse(space, name, entry)) + if (ipc_right_inuse(space, name, entry)) { return KERN_NAME_EXISTS; + } assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE); assert(entry->ie_object == IO_NULL); @@ -815,15 +1087,62 @@ ipc_object_copyout_name( return KERN_INVALID_CAPABILITY; } + /* Don't actually copyout rights we aren't allowed to */ + if (!ip_label_check(space, ip_object_to_port(object), msgt_name)) { + io_unlock(object); + ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + return KERN_INVALID_CAPABILITY; + } + entry->ie_object = object; } /* space is write-locked and active, object is locked and active */ +#if IMPORTANCE_INHERITANCE + /* + * We are slamming a receive right into the space, without + * first having been enqueued on a port destined there. So, + * we have to arrange to boost the task appropriately if this + * port has assertions (and the task wants them). + */ + if (msgt_name == MACH_MSG_TYPE_PORT_RECEIVE) { + ipc_port_t port = ip_object_to_port(object); + + if (space->is_task != TASK_NULL) { + task_imp = space->is_task->task_imp_base; + if (ipc_importance_task_is_any_receiver_type(task_imp)) { + assertcnt = port->ip_impcount; + ipc_importance_task_reference(task_imp); + } else { + task_imp = IIT_NULL; + } + } + + /* take port out of limbo */ + assert(port->ip_tempowner != 0); + port->ip_tempowner = 0; + } + +#endif /* IMPORTANCE_INHERITANCE */ + kr = ipc_right_copyout(space, name, entry, - msgt_name, overflow, object); + msgt_name, NULL, NULL, object); + /* object is unlocked */ is_write_unlock(space); + +#if IMPORTANCE_INHERITANCE + /* + * Add the assertions to the task that we captured before + */ + if (task_imp != IIT_NULL) { + ipc_importance_task_hold_internal_assertion(task_imp, assertcnt); + ipc_importance_task_release(task_imp); + } +#endif /* IMPORTANCE_INHERITANCE */ + return kr; } @@ -842,18 +1161,16 @@ ipc_object_copyout_name( void ipc_object_copyout_dest( - ipc_space_t space, - ipc_object_t object, - mach_msg_type_name_t msgt_name, - mach_port_name_t *namep) + ipc_space_t space, + ipc_object_t object, + mach_msg_type_name_t msgt_name, + mach_port_name_t *namep) { mach_port_name_t name; assert(IO_VALID(object)); assert(io_active(object)); - io_release(object); - /* * If the space is the receiver/owner of the object, * then we quietly consume the right and return @@ -862,15 +1179,16 @@ ipc_object_copyout_dest( */ switch (msgt_name) { - case MACH_MSG_TYPE_PORT_SEND: { - ipc_port_t port = (ipc_port_t) object; + case MACH_MSG_TYPE_PORT_SEND: { + ipc_port_t port = ip_object_to_port(object); ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount; - if (port->ip_receiver == space) + if (port->ip_receiver == space) { name = port->ip_receiver_name; - else + } else { name = MACH_PORT_NULL; + } assert(port->ip_srights > 0); if (--port->ip_srights == 0 && @@ -878,15 +1196,20 @@ ipc_object_copyout_dest( nsrequest = port->ip_nsrequest; port->ip_nsrequest = IP_NULL; mscount = port->ip_mscount; - ip_unlock(port); + ipc_port_clear_sync_rcv_thread_boost_locked(port); + /* port unlocked */ ipc_notify_no_senders(nsrequest, mscount); - } else - ip_unlock(port); + } else { + ipc_port_clear_sync_rcv_thread_boost_locked(port); + /* port unlocked */ + } + + ip_release(port); break; - } + } - case MACH_MSG_TYPE_PORT_SEND_ONCE: { - ipc_port_t port = (ipc_port_t) object; + case MACH_MSG_TYPE_PORT_SEND_ONCE: { + ipc_port_t port = ip_object_to_port(object); assert(port->ip_sorights > 0); @@ -895,7 +1218,9 @@ ipc_object_copyout_dest( port->ip_sorights--; name = port->ip_receiver_name; - ip_unlock(port); + ipc_port_clear_sync_rcv_thread_boost_locked(port); + /* port unlocked */ + ip_release(port); } else { /* * A very bizarre case. The message @@ -906,7 +1231,6 @@ ipc_object_copyout_dest( * so generate a send-once notification. */ - ip_reference(port); /* restore ref */ ip_unlock(port); ipc_notify_send_once(port); @@ -914,9 +1238,9 @@ ipc_object_copyout_dest( } break; - } + } - default: + default: panic("ipc_object_copyout_dest: strange rights"); name = MACH_PORT_DEAD; } @@ -925,143 +1249,44 @@ ipc_object_copyout_dest( } /* - * Routine: ipc_object_rename + * Routine: io_lock * Purpose: - * Rename an entry in a space. - * Conditions: - * Nothing locked. - * Returns: - * KERN_SUCCESS Renamed the entry. - * KERN_INVALID_TASK The space was dead. - * KERN_INVALID_NAME oname didn't denote an entry. - * KERN_NAME_EXISTS nname already denoted an entry. - * KERN_RESOURCE_SHORTAGE Couldn't allocate new entry. + * Validate, then acquire a lock on an ipc object */ -kern_return_t -ipc_object_rename( - ipc_space_t space, - mach_port_name_t oname, - mach_port_name_t nname) +void +io_lock(ipc_object_t io) { - ipc_entry_t oentry, nentry; - kern_return_t kr; - - kr = ipc_entry_alloc_name(space, nname, &nentry); - if (kr != KERN_SUCCESS) - return kr; - - /* space is write-locked and active */ - - if (ipc_right_inuse(space, nname, nentry)) { - /* space is unlocked */ - return KERN_NAME_EXISTS; - } - - /* don't let ipc_entry_lookup see the uninitialized new entry */ + ipc_object_validate(io); + lck_spin_lock_grp(&(io)->io_lock_data, &ipc_lck_grp); +} - if ((oname == nname) || - ((oentry = ipc_entry_lookup(space, oname)) == IE_NULL)) { - ipc_entry_dealloc(space, nname, nentry); - is_write_unlock(space); - return KERN_INVALID_NAME; - } +/* + * Routine: io_lock_try + * Purpose: + * Validate, then try to acquire a lock on an object, + * fail if there is an existing busy lock + */ - kr = ipc_right_rename(space, oname, oentry, nname, nentry); - /* space is unlocked */ - return kr; +boolean_t +io_lock_try(ipc_object_t io) +{ + ipc_object_validate(io); + return lck_spin_try_lock_grp(&(io)->io_lock_data, &ipc_lck_grp); } -#if MACH_ASSERT /* * Check whether the object is a port if so, free it. But * keep track of that fact. */ void io_free( - unsigned int otype, - ipc_object_t object) + unsigned int otype, + ipc_object_t object) { - ipc_port_t port; - if (otype == IOT_PORT) { - port = (ipc_port_t) object; -#if MACH_ASSERT - ipc_port_track_dealloc(port); -#endif /* MACH_ASSERT */ + ipc_port_finalize(ip_object_to_port(object)); } + io_lock_destroy(object); zfree(ipc_object_zones[otype], object); } -#endif /* MACH_ASSERT */ - -#include -#if MACH_KDB - -#include -#include - -#define printf kdbprintf - -/* - * Routine: ipc_object_print - * Purpose: - * Pretty-print an object for kdb. - */ - -const char *ikot_print_array[IKOT_MAX_TYPE] = { - "(NONE) ", - "(THREAD) ", - "(TASK) ", - "(HOST) ", - "(HOST_PRIV) ", - "(PROCESSOR) ", - "(PSET) ", - "(PSET_NAME) ", - "(TIMER) ", - "(PAGER_REQUEST) ", - "(DEVICE) ", /* 10 */ - "(XMM_OBJECT) ", - "(XMM_PAGER) ", - "(XMM_KERNEL) ", - "(XMM_REPLY) ", - "(NOTDEF 15) ", - "(NOTDEF 16) ", - "(HOST_SECURITY) ", - "(LEDGER) ", - "(MASTER_DEVICE) ", - "(ACTIVATION) ", /* 20 */ - "(SUBSYSTEM) ", - "(IO_DONE_QUEUE) ", - "(SEMAPHORE) ", - "(LOCK_SET) ", - "(CLOCK) ", - "(CLOCK_CTRL) ", /* 26 */ - "(IOKIT_SPARE) ", /* 27 */ - "(NAMED_MEM_ENTRY) ", /* 28 */ - "(IOKIT_CONNECT) ", - "(IOKIT_OBJECT) ", /* 30 */ - "(UPL) ", - /* << new entries here */ - "(UNKNOWN) " /* magic catchall */ -}; -/* Please keep in sync with kern/ipc_kobject.h */ - -void -ipc_object_print( - ipc_object_t object) -{ - int kotype; - - iprintf("%s", io_active(object) ? "active" : "dead"); - printf(", refs=%d", object->io_references); - printf(", otype=%d", io_otype(object)); - kotype = io_kotype(object); - if (kotype >= 0 && kotype < IKOT_MAX_TYPE) - printf(", kotype=%d %s\n", io_kotype(object), - ikot_print_array[kotype]); - else - printf(", kotype=0x%x %s\n", io_kotype(object), - ikot_print_array[IKOT_UNKNOWN]); -} - -#endif /* MACH_KDB */