X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/8ad349bb6ed4a0be06e34c92be0d98b92e078db4..cb3231590a3c94ab4375e2228bd5e86b0cf1ad7e:/osfmk/ipc/mach_port.c diff --git a/osfmk/ipc/mach_port.c b/osfmk/ipc/mach_port.c index 934e3e1d7..9f4d8b677 100644 --- a/osfmk/ipc/mach_port.c +++ b/osfmk/ipc/mach_port.c @@ -1,72 +1,76 @@ /* - * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2007 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_OSREFERENCE_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. + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and * limitations under the License. * - * @APPLE_LICENSE_OSREFERENCE_HEADER_END@ + * @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. + */ /* */ /* * File: ipc/mach_port.c * Author: Rich Draves - * Date: 1989 + * Date: 1989 * * Exported kernel calls. See mach/mach_port.defs. */ #include -#include #include #include @@ -79,9 +83,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -91,90 +97,86 @@ #include #include #include +#include -/* - * Forward declarations - */ -void mach_port_names_helper( - ipc_port_timestamp_t timestamp, - ipc_entry_t entry, - mach_port_name_t name, - mach_port_name_t *names, - mach_port_type_t *types, - ipc_entry_num_t *actualp); - -void mach_port_gst_helper( - ipc_pset_t pset, - ipc_port_t port, - ipc_entry_num_t maxnames, - mach_port_name_t *names, - ipc_entry_num_t *actualp); +#if IMPORTANCE_INHERITANCE +#include +#endif /* Zeroed template of qos flags */ -static mach_port_qos_t qos_template; +static mach_port_qos_t qos_template; /* * Routine: mach_port_names_helper * Purpose: * A helper function for mach_port_names. + * + * Conditions: + * Space containing entry is [at least] read-locked. */ - -void +static void mach_port_names_helper( - ipc_port_timestamp_t timestamp, - ipc_entry_t entry, - mach_port_name_t name, - mach_port_name_t *names, - mach_port_type_t *types, - ipc_entry_num_t *actualp) + ipc_port_timestamp_t timestamp, + ipc_entry_t entry, + mach_port_name_t name, + mach_port_name_t *names, + mach_port_type_t *types, + ipc_entry_num_t *actualp) { ipc_entry_bits_t bits; ipc_port_request_index_t request; - mach_port_type_t type; + mach_port_type_t type = 0; ipc_entry_num_t actual; + ipc_port_t port; bits = entry->ie_bits; request = entry->ie_request; - if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { - ipc_port_t port; - boolean_t died; + port = ip_object_to_port(entry->ie_object); - port = (ipc_port_t) entry->ie_object; - assert(port != IP_NULL); + if (bits & MACH_PORT_TYPE_RECEIVE) { + assert(IP_VALID(port)); - /* - * The timestamp serializes mach_port_names - * with ipc_port_destroy. If the port died, - * but after mach_port_names started, pretend - * that it isn't dead. - */ + if (request != IE_REQ_NONE) { + ip_lock(port); + require_ip_active(port); + type |= ipc_port_request_type(port, name, request); + ip_unlock(port); + } + } else if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { + mach_port_type_t reqtype; + assert(IP_VALID(port)); ip_lock(port); - died = (!ip_active(port) && - IP_TIMESTAMP_ORDER(port->ip_timestamp, timestamp)); - ip_unlock(port); - if (died) { - /* pretend this is a dead-name entry */ + reqtype = (request != IE_REQ_NONE) ? + ipc_port_request_type(port, name, request) : 0; + /* + * If the port is alive, or was alive when the mach_port_names + * started, then return that fact. Otherwise, pretend we found + * a dead name entry. + */ + if (ip_active(port) || IP_TIMESTAMP_ORDER(timestamp, port->ip_timestamp)) { + type |= reqtype; + } else { bits &= ~(IE_BITS_TYPE_MASK); bits |= MACH_PORT_TYPE_DEAD_NAME; - if (request != 0) + /* account for additional reference for dead-name notification */ + if (reqtype != 0) { bits++; - request = 0; + } } + ip_unlock(port); } - type = IE_BITS_TYPE(bits); - if (request != 0) - type |= MACH_PORT_TYPE_DNREQUEST; + type |= IE_BITS_TYPE(bits); actual = *actualp; names[actual] = name; types[actual] = type; - *actualp = actual+1; + *actualp = actual + 1; } /* @@ -196,33 +198,33 @@ mach_port_names_helper( kern_return_t mach_port_names( - ipc_space_t space, - mach_port_name_t **namesp, - mach_msg_type_number_t *namesCnt, - mach_port_type_t **typesp, - mach_msg_type_number_t *typesCnt) + ipc_space_t space, + mach_port_name_t **namesp, + mach_msg_type_number_t *namesCnt, + mach_port_type_t **typesp, + mach_msg_type_number_t *typesCnt) { - ipc_tree_entry_t tentry; ipc_entry_t table; ipc_entry_num_t tsize; mach_port_index_t index; - ipc_entry_num_t actual; /* this many names */ - ipc_port_timestamp_t timestamp; /* logical time of this operation */ + ipc_entry_num_t actual; /* this many names */ + ipc_port_timestamp_t timestamp; /* logical time of this operation */ mach_port_name_t *names; mach_port_type_t *types; kern_return_t kr; - vm_size_t size; /* size of allocated memory */ - vm_offset_t addr1; /* allocated memory, for names */ - vm_offset_t addr2; /* allocated memory, for types */ - vm_map_copy_t memory1; /* copied-in memory, for names */ - vm_map_copy_t memory2; /* copied-in memory, for types */ + vm_size_t size; /* size of allocated memory */ + vm_offset_t addr1; /* allocated memory, for names */ + vm_offset_t addr2; /* allocated memory, for types */ + vm_map_copy_t memory1; /* copied-in memory, for names */ + vm_map_copy_t memory2; /* copied-in memory, for types */ /* safe simplifying assumption */ - assert_static(sizeof(mach_port_name_t) == sizeof(mach_port_type_t)); + static_assert(sizeof(mach_port_name_t) == sizeof(mach_port_type_t)); - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } size = 0; @@ -231,7 +233,7 @@ mach_port_names( vm_size_t size_needed; is_read_lock(space); - if (!space->is_active) { + if (!is_active(space)) { is_read_unlock(space); if (size != 0) { kmem_free(ipc_kernel_map, addr1, size); @@ -241,12 +243,14 @@ mach_port_names( } /* upper bound on number of names in the space */ + bound = space->is_table_size; + size_needed = vm_map_round_page( + (bound * sizeof(mach_port_name_t)), + VM_MAP_PAGE_MASK(ipc_kernel_map)); - bound = space->is_table_size + space->is_tree_total; - size_needed = round_page(bound * sizeof(mach_port_name_t)); - - if (size_needed <= size) + if (size_needed <= size) { break; + } is_read_unlock(space); @@ -256,11 +260,12 @@ mach_port_names( } size = size_needed; - kr = vm_allocate(ipc_kernel_map, &addr1, size, VM_FLAGS_ANYWHERE); - if (kr != KERN_SUCCESS) + kr = vm_allocate_kernel(ipc_kernel_map, &addr1, size, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IPC); + if (kr != KERN_SUCCESS) { return KERN_RESOURCE_SHORTAGE; + } - kr = vm_allocate(ipc_kernel_map, &addr2, size, VM_FLAGS_ANYWHERE); + kr = vm_allocate_kernel(ipc_kernel_map, &addr2, size, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IPC); if (kr != KERN_SUCCESS) { kmem_free(ipc_kernel_map, addr1, size); return KERN_RESOURCE_SHORTAGE; @@ -268,24 +273,34 @@ mach_port_names( /* can't fault while we hold locks */ - kr = vm_map_wire(ipc_kernel_map, vm_map_trunc_page(addr1), - vm_map_round_page(addr1 + size), - VM_PROT_READ|VM_PROT_WRITE, FALSE); + kr = vm_map_wire_kernel( + ipc_kernel_map, + vm_map_trunc_page(addr1, + VM_MAP_PAGE_MASK(ipc_kernel_map)), + vm_map_round_page(addr1 + size, + VM_MAP_PAGE_MASK(ipc_kernel_map)), + VM_PROT_READ | VM_PROT_WRITE, VM_KERN_MEMORY_IPC, + FALSE); if (kr != KERN_SUCCESS) { kmem_free(ipc_kernel_map, addr1, size); kmem_free(ipc_kernel_map, addr2, size); return KERN_RESOURCE_SHORTAGE; } - kr = vm_map_wire(ipc_kernel_map, vm_map_trunc_page(addr2), - vm_map_round_page(addr2 + size), - VM_PROT_READ|VM_PROT_WRITE, FALSE); + kr = vm_map_wire_kernel( + ipc_kernel_map, + vm_map_trunc_page(addr2, + VM_MAP_PAGE_MASK(ipc_kernel_map)), + vm_map_round_page(addr2 + size, + VM_MAP_PAGE_MASK(ipc_kernel_map)), + VM_PROT_READ | VM_PROT_WRITE, + VM_KERN_MEMORY_IPC, + FALSE); if (kr != KERN_SUCCESS) { kmem_free(ipc_kernel_map, addr1, size); kmem_free(ipc_kernel_map, addr2, size); return KERN_RESOURCE_SHORTAGE; } - } /* space is read-locked and active */ @@ -307,21 +322,10 @@ mach_port_names( name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits)); mach_port_names_helper(timestamp, entry, name, names, - types, &actual); + types, &actual); } } - for (tentry = ipc_splay_traverse_start(&space->is_tree); - tentry != ITE_NULL; - tentry = ipc_splay_traverse_next(&space->is_tree, FALSE)) { - ipc_entry_t entry = &tentry->ite_entry; - mach_port_name_t name = tentry->ite_name; - - assert(IE_BITS_TYPE(tentry->ite_bits) != MACH_PORT_TYPE_NONE); - mach_port_names_helper(timestamp, entry, name, names, - types, &actual); - } - ipc_splay_traverse_finish(&space->is_tree); is_read_unlock(space); if (actual == 0) { @@ -337,34 +341,46 @@ mach_port_names( vm_size_t vm_size_used; size_used = actual * sizeof(mach_port_name_t); - vm_size_used = round_page(size_used); + vm_size_used = + vm_map_round_page(size_used, + VM_MAP_PAGE_MASK(ipc_kernel_map)); /* * Make used memory pageable and get it into * copied-in form. Free any unused memory. */ - kr = vm_map_unwire(ipc_kernel_map, vm_map_trunc_page(addr1), - vm_map_round_page(addr1 + vm_size_used), FALSE); + kr = vm_map_unwire( + ipc_kernel_map, + vm_map_trunc_page(addr1, + VM_MAP_PAGE_MASK(ipc_kernel_map)), + vm_map_round_page(addr1 + vm_size_used, + VM_MAP_PAGE_MASK(ipc_kernel_map)), + FALSE); assert(kr == KERN_SUCCESS); - kr = vm_map_unwire(ipc_kernel_map, vm_map_trunc_page(addr2), - vm_map_round_page(addr2 + vm_size_used), FALSE); + kr = vm_map_unwire( + ipc_kernel_map, + vm_map_trunc_page(addr2, + VM_MAP_PAGE_MASK(ipc_kernel_map)), + vm_map_round_page(addr2 + vm_size_used, + VM_MAP_PAGE_MASK(ipc_kernel_map)), + FALSE); assert(kr == KERN_SUCCESS); kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr1, - (vm_map_size_t)size_used, TRUE, &memory1); + (vm_map_size_t)size_used, TRUE, &memory1); assert(kr == KERN_SUCCESS); kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr2, - (vm_map_size_t)size_used, TRUE, &memory2); + (vm_map_size_t)size_used, TRUE, &memory2); assert(kr == KERN_SUCCESS); if (vm_size_used != size) { kmem_free(ipc_kernel_map, - addr1 + vm_size_used, size - vm_size_used); + addr1 + vm_size_used, size - vm_size_used); kmem_free(ipc_kernel_map, - addr2 + vm_size_used, size - vm_size_used); + addr2 + vm_size_used, size - vm_size_used); } } @@ -400,19 +416,21 @@ mach_port_names( kern_return_t mach_port_type( - ipc_space_t space, - mach_port_name_t name, - mach_port_type_t *typep) + ipc_space_t space, + mach_port_name_t name, + mach_port_type_t *typep) { mach_port_urefs_t urefs; ipc_entry_t entry; kern_return_t kr; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (name == MACH_PORT_NULL) + if (name == MACH_PORT_NULL) { return KERN_INVALID_NAME; + } if (name == MACH_PORT_DEAD) { *typep = MACH_PORT_TYPE_DEAD_NAME; @@ -420,14 +438,19 @@ mach_port_type( } kr = ipc_right_lookup_write(space, name, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; - /* space is write-locked and active */ + } + /* space is write-locked and active */ kr = ipc_right_info(space, name, entry, typep, &urefs); - if (kr == KERN_SUCCESS) - is_write_unlock(space); /* space is unlocked */ + +#if 1 + /* JMM - workaround rdar://problem/9121297 (CF being too picky on these bits). */ + *typep &= ~(MACH_PORT_TYPE_SPREQUEST | MACH_PORT_TYPE_SPREQUEST_DELAYED); +#endif + return kr; } @@ -446,26 +469,21 @@ mach_port_type( * KERN_INVALID_VALUE The nname isn't a legal name. * KERN_NAME_EXISTS The nname already denotes a right. * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + * + * This interface is obsolete and always returns + * KERN_NOT_SUPPORTED. */ kern_return_t mach_port_rename( - ipc_space_t space, - mach_port_name_t oname, - mach_port_name_t nname) + __unused ipc_space_t space, + __unused mach_port_name_t oname, + __unused mach_port_name_t nname) { - if (space == IS_NULL) - return KERN_INVALID_TASK; - - if (!MACH_PORT_VALID(oname)) - return KERN_INVALID_NAME; - - if (!MACH_PORT_VALID(nname)) - return KERN_INVALID_VALUE; - - return ipc_object_rename(space, oname, nname); + return KERN_NOT_SUPPORTED; } + /* * Routine: mach_port_allocate_name [kernel call] * Purpose: @@ -503,21 +521,22 @@ mach_port_rename( kern_return_t mach_port_allocate_name( - ipc_space_t space, - mach_port_right_t right, - mach_port_name_t name) + ipc_space_t space, + mach_port_right_t right, + mach_port_name_t name) { - kern_return_t kr; - mach_port_qos_t qos = qos_template; + kern_return_t kr; + mach_port_qos_t qos = qos_template; qos.name = TRUE; - if (!MACH_PORT_VALID(name)) + if (!MACH_PORT_VALID(name)) { return KERN_INVALID_VALUE; + } - kr = mach_port_allocate_full (space, right, MACH_PORT_NULL, - &qos, &name); - return (kr); + kr = mach_port_allocate_full(space, right, MACH_PORT_NULL, + &qos, &name); + return kr; } /* @@ -540,24 +559,24 @@ mach_port_allocate_name( kern_return_t mach_port_allocate( - ipc_space_t space, - mach_port_right_t right, - mach_port_name_t *namep) + ipc_space_t space, + mach_port_right_t right, + mach_port_name_t *namep) { - kern_return_t kr; - mach_port_qos_t qos = qos_template; + kern_return_t kr; + mach_port_qos_t qos = qos_template; - kr = mach_port_allocate_full (space, right, MACH_PORT_NULL, - &qos, namep); - return (kr); + kr = mach_port_allocate_full(space, right, MACH_PORT_NULL, + &qos, namep); + return kr; } /* * Routine: mach_port_allocate_qos [kernel call] * Purpose: - * Allocates a right, with qos options, in a space. Like - * mach_port_allocate_name, except that the implementation - * picks a name for the right. The name may be any legal name + * Allocates a right, with qos options, in a space. Like + * mach_port_allocate_name, except that the implementation + * picks a name for the right. The name may be any legal name * in the space that doesn't currently denote a right. * Conditions: * Nothing locked. @@ -573,28 +592,33 @@ mach_port_allocate( kern_return_t mach_port_allocate_qos( - ipc_space_t space, - mach_port_right_t right, - mach_port_qos_t *qosp, - mach_port_name_t *namep) + ipc_space_t space, + mach_port_right_t right, + mach_port_qos_t *qosp, + mach_port_name_t *namep) { - kern_return_t kr; + kern_return_t kr; - if (qosp->name) + if (qosp->name) { return KERN_INVALID_ARGUMENT; - kr = mach_port_allocate_full (space, right, MACH_PORT_NULL, - qosp, namep); - return (kr); + } + kr = mach_port_allocate_full(space, right, MACH_PORT_NULL, + qosp, namep); + return kr; } /* * Routine: mach_port_allocate_full [kernel call] * Purpose: - * Allocates a right in a space. Supports all of the - * special cases, such as specifying a subsystem, - * a specific name, a real-time port, etc. - * The name may be any legal name in the space that doesn't + * Allocates a right in a space. Supports the + * special case of specifying a name. The name may + * be any legal name in the space that doesn't * currently denote a right. + * + * While we no longer support users requesting + * preallocated message for the port, we still + * check for errors in such requests and then + * just clear the request. * Conditions: * Nothing locked. * Returns: @@ -608,84 +632,139 @@ mach_port_allocate_qos( kern_return_t mach_port_allocate_full( - ipc_space_t space, - mach_port_right_t right, - mach_port_t proto, - mach_port_qos_t *qosp, - mach_port_name_t *namep) + ipc_space_t space, + mach_port_right_t right, + mach_port_t proto, + mach_port_qos_t *qosp, + mach_port_name_t *namep) { - ipc_kmsg_t kmsg = IKM_NULL; - kern_return_t kr; + kern_return_t kr; - if (space == IS_NULL) - return (KERN_INVALID_TASK); + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } - if (proto != MACH_PORT_NULL) - return (KERN_INVALID_VALUE); + if (proto != MACH_PORT_NULL) { + return KERN_INVALID_VALUE; + } if (qosp->name) { - if (!MACH_PORT_VALID (*namep)) - return (KERN_INVALID_VALUE); - if (is_fast_space (space)) - return (KERN_FAILURE); + if (!MACH_PORT_VALID(*namep)) { + return KERN_INVALID_VALUE; + } } + /* + * Don't actually honor prealloc requests from user-space + * (for security reasons, and because it isn't guaranteed anyway). + * Keep old errors for legacy reasons. + */ if (qosp->prealloc) { if (qosp->len > MACH_MSG_SIZE_MAX - MAX_TRAILER_SIZE) { return KERN_RESOURCE_SHORTAGE; - } else { - mach_msg_size_t size = qosp->len + MAX_TRAILER_SIZE; - if (right != MACH_PORT_RIGHT_RECEIVE) - return (KERN_INVALID_VALUE); - kmsg = (ipc_kmsg_t)ipc_kmsg_alloc(size); - if (kmsg == IKM_NULL) - return (KERN_RESOURCE_SHORTAGE); } + if (right != MACH_PORT_RIGHT_RECEIVE) { + return KERN_INVALID_VALUE; + } + qosp->prealloc = 0; } + kr = mach_port_allocate_internal(space, right, qosp, namep); + return kr; +} + + +/* + * Routine: mach_port_allocate_internal [kernel private] + * Purpose: + * Allocates a right in a space. Supports all of the + * special cases, a specific name, a real-time port, etc. + * The name may be any legal name in the space that doesn't + * currently denote a right. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS The right is allocated. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_VALUE "right" isn't a legal kind of right. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + * KERN_NO_SPACE No room in space for another right. + */ +kern_return_t +mach_port_allocate_internal( + ipc_space_t space, + mach_port_right_t right, + mach_port_qos_t *qosp, + mach_port_name_t *namep) +{ + kern_return_t kr; + + assert(space != IS_NULL); + switch (right) { - case MACH_PORT_RIGHT_RECEIVE: - { - ipc_port_t port; + case MACH_PORT_RIGHT_RECEIVE: + { + ipc_kmsg_t kmsg = IKM_NULL; + ipc_port_t port; + + /* + * For in-kernel uses, only allow small (from the kmsg zone) + * preallocated messages for the port. + */ + if (qosp->prealloc) { + mach_msg_size_t size = qosp->len; + + if (size > IKM_SAVED_MSG_SIZE - MAX_TRAILER_SIZE) { + panic("mach_port_allocate_internal: too large a prealloc kmsg"); + } + kmsg = (ipc_kmsg_t)ipc_kmsg_prealloc(size + MAX_TRAILER_SIZE); + if (kmsg == IKM_NULL) { + return KERN_RESOURCE_SHORTAGE; + } + } - if (qosp->name) + if (qosp->name) { kr = ipc_port_alloc_name(space, *namep, &port); - else - kr = ipc_port_alloc(space, namep, &port); + } else { + kr = ipc_port_alloc(space, FALSE, namep, &port); + } if (kr == KERN_SUCCESS) { - if (kmsg != IKM_NULL) + if (kmsg != IKM_NULL) { ipc_kmsg_set_prealloc(kmsg, port); - + } ip_unlock(port); - - } else if (kmsg != IKM_NULL) + } else if (kmsg != IKM_NULL) { ipc_kmsg_free(kmsg); + } break; - } + } - case MACH_PORT_RIGHT_PORT_SET: - { - ipc_pset_t pset; + case MACH_PORT_RIGHT_PORT_SET: + { + ipc_pset_t pset; - if (qosp->name) + if (qosp->name) { kr = ipc_pset_alloc_name(space, *namep, &pset); - else + } else { kr = ipc_pset_alloc(space, namep, &pset); - if (kr == KERN_SUCCESS) + } + if (kr == KERN_SUCCESS) { ips_unlock(pset); + } break; - } + } - case MACH_PORT_RIGHT_DEAD_NAME: + case MACH_PORT_RIGHT_DEAD_NAME: kr = ipc_object_alloc_dead(space, namep); break; - default: + default: kr = KERN_INVALID_VALUE; break; } - return (kr); + return kr; } /* @@ -707,25 +786,28 @@ mach_port_allocate_full( kern_return_t mach_port_destroy( - 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; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (!MACH_PORT_VALID(name)) + if (!MACH_PORT_VALID(name)) { return KERN_SUCCESS; + } kr = ipc_right_lookup_write(space, name, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME); return kr; + } /* space is write-locked and active */ - kr = ipc_right_destroy(space, name, entry); - is_write_unlock(space); + kr = ipc_right_destroy(space, name, entry, TRUE, 0); /* unlocks space */ return kr; } @@ -733,8 +815,8 @@ mach_port_destroy( * Routine: mach_port_deallocate [kernel call] * Purpose: * Deallocates a user reference from a send right, - * send-once right, or a dead-name right. May - * deallocate the right, if this is the last uref, + * send-once right, dead-name right or a port_set right. + * May deallocate the right, if this is the last uref, * and destroy the name, if it doesn't denote * other rights. * Conditions: @@ -749,21 +831,25 @@ mach_port_destroy( kern_return_t mach_port_deallocate( - 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; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (!MACH_PORT_VALID(name)) + if (!MACH_PORT_VALID(name)) { return KERN_SUCCESS; + } kr = ipc_right_lookup_write(space, name, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME); return kr; + } /* space is write-locked */ kr = ipc_right_dealloc(space, name, entry); /* unlocks space */ @@ -789,24 +875,26 @@ mach_port_deallocate( kern_return_t mach_port_get_refs( - ipc_space_t space, - mach_port_name_t name, - mach_port_right_t right, - mach_port_urefs_t *urefsp) + ipc_space_t space, + mach_port_name_t name, + mach_port_right_t right, + mach_port_urefs_t *urefsp) { mach_port_type_t type; mach_port_urefs_t urefs; ipc_entry_t entry; kern_return_t kr; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (right >= MACH_PORT_RIGHT_NUMBER) + if (right >= MACH_PORT_RIGHT_NUMBER) { return KERN_INVALID_VALUE; + } if (!MACH_PORT_VALID(name)) { - if (right == MACH_PORT_RIGHT_SEND || + if (right == MACH_PORT_RIGHT_SEND || right == MACH_PORT_RIGHT_SEND_ONCE) { *urefsp = 1; return KERN_SUCCESS; @@ -815,37 +903,41 @@ mach_port_get_refs( } kr = ipc_right_lookup_write(space, name, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } + /* space is write-locked and active */ + kr = ipc_right_info(space, name, entry, &type, &urefs); + /* space is unlocked */ - kr = ipc_right_info(space, name, entry, &type, &urefs); /* unlocks */ - if (kr != KERN_SUCCESS) - return kr; /* space is unlocked */ - is_write_unlock(space); + if (kr != KERN_SUCCESS) { + return kr; + } - if (type & MACH_PORT_TYPE(right)) + if (type & MACH_PORT_TYPE(right)) { switch (right) { - case MACH_PORT_RIGHT_SEND_ONCE: + case MACH_PORT_RIGHT_SEND_ONCE: assert(urefs == 1); - /* fall-through */ + /* fall-through */ - case MACH_PORT_RIGHT_PORT_SET: - case MACH_PORT_RIGHT_RECEIVE: + case MACH_PORT_RIGHT_PORT_SET: + case MACH_PORT_RIGHT_RECEIVE: *urefsp = 1; break; - case MACH_PORT_RIGHT_DEAD_NAME: - case MACH_PORT_RIGHT_SEND: + case MACH_PORT_RIGHT_DEAD_NAME: + case MACH_PORT_RIGHT_SEND: assert(urefs > 0); *urefsp = urefs; break; - default: + default: panic("mach_port_get_refs: strange rights"); } - else + } else { *urefsp = 0; + } return kr; } @@ -872,37 +964,142 @@ mach_port_get_refs( kern_return_t mach_port_mod_refs( - ipc_space_t space, - mach_port_name_t name, - mach_port_right_t right, - mach_port_delta_t delta) + ipc_space_t space, + mach_port_name_t name, + mach_port_right_t right, + mach_port_delta_t delta) { ipc_entry_t entry; kern_return_t kr; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (right >= MACH_PORT_RIGHT_NUMBER) + if (right >= MACH_PORT_RIGHT_NUMBER) { return KERN_INVALID_VALUE; + } if (!MACH_PORT_VALID(name)) { if (right == MACH_PORT_RIGHT_SEND || - right == MACH_PORT_RIGHT_SEND_ONCE) + right == MACH_PORT_RIGHT_SEND_ONCE) { return KERN_SUCCESS; + } return KERN_INVALID_NAME; } kr = ipc_right_lookup_write(space, name, &entry); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME); return kr; + } + /* space is write-locked and active */ - kr = ipc_right_delta(space, name, entry, right, delta); /* unlocks */ + kr = ipc_right_delta(space, name, entry, right, delta); /* unlocks */ return kr; } +/* + * Routine: mach_port_peek [kernel call] + * Purpose: + * Peek at the message queue for the specified receive + * right and return info about a message in the queue. + * + * On input, seqnop points to a sequence number value + * to match the message being peeked. If zero is specified + * as the seqno, the first message in the queue will be + * peeked. + * + * Only the following trailer types are currently supported: + * MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) + * + * or'ed with one of these element types: + * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_NULL) + * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SEQNO) + * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER) + * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) + * + * On input, the value pointed to by trailer_sizep must be + * large enough to hold the requested trailer size. + * + * The message sequence number, id, size, requested trailer info + * and requested trailer size are returned in their respective + * output parameters upon success. + * + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Matching message found, out parameters set. + * KERN_INVALID_TASK The space is null or dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + * KERN_INVALID_VALUE The input parameter values are out of bounds. + * KERN_FAILURE The requested message was not found. + */ + +kern_return_t +mach_port_peek( + ipc_space_t space, + mach_port_name_t name, + mach_msg_trailer_type_t trailer_type, + mach_port_seqno_t *seqnop, + mach_msg_size_t *msg_sizep, + mach_msg_id_t *msg_idp, + mach_msg_trailer_info_t trailer_infop, + mach_msg_type_number_t *trailer_sizep) +{ + ipc_port_t port; + kern_return_t kr; + boolean_t found; + mach_msg_max_trailer_t max_trailer; + + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } + + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_RIGHT; + } + + /* + * We don't allow anything greater than the audit trailer - to avoid + * leaking the context pointer and to avoid variable-sized context issues. + */ + if (GET_RCV_ELEMENTS(trailer_type) > MACH_RCV_TRAILER_AUDIT || + REQUESTED_TRAILER_SIZE(TRUE, trailer_type) > *trailer_sizep) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_VALUE); + return KERN_INVALID_VALUE; + } + + *trailer_sizep = REQUESTED_TRAILER_SIZE(TRUE, trailer_type); + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + mach_port_guard_exception(name, 0, 0, + ((KERN_INVALID_NAME == kr) ? + kGUARD_EXC_INVALID_NAME : + kGUARD_EXC_INVALID_RIGHT)); + return kr; + } + + /* Port locked and active */ + + found = ipc_mqueue_peek(&port->ip_messages, seqnop, + msg_sizep, msg_idp, &max_trailer, NULL); + ip_unlock(port); + + if (found != TRUE) { + return KERN_FAILURE; + } + + max_trailer.msgh_seqno = *seqnop; + memcpy(trailer_infop, &max_trailer, *trailer_sizep); + + return KERN_SUCCESS; +} + /* * Routine: mach_port_set_mscount [kernel call] * Purpose: @@ -919,26 +1116,28 @@ mach_port_mod_refs( kern_return_t mach_port_set_mscount( - ipc_space_t space, - mach_port_name_t name, - mach_port_mscount_t mscount) + ipc_space_t space, + mach_port_name_t name, + mach_port_mscount_t mscount) { ipc_port_t port; kern_return_t kr; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (!MACH_PORT_VALID(name)) + if (!MACH_PORT_VALID(name)) { return KERN_INVALID_RIGHT; + } kr = ipc_port_translate_receive(space, name, &port); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* port is locked and active */ - ipc_port_set_mscount(port, mscount); - + port->ip_mscount = mscount; ip_unlock(port); return KERN_SUCCESS; } @@ -959,22 +1158,25 @@ mach_port_set_mscount( kern_return_t mach_port_set_seqno( - ipc_space_t space, - mach_port_name_t name, - mach_port_seqno_t seqno) + ipc_space_t space, + mach_port_name_t name, + mach_port_seqno_t seqno) { ipc_port_t port; kern_return_t kr; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (!MACH_PORT_VALID(name)) + if (!MACH_PORT_VALID(name)) { return KERN_INVALID_RIGHT; + } kr = ipc_port_translate_receive(space, name, &port); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* port is locked and active */ ipc_mqueue_set_seqno(&port->ip_messages, seqno); @@ -984,41 +1186,107 @@ mach_port_set_seqno( } /* - * Routine: mach_port_gst_helper + * Routine: mach_port_get_context [kernel call] * Purpose: - * A helper function for mach_port_get_set_status. + * Returns a receive right's context pointer. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Set context pointer. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. */ -void -mach_port_gst_helper( - ipc_pset_t pset, - ipc_port_t port, - ipc_entry_num_t maxnames, - mach_port_name_t *names, - ipc_entry_num_t *actualp) +kern_return_t +mach_port_get_context( + ipc_space_t space, + mach_port_name_t name, + mach_vm_address_t *context) { - mach_port_name_t name; + ipc_port_t port; + kern_return_t kr; - assert(port != IP_NULL); + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } + + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_RIGHT; + } + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } - ip_lock(port); - assert(ip_active(port)); + /* Port locked and active */ - name = port->ip_receiver_name; - assert(name != MACH_PORT_NULL); + /* For strictly guarded ports, return empty context (which acts as guard) */ + if (port->ip_strict_guard) { + *context = 0; + } else { + *context = port->ip_context; + } ip_unlock(port); + return KERN_SUCCESS; +} + + +/* + * Routine: mach_port_set_context [kernel call] + * Purpose: + * Changes a receive right's context pointer. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Set context pointer. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_set_context( + ipc_space_t space, + mach_port_name_t name, + mach_vm_address_t context) +{ + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } - if (ipc_pset_member(pset, port)) { - ipc_entry_num_t actual = *actualp; + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_RIGHT; + } - if (actual < maxnames) - names[actual] = name; + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } - *actualp = actual+1; + /* port is locked and active */ + if (port->ip_strict_guard) { + uint64_t portguard = port->ip_context; + ip_unlock(port); + /* For strictly guarded ports, disallow overwriting context; Raise Exception */ + mach_port_guard_exception(name, context, portguard, kGUARD_EXC_SET_CONTEXT); + return KERN_INVALID_ARGUMENT; } + + port->ip_context = context; + + ip_unlock(port); + return KERN_SUCCESS; } + /* * Routine: mach_port_get_set_status [kernel call] * Purpose: @@ -1037,105 +1305,74 @@ mach_port_gst_helper( kern_return_t mach_port_get_set_status( - ipc_space_t space, - mach_port_name_t name, - mach_port_name_t **members, - mach_msg_type_number_t *membersCnt) + ipc_space_t space, + mach_port_name_t name, + mach_port_name_t **members, + mach_msg_type_number_t *membersCnt) { - ipc_entry_num_t actual; /* this many members */ - ipc_entry_num_t maxnames; /* space for this many members */ + ipc_entry_num_t actual; /* this many members */ + ipc_entry_num_t maxnames; /* space for this many members */ kern_return_t kr; - vm_size_t size; /* size of allocated memory */ - vm_offset_t addr; /* allocated memory */ - vm_map_copy_t memory; /* copied-in memory */ + vm_size_t size; /* size of allocated memory */ + vm_offset_t addr; /* allocated memory */ + vm_map_copy_t memory; /* copied-in memory */ - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (!MACH_PORT_VALID(name)) + if (!MACH_PORT_VALID(name)) { return KERN_INVALID_RIGHT; + } - size = PAGE_SIZE; /* initial guess */ + size = VM_MAP_PAGE_SIZE(ipc_kernel_map); /* initial guess */ for (;;) { - ipc_tree_entry_t tentry; - ipc_entry_t entry, table; - ipc_entry_num_t tsize; - mach_port_index_t index; mach_port_name_t *names; + ipc_object_t psobj; ipc_pset_t pset; - kr = vm_allocate(ipc_kernel_map, &addr, size, VM_FLAGS_ANYWHERE); - if (kr != KERN_SUCCESS) + kr = vm_allocate_kernel(ipc_kernel_map, &addr, size, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IPC); + if (kr != KERN_SUCCESS) { return KERN_RESOURCE_SHORTAGE; + } /* can't fault while we hold locks */ - kr = vm_map_wire(ipc_kernel_map, addr, addr + size, - VM_PROT_READ|VM_PROT_WRITE, FALSE); + kr = vm_map_wire_kernel(ipc_kernel_map, addr, addr + size, + VM_PROT_READ | VM_PROT_WRITE, VM_KERN_MEMORY_IPC, FALSE); assert(kr == KERN_SUCCESS); - kr = ipc_right_lookup_read(space, name, &entry); + kr = ipc_object_translate(space, name, MACH_PORT_RIGHT_PORT_SET, &psobj); if (kr != KERN_SUCCESS) { kmem_free(ipc_kernel_map, addr, size); return kr; } - /* space is read-locked and active */ - - if (IE_BITS_TYPE(entry->ie_bits) != MACH_PORT_TYPE_PORT_SET) { - is_read_unlock(space); - kmem_free(ipc_kernel_map, addr, size); - return KERN_INVALID_RIGHT; - } - pset = (ipc_pset_t) entry->ie_object; - assert(pset != IPS_NULL); - /* the port set must be active */ + /* just use a portset reference from here on out */ + pset = ips_object_to_pset(psobj); + ips_reference(pset); + ips_unlock(pset); names = (mach_port_name_t *) addr; - maxnames = size / sizeof(mach_port_name_t); - actual = 0; - - table = space->is_table; - tsize = space->is_table_size; - - for (index = 0; index < tsize; index++) { - ipc_entry_t ientry = &table[index]; - - if (ientry->ie_bits & MACH_PORT_TYPE_RECEIVE) { - ipc_port_t port = - (ipc_port_t) ientry->ie_object; - - mach_port_gst_helper(pset, port, - maxnames, names, &actual); - } - } + maxnames = (ipc_entry_num_t)(size / sizeof(mach_port_name_t)); - for (tentry = ipc_splay_traverse_start(&space->is_tree); - tentry != ITE_NULL; - tentry = ipc_splay_traverse_next(&space->is_tree,FALSE)) { - ipc_entry_bits_t bits = tentry->ite_bits; + ipc_mqueue_set_gather_member_names(space, &pset->ips_messages, maxnames, names, &actual); - assert(IE_BITS_TYPE(bits) != MACH_PORT_TYPE_NONE); + /* release the portset reference */ + ips_release(pset); - if (bits & MACH_PORT_TYPE_RECEIVE) { - ipc_port_t port = (ipc_port_t) tentry->ite_object; - - mach_port_gst_helper(pset, port, maxnames, - names, &actual); - } - } - ipc_splay_traverse_finish(&space->is_tree); - is_read_unlock(space); - - if (actual <= maxnames) + if (actual <= maxnames) { break; + } /* didn't have enough memory; allocate more */ - kmem_free(ipc_kernel_map, addr, size); - size = round_page(actual * sizeof(mach_port_name_t)) + PAGE_SIZE; + size = vm_map_round_page( + (actual * sizeof(mach_port_name_t)), + VM_MAP_PAGE_MASK(ipc_kernel_map)) + + VM_MAP_PAGE_SIZE(ipc_kernel_map); } if (actual == 0) { @@ -1147,24 +1384,32 @@ mach_port_get_set_status( vm_size_t vm_size_used; size_used = actual * sizeof(mach_port_name_t); - vm_size_used = round_page(size_used); + vm_size_used = vm_map_round_page( + size_used, + VM_MAP_PAGE_MASK(ipc_kernel_map)); /* * Make used memory pageable and get it into * copied-in form. Free any unused memory. */ - kr = vm_map_unwire(ipc_kernel_map, vm_map_trunc_page(addr), - vm_map_round_page(addr + vm_size_used), FALSE); + kr = vm_map_unwire( + ipc_kernel_map, + vm_map_trunc_page(addr, + VM_MAP_PAGE_MASK(ipc_kernel_map)), + vm_map_round_page(addr + vm_size_used, + VM_MAP_PAGE_MASK(ipc_kernel_map)), + FALSE); assert(kr == KERN_SUCCESS); kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr, - (vm_map_size_t)size_used, TRUE, &memory); + (vm_map_size_t)size_used, TRUE, &memory); assert(kr == KERN_SUCCESS); - if (vm_size_used != size) + if (vm_size_used != size) { kmem_free(ipc_kernel_map, - addr + vm_size_used, size - vm_size_used); + addr + vm_size_used, size - vm_size_used); + } } *members = (mach_port_name_t *) memory; @@ -1195,64 +1440,83 @@ mach_port_get_set_status( kern_return_t mach_port_move_member( - ipc_space_t space, - mach_port_name_t member, - mach_port_name_t after) + ipc_space_t space, + mach_port_name_t member, + mach_port_name_t after) { - ipc_entry_t entry; + ipc_object_t port_obj, ps_obj; ipc_port_t port; - ipc_pset_t nset; + ipc_pset_t nset = IPS_NULL; kern_return_t kr; + uint64_t wq_link_id = 0; + uint64_t wq_reserved_prepost = 0; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (!MACH_PORT_VALID(member)) - return KERN_INVALID_RIGHT; - - if (after == MACH_PORT_DEAD) - return KERN_INVALID_RIGHT; - - kr = ipc_right_lookup_read(space, member, &entry); - if (kr != KERN_SUCCESS) - return kr; - /* space is read-locked and active */ - - if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0) { - is_read_unlock(space); + if (!MACH_PORT_VALID(member)) { return KERN_INVALID_RIGHT; } - port = (ipc_port_t) entry->ie_object; - assert(port != IP_NULL); - - if (after == MACH_PORT_NULL) - nset = IPS_NULL; - else { - entry = ipc_entry_lookup(space, after); - if (entry == IE_NULL) { - is_read_unlock(space); - return KERN_INVALID_NAME; + if (after == MACH_PORT_DEAD) { + return KERN_INVALID_RIGHT; + } else if (after == MACH_PORT_NULL) { + wq_link_id = 0; + } else { + /* + * We reserve both a link, and + * enough prepost objects to complete + * the set move atomically - we can't block + * while we're holding the space lock, and + * the ipc_pset_add calls ipc_mqueue_add + * which may have to prepost this port onto + * this set. + */ + wq_link_id = waitq_link_reserve(NULL); + wq_reserved_prepost = waitq_prepost_reserve(NULL, 10, + WAITQ_DONT_LOCK); + kr = ipc_pset_lazy_allocate(space, after); + if (kr != KERN_SUCCESS) { + goto done; } + } - if ((entry->ie_bits & MACH_PORT_TYPE_PORT_SET) == 0) { - is_read_unlock(space); - return KERN_INVALID_RIGHT; - } + if (after != MACH_PORT_NULL) { + kr = ipc_object_translate_two(space, + member, MACH_PORT_RIGHT_RECEIVE, &port_obj, + after, MACH_PORT_RIGHT_PORT_SET, &ps_obj); + } else { + kr = ipc_object_translate(space, + member, MACH_PORT_RIGHT_RECEIVE, &port_obj); + } + if (kr != KERN_SUCCESS) { + goto done; + } - nset = (ipc_pset_t) entry->ie_object; - assert(nset != IPS_NULL); + port = ip_object_to_port(port_obj); + if (after != MACH_PORT_NULL) { + nset = ips_object_to_pset(ps_obj); } - ip_lock(port); + /* port and nset are locked */ + ipc_pset_remove_from_all(port); - if (nset != IPS_NULL) { - ips_lock(nset); - kr = ipc_pset_add(nset, port); + if (after != MACH_PORT_NULL) { + kr = ipc_pset_add(nset, port, &wq_link_id, &wq_reserved_prepost); ips_unlock(nset); } + ip_unlock(port); - is_read_unlock(space); + +done: + /* + * on success the ipc_pset_add() will consume the wq_link_id + * value (resetting it to 0), so this function is always safe to call. + */ + waitq_link_release(wq_link_id); + waitq_prepost_release_reserve(wq_reserved_prepost); + return kr; } @@ -1292,6 +1556,7 @@ mach_port_move_member( * KERN_INVALID_CAPABILITY The notify port is dead. * MACH_NOTIFY_PORT_DESTROYED: * KERN_INVALID_VALUE Sync isn't zero. + * KERN_FAILURE Re-registering for this notification * MACH_NOTIFY_DEAD_NAME: * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. * KERN_INVALID_ARGUMENT Name denotes dead name, but @@ -1302,101 +1567,135 @@ mach_port_move_member( kern_return_t mach_port_request_notification( - ipc_space_t space, - mach_port_name_t name, - mach_msg_id_t id, - mach_port_mscount_t sync, - ipc_port_t notify, - ipc_port_t *previousp) + ipc_space_t space, + mach_port_name_t name, + mach_msg_id_t id, + mach_port_mscount_t sync, + ipc_port_t notify, + ipc_port_t *previousp) { kern_return_t kr; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (notify == IP_DEAD) + if (notify == IP_DEAD) { return KERN_INVALID_CAPABILITY; + } -#if NOTYET +#if NOTYET /* * Requesting notifications on RPC ports is an error. */ { ipc_port_t port; - ipc_entry_t entry; + ipc_entry_t entry; - kr = ipc_right_lookup_write(space, name, &entry); - if (kr != KERN_SUCCESS) + kr = ipc_right_lookup_write(space, name, &entry); + if (kr != KERN_SUCCESS) { return kr; + } - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); if (port->ip_subsystem != NULL) { is_write_unlock(space); - panic("mach_port_request_notification: on RPC port!!"); + panic("mach_port_request_notification: on RPC port!!"); return KERN_INVALID_CAPABILITY; } is_write_unlock(space); } -#endif /* NOTYET */ +#endif /* NOTYET */ switch (id) { - case MACH_NOTIFY_PORT_DESTROYED: { - ipc_port_t port, previous; + case MACH_NOTIFY_PORT_DESTROYED: { + ipc_port_t port; - if (sync != 0) + if (sync != 0) { return KERN_INVALID_VALUE; + } - if (!MACH_PORT_VALID(name)) + if (!MACH_PORT_VALID(name)) { return KERN_INVALID_RIGHT; + } kr = ipc_port_translate_receive(space, name, &port); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* port is locked and active */ - ipc_port_pdrequest(port, notify, &previous); - /* port is unlocked */ + /* you cannot register for port death notifications on a kobject */ + if (ip_kotype(port) != IKOT_NONE) { + ip_unlock(port); + return KERN_INVALID_RIGHT; + } + + /* Allow only one registeration of this notification */ + if (port->ip_pdrequest != IP_NULL) { + ip_unlock(port); + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_KERN_FAILURE); + return KERN_FAILURE; + } - *previousp = previous; + ipc_port_pdrequest(port, notify, previousp); + /* port is unlocked */ + assert(*previousp == IP_NULL); break; - } + } - case MACH_NOTIFY_NO_SENDERS: { + case MACH_NOTIFY_NO_SENDERS: { ipc_port_t port; - if (!MACH_PORT_VALID(name)) + if (!MACH_PORT_VALID(name)) { return KERN_INVALID_RIGHT; + } kr = ipc_port_translate_receive(space, name, &port); - if (kr != KERN_SUCCESS) + if (kr != KERN_SUCCESS) { return kr; + } /* port is locked and active */ ipc_port_nsrequest(port, sync, notify, previousp); /* port is unlocked */ break; - } + } + + case MACH_NOTIFY_SEND_POSSIBLE: + + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_ARGUMENT; + } + + kr = ipc_right_request_alloc(space, name, sync != 0, + TRUE, notify, previousp); + if (kr != KERN_SUCCESS) { + return kr; + } + break; - case MACH_NOTIFY_DEAD_NAME: + case MACH_NOTIFY_DEAD_NAME: - if (!MACH_PORT_VALID(name)) { + if (!MACH_PORT_VALID(name)) { /* * Already dead. * Should do immediate delivery check - * will do that in the near future. */ - return KERN_INVALID_ARGUMENT; + return KERN_INVALID_ARGUMENT; } - kr = ipc_right_dnrequest(space, name, sync != 0, - notify, previousp); - if (kr != KERN_SUCCESS) + kr = ipc_right_request_alloc(space, name, sync != 0, + FALSE, notify, previousp); + if (kr != KERN_SUCCESS) { return kr; + } break; - default: + default: return KERN_INVALID_VALUE; } @@ -1426,23 +1725,26 @@ mach_port_request_notification( kern_return_t mach_port_insert_right( - ipc_space_t space, - mach_port_name_t name, - ipc_port_t poly, - mach_msg_type_name_t polyPoly) + ipc_space_t space, + mach_port_name_t name, + ipc_port_t poly, + mach_msg_type_name_t polyPoly) { - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } if (!MACH_PORT_VALID(name) || - !MACH_MSG_TYPE_PORT_ANY_RIGHT(polyPoly)) + !MACH_MSG_TYPE_PORT_ANY_RIGHT(polyPoly)) { return KERN_INVALID_VALUE; + } - if (!IO_VALID((ipc_object_t) poly)) + if (!IP_VALID(poly)) { return KERN_INVALID_CAPABILITY; + } - return ipc_object_copyout_name(space, (ipc_object_t) poly, - polyPoly, FALSE, name); + return ipc_object_copyout_name(space, ip_to_object(poly), + polyPoly, name); } /* @@ -1463,19 +1765,21 @@ mach_port_insert_right( kern_return_t mach_port_extract_right( - ipc_space_t space, - mach_port_name_t name, - mach_msg_type_name_t msgt_name, - ipc_port_t *poly, - mach_msg_type_name_t *polyPoly) + ipc_space_t space, + mach_port_name_t name, + mach_msg_type_name_t msgt_name, + ipc_port_t *poly, + mach_msg_type_name_t *polyPoly) { kern_return_t kr; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (!MACH_MSG_TYPE_PORT_ANY(msgt_name)) + if (!MACH_MSG_TYPE_PORT_ANY(msgt_name)) { return KERN_INVALID_VALUE; + } if (!MACH_PORT_VALID(name)) { /* @@ -1486,180 +1790,347 @@ mach_port_extract_right( return KERN_INVALID_RIGHT; } - kr = ipc_object_copyin(space, name, msgt_name, (ipc_object_t *) poly); + kr = ipc_object_copyin(space, name, msgt_name, (ipc_object_t *) poly, 0, NULL, + IPC_KMSG_FLAGS_ALLOW_IMMOVABLE_SEND); - if (kr == KERN_SUCCESS) + if (kr == KERN_SUCCESS) { *polyPoly = ipc_object_copyin_type(msgt_name); + } return kr; } +/* + * Routine: mach_port_get_status_helper [helper] + * Purpose: + * Populates a mach_port_status_t structure with + * port information. + * Conditions: + * Port needs to be locked + * Returns: + * None. + */ +static void +mach_port_get_status_helper( + ipc_port_t port, + mach_port_status_t *statusp) +{ + imq_lock(&port->ip_messages); + /* don't leak set IDs, just indicate that the port is in one or not */ + statusp->mps_pset = !!(port->ip_in_pset); + statusp->mps_seqno = port->ip_messages.imq_seqno; + statusp->mps_qlimit = port->ip_messages.imq_qlimit; + statusp->mps_msgcount = port->ip_messages.imq_msgcount; + imq_unlock(&port->ip_messages); + + statusp->mps_mscount = port->ip_mscount; + statusp->mps_sorights = port->ip_sorights; + statusp->mps_srights = port->ip_srights > 0; + statusp->mps_pdrequest = port->ip_pdrequest != IP_NULL; + statusp->mps_nsrequest = port->ip_nsrequest != IP_NULL; + statusp->mps_flags = 0; + if (port->ip_impdonation) { + statusp->mps_flags |= MACH_PORT_STATUS_FLAG_IMP_DONATION; + if (port->ip_tempowner) { + statusp->mps_flags |= MACH_PORT_STATUS_FLAG_TEMPOWNER; + if (IIT_NULL != port->ip_imp_task) { + statusp->mps_flags |= MACH_PORT_STATUS_FLAG_TASKPTR; + } + } + } + if (port->ip_guarded) { + statusp->mps_flags |= MACH_PORT_STATUS_FLAG_GUARDED; + if (port->ip_strict_guard) { + statusp->mps_flags |= MACH_PORT_STATUS_FLAG_STRICT_GUARD; + } + if (port->ip_immovable_receive) { + statusp->mps_flags |= MACH_PORT_STATUS_FLAG_GUARD_IMMOVABLE_RECEIVE; + } + } + if (port->ip_no_grant) { + statusp->mps_flags |= MACH_PORT_STATUS_FLAG_NO_GRANT; + } + return; +} + + kern_return_t mach_port_get_attributes( - ipc_space_t space, - mach_port_name_t name, - int flavor, - mach_port_info_t info, - mach_msg_type_number_t *count) + ipc_space_t space, + mach_port_name_t name, + int flavor, + mach_port_info_t info, + mach_msg_type_number_t *count) { ipc_port_t port; kern_return_t kr; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - switch (flavor) { - case MACH_PORT_LIMITS_INFO: { - mach_port_limits_t *lp = (mach_port_limits_t *)info; + switch (flavor) { + case MACH_PORT_LIMITS_INFO: { + mach_port_limits_t *lp = (mach_port_limits_t *)info; - if (*count < MACH_PORT_LIMITS_INFO_COUNT) - return KERN_FAILURE; + if (*count < MACH_PORT_LIMITS_INFO_COUNT) { + return KERN_FAILURE; + } - if (!MACH_PORT_VALID(name)) { + if (!MACH_PORT_VALID(name)) { *count = 0; break; } - - kr = ipc_port_translate_receive(space, name, &port); - if (kr != KERN_SUCCESS) - return kr; - /* port is locked and active */ - - lp->mpl_qlimit = port->ip_messages.imq_qlimit; - *count = MACH_PORT_LIMITS_INFO_COUNT; - ip_unlock(port); - break; - } - - case MACH_PORT_RECEIVE_STATUS: { - mach_port_status_t *statusp = (mach_port_status_t *)info; - spl_t s; - - if (*count < MACH_PORT_RECEIVE_STATUS_COUNT) - return KERN_FAILURE; - - if (!MACH_PORT_VALID(name)) + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } + /* port is locked and active */ + + lp->mpl_qlimit = port->ip_messages.imq_qlimit; + *count = MACH_PORT_LIMITS_INFO_COUNT; + ip_unlock(port); + break; + } + + case MACH_PORT_RECEIVE_STATUS: { + mach_port_status_t *statusp = (mach_port_status_t *)info; + + if (*count < MACH_PORT_RECEIVE_STATUS_COUNT) { + return KERN_FAILURE; + } + + if (!MACH_PORT_VALID(name)) { return KERN_INVALID_RIGHT; + } + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } + /* port is locked and active */ + mach_port_get_status_helper(port, statusp); + *count = MACH_PORT_RECEIVE_STATUS_COUNT; + ip_unlock(port); + break; + } - kr = ipc_port_translate_receive(space, name, &port); - if (kr != KERN_SUCCESS) - return kr; - /* port is locked and active */ - - statusp->mps_pset = port->ip_pset_count; - - s = splsched(); - imq_lock(&port->ip_messages); - statusp->mps_seqno = port->ip_messages.imq_seqno; - statusp->mps_qlimit = port->ip_messages.imq_qlimit; - statusp->mps_msgcount = port->ip_messages.imq_msgcount; - imq_unlock(&port->ip_messages); - splx(s); - - statusp->mps_mscount = port->ip_mscount; - statusp->mps_sorights = port->ip_sorights; - statusp->mps_srights = port->ip_srights > 0; - statusp->mps_pdrequest = port->ip_pdrequest != IP_NULL; - statusp->mps_nsrequest = port->ip_nsrequest != IP_NULL; - statusp->mps_flags = 0; - - *count = MACH_PORT_RECEIVE_STATUS_COUNT; - ip_unlock(port); - break; - } - case MACH_PORT_DNREQUESTS_SIZE: { - ipc_port_request_t table; - - if (*count < MACH_PORT_DNREQUESTS_SIZE_COUNT) - return KERN_FAILURE; + ipc_port_request_t table; + + if (*count < MACH_PORT_DNREQUESTS_SIZE_COUNT) { + return KERN_FAILURE; + } if (!MACH_PORT_VALID(name)) { *(int *)info = 0; break; } - kr = ipc_port_translate_receive(space, name, &port); - if (kr != KERN_SUCCESS) - return kr; - /* port is locked and active */ - - table = port->ip_dnrequests; - if (table == IPR_NULL) + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } + /* port is locked and active */ + + table = port->ip_requests; + if (table == IPR_NULL) { *(int *)info = 0; - else + } else { *(int *)info = table->ipr_size->its_size; - *count = MACH_PORT_DNREQUESTS_SIZE_COUNT; - ip_unlock(port); + } + *count = MACH_PORT_DNREQUESTS_SIZE_COUNT; + ip_unlock(port); + break; + } + + case MACH_PORT_INFO_EXT: { + mach_port_info_ext_t *mp_info = (mach_port_info_ext_t *)info; + if (*count < MACH_PORT_INFO_EXT_COUNT) { + return KERN_FAILURE; + } + + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_RIGHT; + } + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } + /* port is locked and active */ + mach_port_get_status_helper(port, &mp_info->mpie_status); + mp_info->mpie_boost_cnt = port->ip_impcount; + *count = MACH_PORT_INFO_EXT_COUNT; + ip_unlock(port); break; } - default: + default: return KERN_INVALID_ARGUMENT; - /*NOTREACHED*/ - } + /*NOTREACHED*/ + } return KERN_SUCCESS; } kern_return_t mach_port_set_attributes( - ipc_space_t space, - mach_port_name_t name, - int flavor, - mach_port_info_t info, - mach_msg_type_number_t count) + ipc_space_t space, + mach_port_name_t name, + int flavor, + mach_port_info_t info, + mach_msg_type_number_t count) { ipc_port_t port; kern_return_t kr; - - if (space == IS_NULL) + + if (space == IS_NULL) { return KERN_INVALID_TASK; + } + + switch (flavor) { + case MACH_PORT_LIMITS_INFO: { + mach_port_limits_t *mplp = (mach_port_limits_t *)info; + + if (count < MACH_PORT_LIMITS_INFO_COUNT) { + return KERN_FAILURE; + } + + if (mplp->mpl_qlimit > MACH_PORT_QLIMIT_MAX) { + return KERN_INVALID_VALUE; + } - switch (flavor) { - - case MACH_PORT_LIMITS_INFO: { - mach_port_limits_t *mplp = (mach_port_limits_t *)info; - - if (count < MACH_PORT_LIMITS_INFO_COUNT) - return KERN_FAILURE; - - if (mplp->mpl_qlimit > MACH_PORT_QLIMIT_MAX) - return KERN_INVALID_VALUE; - - if (!MACH_PORT_VALID(name)) + if (!MACH_PORT_VALID(name)) { return KERN_INVALID_RIGHT; + } - kr = ipc_port_translate_receive(space, name, &port); - if (kr != KERN_SUCCESS) - return kr; - /* port is locked and active */ + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } + /* port is locked and active */ - ipc_mqueue_set_qlimit(&port->ip_messages, mplp->mpl_qlimit); - ip_unlock(port); - break; - } + ipc_mqueue_set_qlimit(&port->ip_messages, mplp->mpl_qlimit); + ip_unlock(port); + break; + } case MACH_PORT_DNREQUESTS_SIZE: { - if (count < MACH_PORT_DNREQUESTS_SIZE_COUNT) - return KERN_FAILURE; + if (count < MACH_PORT_DNREQUESTS_SIZE_COUNT) { + return KERN_FAILURE; + } - if (!MACH_PORT_VALID(name)) + if (!MACH_PORT_VALID(name)) { return KERN_INVALID_RIGHT; - - kr = ipc_port_translate_receive(space, name, &port); - if (kr != KERN_SUCCESS) - return kr; - /* port is locked and active */ - - kr = ipc_port_dngrow(port, *(int *)info); - if (kr != KERN_SUCCESS) + } + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } + /* port is locked and active */ + + kr = ipc_port_request_grow(port, *(int *)info); + if (kr != KERN_SUCCESS) { return kr; + } break; } - default: + case MACH_PORT_TEMPOWNER: + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_RIGHT; + } + + ipc_importance_task_t release_imp_task = IIT_NULL; + natural_t assertcnt = 0; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } + /* port is locked and active */ + + /* + * don't allow temp-owner importance donation if user + * associated it with a kobject already (timer, host_notify target), + * or is a special reply port. + */ + if (ip_is_kobject(port) || port->ip_specialreply) { + ip_unlock(port); + return KERN_INVALID_ARGUMENT; + } + + if (port->ip_tempowner != 0) { + if (IIT_NULL != port->ip_imp_task) { + release_imp_task = port->ip_imp_task; + port->ip_imp_task = IIT_NULL; + assertcnt = port->ip_impcount; + } + } else { + assertcnt = port->ip_impcount; + } + + port->ip_impdonation = 1; + port->ip_tempowner = 1; + ip_unlock(port); + +#if IMPORTANCE_INHERITANCE + /* drop assertions from previous destination task */ + if (release_imp_task != IIT_NULL) { + assert(ipc_importance_task_is_any_receiver_type(release_imp_task)); + if (assertcnt > 0) { + ipc_importance_task_drop_internal_assertion(release_imp_task, assertcnt); + } + ipc_importance_task_release(release_imp_task); + } else if (assertcnt > 0) { + release_imp_task = current_task()->task_imp_base; + if (release_imp_task != IIT_NULL && + ipc_importance_task_is_any_receiver_type(release_imp_task)) { + ipc_importance_task_drop_internal_assertion(release_imp_task, assertcnt); + } + } +#else + if (release_imp_task != IIT_NULL) { + ipc_importance_task_release(release_imp_task); + } +#endif /* IMPORTANCE_INHERITANCE */ + + break; + +#if IMPORTANCE_INHERITANCE + case MACH_PORT_DENAP_RECEIVER: + case MACH_PORT_IMPORTANCE_RECEIVER: + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_RIGHT; + } + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } + + /* + * don't allow importance donation if user associated + * it with a kobject already (timer, host_notify target), + * or is a special reply port. + */ + if (ip_is_kobject(port) || port->ip_specialreply) { + ip_unlock(port); + return KERN_INVALID_ARGUMENT; + } + + /* port is locked and active */ + port->ip_impdonation = 1; + ip_unlock(port); + + break; +#endif /* IMPORTANCE_INHERITANCE */ + + default: return KERN_INVALID_ARGUMENT; - /*NOTREACHED*/ - } + /*NOTREACHED*/ + } return KERN_SUCCESS; } @@ -1684,33 +2155,55 @@ mach_port_set_attributes( kern_return_t mach_port_insert_member( - ipc_space_t space, - mach_port_name_t name, - mach_port_name_t psname) + ipc_space_t space, + mach_port_name_t name, + mach_port_name_t psname) { ipc_object_t obj; ipc_object_t psobj; kern_return_t kr; + uint64_t wq_link_id; + uint64_t wq_reserved_prepost; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (!MACH_PORT_VALID(name) || !MACH_PORT_VALID(psname)) + if (!MACH_PORT_VALID(name) || !MACH_PORT_VALID(psname)) { return KERN_INVALID_RIGHT; + } - kr = ipc_object_translate_two(space, - name, MACH_PORT_RIGHT_RECEIVE, &obj, - psname, MACH_PORT_RIGHT_PORT_SET, &psobj); - if (kr != KERN_SUCCESS) - return kr; + wq_link_id = waitq_link_reserve(NULL); + wq_reserved_prepost = waitq_prepost_reserve(NULL, 10, + WAITQ_DONT_LOCK); + kr = ipc_pset_lazy_allocate(space, psname); + if (kr != KERN_SUCCESS) { + goto done; + } + + + kr = ipc_object_translate_two(space, + name, MACH_PORT_RIGHT_RECEIVE, &obj, + psname, MACH_PORT_RIGHT_PORT_SET, &psobj); + if (kr != KERN_SUCCESS) { + goto done; + } /* obj and psobj are locked (and were locked in that order) */ assert(psobj != IO_NULL); assert(obj != IO_NULL); - kr = ipc_pset_add((ipc_pset_t)psobj, (ipc_port_t)obj); + kr = ipc_pset_add(ips_object_to_pset(psobj), ip_object_to_port(obj), + &wq_link_id, &wq_reserved_prepost); + io_unlock(psobj); io_unlock(obj); + +done: + /* on success, wq_link_id is reset to 0, so this is always safe */ + waitq_link_release(wq_link_id); + waitq_prepost_release_reserve(wq_reserved_prepost); + return kr; } @@ -1734,33 +2227,38 @@ mach_port_insert_member( kern_return_t mach_port_extract_member( - ipc_space_t space, - mach_port_name_t name, - mach_port_name_t psname) + ipc_space_t space, + mach_port_name_t name, + mach_port_name_t psname) { ipc_object_t psobj; ipc_object_t obj; kern_return_t kr; - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_INVALID_TASK; + } - if (!MACH_PORT_VALID(name) || !MACH_PORT_VALID(psname)) + if (!MACH_PORT_VALID(name) || !MACH_PORT_VALID(psname)) { return KERN_INVALID_RIGHT; + } - kr = ipc_object_translate_two(space, - name, MACH_PORT_RIGHT_RECEIVE, &obj, - psname, MACH_PORT_RIGHT_PORT_SET, &psobj); - if (kr != KERN_SUCCESS) + kr = ipc_object_translate_two(space, + name, MACH_PORT_RIGHT_RECEIVE, &obj, + psname, MACH_PORT_RIGHT_PORT_SET, &psobj); + if (kr != KERN_SUCCESS) { return kr; + } /* obj and psobj are both locked (and were locked in that order) */ assert(psobj != IO_NULL); assert(obj != IO_NULL); - kr = ipc_pset_remove((ipc_pset_t)psobj, (ipc_port_t)obj); + kr = ipc_pset_remove(ips_object_to_pset(psobj), ip_object_to_port(obj)); + io_unlock(psobj); io_unlock(obj); + return kr; } @@ -1771,15 +2269,585 @@ mach_port_extract_member( */ kern_return_t task_set_port_space( - ipc_space_t space, - int table_entries) + ipc_space_t space, + int table_entries) { kern_return_t kr; - + + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } + is_write_lock(space); + + if (!is_active(space)) { + is_write_unlock(space); + return KERN_INVALID_TASK; + } + kr = ipc_entry_grow_table(space, table_entries); - if (kr == KERN_SUCCESS) + if (kr == KERN_SUCCESS) { is_write_unlock(space); + } + return kr; +} + +/* + * Routine: mach_port_guard_locked [helper routine] + * Purpose: + * Sets a new guard for a locked port. + * Conditions: + * Port Locked. + * Returns: + * KERN_SUCCESS Port Guarded. + * KERN_INVALID_ARGUMENT Port already contains a context/guard. + */ +static kern_return_t +mach_port_guard_locked( + ipc_port_t port, + uint64_t guard, + uint64_t flags) +{ + if (port->ip_context) { + return KERN_INVALID_ARGUMENT; + } + + int strict = (flags & MPG_STRICT)? 1 : 0; + int immovable_receive = (flags & MPG_IMMOVABLE_RECEIVE)? 1 : 0; + + imq_lock(&port->ip_messages); + port->ip_context = guard; + port->ip_guarded = 1; + port->ip_strict_guard = strict; + /* ip_immovable_receive bit is sticky and can't be un-guarded */ + if (!port->ip_immovable_receive) { + port->ip_immovable_receive = immovable_receive; + } + imq_unlock(&port->ip_messages); + + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_unguard_locked [helper routine] + * Purpose: + * Removes guard for a locked port. + * Conditions: + * Port Locked. + * Returns: + * KERN_SUCCESS Port Unguarded. + * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch. + * This also raises a EXC_GUARD exception. + */ +static kern_return_t +mach_port_unguard_locked( + ipc_port_t port, + mach_port_name_t name, + uint64_t guard) +{ + /* Port locked and active */ + if (!port->ip_guarded) { + /* Port already unguarded; Raise exception */ + mach_port_guard_exception(name, guard, 0, kGUARD_EXC_UNGUARDED); + return KERN_INVALID_ARGUMENT; + } + + if (port->ip_context != guard) { + /* Incorrect guard; Raise exception */ + mach_port_guard_exception(name, guard, port->ip_context, kGUARD_EXC_INCORRECT_GUARD); + return KERN_INVALID_ARGUMENT; + } + + imq_lock(&port->ip_messages); + port->ip_context = 0; + port->ip_guarded = port->ip_strict_guard = 0; + /* Don't clear the ip_immovable_receive bit */ + imq_unlock(&port->ip_messages); + + return KERN_SUCCESS; +} + + +/* + * Routine: mach_port_guard_exception [helper routine] + * Purpose: + * Marks the thread with AST_GUARD for mach port guard violation. + * Also saves exception info in thread structure. + * Conditions: + * None. + * Returns: + * KERN_FAILURE Thread marked with AST_GUARD. + */ +void +mach_port_guard_exception( + mach_port_name_t name, + __unused uint64_t inguard, + uint64_t portguard, + unsigned reason) +{ + mach_exception_code_t code = 0; + EXC_GUARD_ENCODE_TYPE(code, GUARD_TYPE_MACH_PORT); + EXC_GUARD_ENCODE_FLAVOR(code, reason); + EXC_GUARD_ENCODE_TARGET(code, name); + mach_exception_subcode_t subcode = (uint64_t)portguard; + thread_t t = current_thread(); + boolean_t fatal = FALSE; + if (t->task->task_exc_guard & TASK_EXC_GUARD_MP_FATAL) { + fatal = TRUE; + } else if (reason <= MAX_FATAL_kGUARD_EXC_CODE) { + fatal = TRUE; + } + thread_guard_violation(t, code, subcode, fatal); +} + + +/* + * Routine: mach_port_guard_ast + * Purpose: + * Raises an exception for mach port guard violation. + * Conditions: + * None. + * Returns: + * None. + */ + +void +mach_port_guard_ast(thread_t t, + mach_exception_data_type_t code, mach_exception_data_type_t subcode) +{ + unsigned int reason = EXC_GUARD_DECODE_GUARD_FLAVOR(code); + task_t task = t->task; + unsigned int behavior = task->task_exc_guard; + assert(task == current_task()); + assert(task != kernel_task); + + switch (reason) { + /* + * Fatal Mach port guards - always delivered synchronously + */ + case kGUARD_EXC_DESTROY: + case kGUARD_EXC_MOD_REFS: + case kGUARD_EXC_SET_CONTEXT: + case kGUARD_EXC_UNGUARDED: + case kGUARD_EXC_INCORRECT_GUARD: + case kGUARD_EXC_IMMOVABLE: + case kGUARD_EXC_STRICT_REPLY: + task_exception_notify(EXC_GUARD, code, subcode); + task_bsdtask_kill(task); + break; + + default: + /* + * Mach port guards controlled by task settings. + */ + + /* Is delivery enabled */ + if ((behavior & TASK_EXC_GUARD_MP_DELIVER) == 0) { + return; + } + + /* If only once, make sure we're that once */ + while (behavior & TASK_EXC_GUARD_MP_ONCE) { + uint32_t new_behavior = behavior & ~TASK_EXC_GUARD_MP_DELIVER; + + if (OSCompareAndSwap(behavior, new_behavior, &task->task_exc_guard)) { + break; + } + behavior = task->task_exc_guard; + if ((behavior & TASK_EXC_GUARD_MP_DELIVER) == 0) { + return; + } + } + + /* Raise exception via corpse fork or synchronously */ + if ((task->task_exc_guard & TASK_EXC_GUARD_MP_CORPSE) && + (task->task_exc_guard & TASK_EXC_GUARD_MP_FATAL) == 0) { + task_violated_guard(code, subcode, NULL); + } else { + task_exception_notify(EXC_GUARD, code, subcode); + } + + /* Terminate the task if desired */ + if (task->task_exc_guard & TASK_EXC_GUARD_MP_FATAL) { + task_bsdtask_kill(task); + } + break; + } +} + +/* + * Routine: mach_port_construct [kernel call] + * Purpose: + * Constructs a mach port with the provided set of options. + * Conditions: + * None. + * Returns: + * KERN_SUCCESS The right is allocated. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + * KERN_NO_SPACE No room in space for another right. + * KERN_FAILURE Illegal option values requested. + */ + +kern_return_t +mach_port_construct( + ipc_space_t space, + mach_port_options_t *options, + uint64_t context, + mach_port_name_t *name) +{ + kern_return_t kr; + ipc_port_t port; + + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } + + /* Allocate a new port in the IPC space */ + kr = ipc_port_alloc(space, (options->flags & MPO_INSERT_SEND_RIGHT), + name, &port); + if (kr != KERN_SUCCESS) { + return kr; + } + + /* Port locked and active */ + if (options->flags & MPO_CONTEXT_AS_GUARD) { + uint64_t flags = 0; + if (options->flags & MPO_STRICT) { + flags |= MPG_STRICT; + } + if (options->flags & MPO_IMMOVABLE_RECEIVE) { + flags |= MPG_IMMOVABLE_RECEIVE; + } + kr = mach_port_guard_locked(port, (uint64_t) context, flags); + /* A newly allocated and locked port should always be guarded successfully */ + assert(kr == KERN_SUCCESS); + } else { + port->ip_context = context; + } + + /* Unlock port */ + ip_unlock(port); + + /* Set port attributes as requested */ + + if (options->flags & MPO_QLIMIT) { + kr = mach_port_set_attributes(space, *name, MACH_PORT_LIMITS_INFO, + (mach_port_info_t)&options->mpl, sizeof(options->mpl) / sizeof(int)); + if (kr != KERN_SUCCESS) { + goto cleanup; + } + } + + if (options->flags & MPO_TEMPOWNER) { + kr = mach_port_set_attributes(space, *name, MACH_PORT_TEMPOWNER, NULL, 0); + if (kr != KERN_SUCCESS) { + goto cleanup; + } + } + + if (options->flags & MPO_IMPORTANCE_RECEIVER) { + kr = mach_port_set_attributes(space, *name, MACH_PORT_IMPORTANCE_RECEIVER, NULL, 0); + if (kr != KERN_SUCCESS) { + goto cleanup; + } + } + + if (options->flags & MPO_DENAP_RECEIVER) { + kr = mach_port_set_attributes(space, *name, MACH_PORT_DENAP_RECEIVER, NULL, 0); + if (kr != KERN_SUCCESS) { + goto cleanup; + } + } + + return KERN_SUCCESS; + +cleanup: + /* Attempt to destroy port. If its already destroyed by some other thread, we're done */ + (void) mach_port_destruct(space, *name, + (options->flags & MPO_INSERT_SEND_RIGHT) ? -1 : 0, context); + return kr; +} + +/* + * Routine: mach_port_destruct [kernel call] + * Purpose: + * Destroys a mach port with appropriate guard + * Conditions: + * None. + * Returns: + * KERN_SUCCESS The name is destroyed. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT The right isn't correct. + * KERN_INVALID_VALUE The delta for send right is incorrect. + * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch. + * This also raises a EXC_GUARD exception. + */ + +kern_return_t +mach_port_destruct( + ipc_space_t space, + mach_port_name_t name, + mach_port_delta_t srdelta, + uint64_t guard) +{ + kern_return_t kr; + ipc_entry_t entry; + + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } + + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_NAME; + } + + /* Remove reference for receive right */ + kr = ipc_right_lookup_write(space, name, &entry); + if (kr != KERN_SUCCESS) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME); + return kr; + } + /* space is write-locked and active */ + kr = ipc_right_destruct(space, name, entry, srdelta, guard); /* unlocks */ + + return kr; +} + +/* + * Routine: mach_port_guard [kernel call] + * Purpose: + * Guard a mach port with specified guard value. + * The context field of the port is used as the guard. + * Conditions: + * None. + * Returns: + * KERN_SUCCESS The name is destroyed. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT The right isn't correct. + * KERN_INVALID_ARGUMENT Port already contains a context/guard. + */ +kern_return_t +mach_port_guard( + ipc_space_t space, + mach_port_name_t name, + uint64_t guard, + boolean_t strict) +{ + kern_return_t kr; + ipc_port_t port; + uint64_t flags = 0; + + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } + + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_NAME; + } + + /* Guard can be applied only to receive rights */ + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + mach_port_guard_exception(name, 0, 0, + ((KERN_INVALID_NAME == kr) ? + kGUARD_EXC_INVALID_NAME : + kGUARD_EXC_INVALID_RIGHT)); + return kr; + } + + /* Port locked and active */ + if (strict) { + flags = MPG_STRICT; + } + + kr = mach_port_guard_locked(port, guard, flags); + ip_unlock(port); + + if (KERN_INVALID_ARGUMENT == kr) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_ARGUMENT); + } + + return kr; +} + +/* + * Routine: mach_port_unguard [kernel call] + * Purpose: + * Unguard a mach port with specified guard value. + * Conditions: + * None. + * Returns: + * KERN_SUCCESS The name is destroyed. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT The right isn't correct. + * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch. + * This also raises a EXC_GUARD exception. + */ +kern_return_t +mach_port_unguard( + ipc_space_t space, + mach_port_name_t name, + uint64_t guard) +{ + kern_return_t kr; + ipc_port_t port; + + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } + + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_NAME; + } + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + mach_port_guard_exception(name, 0, 0, + ((KERN_INVALID_NAME == kr) ? + kGUARD_EXC_INVALID_NAME : + kGUARD_EXC_INVALID_RIGHT)); + return kr; + } + + /* Port locked and active */ + kr = mach_port_unguard_locked(port, name, guard); + ip_unlock(port); + + return kr; +} + +/* + * Routine: mach_port_guard_with_flags [kernel call] + * Purpose: + * Guard a mach port with specified guard value and guard flags. + * The context field of the port is used as the guard. + * Conditions: + * Should hold receive right for that port + * Returns: + * KERN_SUCCESS The name is destroyed. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT The right isn't correct. + * KERN_INVALID_ARGUMENT Port already contains a context/guard. + * KERN_INVALID_CAPABILITY Cannot set MPG_IMMOVABLE_RECEIVE flag for a port with + * a movable port-destroyed notification port + */ +kern_return_t +mach_port_guard_with_flags( + ipc_space_t space, + mach_port_name_t name, + uint64_t guard, + uint64_t flags) +{ + kern_return_t kr; + ipc_port_t port; + + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } + + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_NAME; + } + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + mach_port_guard_exception(name, 0, 0, + ((KERN_INVALID_NAME == kr) ? + kGUARD_EXC_INVALID_NAME : + kGUARD_EXC_INVALID_RIGHT)); + return kr; + } + + /* Port locked and active */ + kr = mach_port_guard_locked(port, guard, flags); + ip_unlock(port); + + if (KERN_INVALID_ARGUMENT == kr) { + mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_ARGUMENT); + } + return kr; } +/* + * Routine: mach_port_swap_guard [kernel call] + * Purpose: + * Swap guard value. + * Conditions: + * Port should already be guarded. + * Returns: + * KERN_SUCCESS The name is destroyed. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT The right isn't correct. + * KERN_INVALID_ARGUMENT Port doesn't contain a guard; is strictly guarded + * or the old_guard doesnt match the context + */ +kern_return_t +mach_port_swap_guard( + ipc_space_t space, + mach_port_name_t name, + uint64_t old_guard, + uint64_t new_guard) +{ + kern_return_t kr; + ipc_port_t port; + + if (space == IS_NULL) { + return KERN_INVALID_TASK; + } + + if (!MACH_PORT_VALID(name)) { + return KERN_INVALID_NAME; + } + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) { + mach_port_guard_exception(name, 0, 0, + ((KERN_INVALID_NAME == kr) ? + kGUARD_EXC_INVALID_NAME : + kGUARD_EXC_INVALID_RIGHT)); + return kr; + } + + /* Port locked and active */ + if (!port->ip_guarded) { + ip_unlock(port); + mach_port_guard_exception(name, old_guard, 0, kGUARD_EXC_UNGUARDED); + return KERN_INVALID_ARGUMENT; + } + + if (port->ip_strict_guard) { + uint64_t portguard = port->ip_context; + ip_unlock(port); + /* For strictly guarded ports, disallow overwriting context; Raise Exception */ + mach_port_guard_exception(name, old_guard, portguard, kGUARD_EXC_SET_CONTEXT); + return KERN_INVALID_ARGUMENT; + } + + if (port->ip_context != old_guard) { + uint64_t portguard = port->ip_context; + ip_unlock(port); + mach_port_guard_exception(name, old_guard, portguard, kGUARD_EXC_INCORRECT_GUARD); + return KERN_INVALID_ARGUMENT; + } + + imq_lock(&port->ip_messages); + port->ip_context = new_guard; + imq_unlock(&port->ip_messages); + + ip_unlock(port); + + return KERN_SUCCESS; +}