X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/316670eb35587141e969394ae8537d66b9211e80..f427ee49d309d8fc33ebf3042c3a775f2f530ded:/osfmk/ipc/ipc_object.c diff --git a/osfmk/ipc/ipc_object.c b/osfmk/ipc/ipc_object.c index 35f9224f0..5086568f6 100644 --- a/osfmk/ipc/ipc_object.c +++ b/osfmk/ipc/ipc_object.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2007 Apple Inc. All rights reserved. + * Copyright (c) 2000-2020 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_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. */ @@ -70,8 +70,6 @@ * Functions to manipulate IPC objects. */ -#include - #include #include #include @@ -83,6 +81,7 @@ #include #include +#include #include #include #include @@ -92,11 +91,19 @@ #include #include #include -#include #include -zone_t ipc_object_zones[IOT_NUMBER]; +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 @@ -106,7 +113,7 @@ zone_t ipc_object_zones[IOT_NUMBER]; void ipc_object_reference( - ipc_object_t object) + ipc_object_t object) { io_reference(object); } @@ -119,7 +126,7 @@ ipc_object_reference( void ipc_object_release( - ipc_object_t object) + ipc_object_t object) { io_release(object); } @@ -130,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) { @@ -165,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; } @@ -185,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; @@ -242,15 +288,16 @@ 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 */ @@ -277,19 +324,21 @@ 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 */ @@ -306,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. @@ -316,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; @@ -333,18 +383,16 @@ 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)); -#if CONFIG_MACF_MACH - mac_port_label_init(&port->ip_label); -#endif } 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)); } @@ -362,11 +410,10 @@ ipc_object_alloc( 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; @@ -388,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; @@ -405,18 +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)); -#if CONFIG_MACF_MACH - mac_port_label_init(&port->ip_label); -#endif } 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)); } @@ -438,16 +483,36 @@ ipc_object_alloc_name( 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: @@ -456,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; } } @@ -495,20 +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; - queue_head_t links_data; - queue_t links = &links_data; - wait_queue_link_t wql; + int assertcnt = 0; - queue_init(links); + 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 @@ -517,30 +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, - &release_port, - links); - 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); - while(!queue_empty(links)) { - wql = (wait_queue_link_t) dequeue(links); - wait_queue_link_free(wql); +#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) + if (release_port != IP_NULL) { ip_release(release_port); + } - if ((kr == KERN_SUCCESS) && (soright != IP_NULL)) + if ((kr == KERN_SUCCESS) && (soright != IP_NULL)) { ipc_notify_port_deleted(soright, name); + } return kr; } @@ -575,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)) { @@ -610,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"); } } @@ -670,26 +751,26 @@ 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"); } } @@ -706,30 +787,123 @@ ipc_object_destroy( void ipc_object_destroy_dest( - 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: - if (io_active(object) && - !ip_full_kernel((ipc_port_t) object)) - ipc_notify_send_once((ipc_port_t) object); - else - ipc_port_release_sonce((ipc_port_t) object); + 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: + 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: @@ -749,12 +923,14 @@ ipc_object_destroy_dest( 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; @@ -762,6 +938,11 @@ 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 (;;) { @@ -771,22 +952,23 @@ ipc_object_copyout( } 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 = 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; } @@ -801,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; } @@ -808,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; } @@ -838,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) && @@ -864,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; @@ -874,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); @@ -888,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; } @@ -915,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 @@ -935,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 && @@ -951,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); @@ -968,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 @@ -979,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); @@ -987,9 +1238,9 @@ ipc_object_copyout_dest( } break; - } + } - default: + default: panic("ipc_object_copyout_dest: strange rights"); name = MACH_PORT_DEAD; } @@ -998,77 +1249,31 @@ 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 */ - - 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; - } - - kr = ipc_right_rename(space, oname, oentry, nname, nentry); - /* space is unlocked */ - return kr; + ipc_object_validate(io); + lck_spin_lock_grp(&(io)->io_lock_data, &ipc_lck_grp); } /* - * Get a label out of a port, to be used by a kernel call - * that takes a security label as a parameter. In this case, we want - * to use the label stored in the label handle and not the label on its - * port. - * - * The port should be locked for this call. The lock protecting - * label handle contents should not be necessary, as they can only - * be modified when a label handle with one reference is a task label. - * User allocated label handles can never be modified. + * Routine: io_lock_try + * Purpose: + * Validate, then try to acquire a lock on an object, + * fail if there is an existing busy lock */ -#if CONFIG_MACF_MACH -struct label *io_getlabel (ipc_object_t objp) -{ - ipc_port_t port = (ipc_port_t)objp; - assert(io_otype(objp) == IOT_PORT); - - if (ip_kotype(port) == IKOT_LABELH) - return &((ipc_labelh_t) port->ip_kobject)->lh_label; - else - return &port->ip_label; +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); } -#endif /* * Check whether the object is a port if so, free it. But @@ -1076,14 +1281,11 @@ struct label *io_getlabel (ipc_object_t objp) */ 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; - ipc_port_finalize(port); + ipc_port_finalize(ip_object_to_port(object)); } io_lock_destroy(object); zfree(ipc_object_zones[otype], object);