X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/91447636331957f3d9b5ca5b508f07c526b0074d..a1c7dba18ef36983396c282fe85292db066e39db:/osfmk/ipc/mach_port.c diff --git a/osfmk/ipc/mach_port.c b/osfmk/ipc/mach_port.c index f9dc656fc..d8e489eb5 100644 --- a/osfmk/ipc/mach_port.c +++ b/osfmk/ipc/mach_port.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2007 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ @@ -47,6 +53,13 @@ * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ +/* + * NOTICE: This file was modified by McAfee Research in 2004 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + * Copyright (c) 2005-2006 SPARTA, Inc. + */ /* */ /* @@ -83,6 +96,11 @@ #include #include #include +#include + +#if IMPORTANCE_INHERITANCE +#include +#endif /* * Forward declarations @@ -97,12 +115,23 @@ void mach_port_names_helper( 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_guard_exception( + mach_port_name_t name, + uint64_t inguard, + uint64_t portguard, + unsigned reason); + +/* Needs port locked */ +void mach_port_get_status_helper( + ipc_port_t port, + mach_port_status_t *status); + /* Zeroed template of qos flags */ static mach_port_qos_t qos_template; @@ -111,6 +140,9 @@ 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 @@ -124,44 +156,51 @@ mach_port_names_helper( { 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 = (ipc_port_t) 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); + assert(ip_active(port)); + type |= ipc_port_request_type(port, name, request); + ip_unlock(port); + } - ip_lock(port); - died = (!ip_active(port) && - IP_TIMESTAMP_ORDER(port->ip_timestamp, timestamp)); - ip_unlock(port); + } else if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { + mach_port_type_t reqtype; - if (died) { - /* pretend this is a dead-name entry */ + assert(IP_VALID(port)); + ip_lock(port); + 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; @@ -194,7 +233,6 @@ mach_port_names( 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; @@ -223,7 +261,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); @@ -233,9 +271,10 @@ mach_port_names( } /* upper bound on number of names in the space */ - - bound = space->is_table_size + space->is_tree_total; - size_needed = round_page(bound * sizeof(mach_port_name_t)); + bound = space->is_table_size; + size_needed = vm_map_round_page( + (bound * sizeof(mach_port_name_t)), + VM_MAP_PAGE_MASK(ipc_kernel_map)); if (size_needed <= size) break; @@ -260,18 +299,28 @@ 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( + 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, + 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( + 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, + FALSE); if (kr != KERN_SUCCESS) { kmem_free(ipc_kernel_map, addr1, size); kmem_free(ipc_kernel_map, addr2, size); @@ -303,17 +352,6 @@ mach_port_names( } } - 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) { @@ -329,19 +367,31 @@ 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, @@ -414,12 +464,16 @@ mach_port_type( kr = ipc_right_lookup_write(space, name, &entry); 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; } @@ -438,26 +492,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: @@ -618,17 +667,21 @@ mach_port_allocate_full( if (qosp->name) { if (!MACH_PORT_VALID (*namep)) return (KERN_INVALID_VALUE); - if (is_fast_space (space)) - return (KERN_FAILURE); } if (qosp->prealloc) { - 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 (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_prealloc(size); + if (kmsg == IKM_NULL) + return (KERN_RESOURCE_SHORTAGE); + } } switch (right) { @@ -712,8 +765,7 @@ mach_port_destroy( 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; } @@ -805,12 +857,13 @@ mach_port_get_refs( kr = ipc_right_lookup_write(space, name, &entry); 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); + return kr; if (type & MACH_PORT_TYPE(right)) switch (right) { @@ -891,6 +944,95 @@ mach_port_mod_refs( } +/* + * 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) + return KERN_INVALID_VALUE; + + *trailer_sizep = REQUESTED_TRAILER_SIZE(TRUE, trailer_type); + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + + /* Port locked and active */ + + found = ipc_mqueue_peek(&port->ip_messages, seqnop, + msg_sizep, msg_idp, &max_trailer); + 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: @@ -972,41 +1114,100 @@ 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; - ip_lock(port); - assert(ip_active(port)); + if (!MACH_PORT_VALID(name)) + return KERN_INVALID_RIGHT; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; - name = port->ip_receiver_name; - assert(name != MACH_PORT_NULL); + /* Port locked and active */ + + /* 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. + */ - if (ipc_pset_member(pset, port)) { - ipc_entry_num_t actual = *actualp; +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 (!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: @@ -1044,14 +1245,11 @@ mach_port_get_set_status( 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); @@ -1064,66 +1262,34 @@ mach_port_get_set_status( VM_PROT_READ|VM_PROT_WRITE, 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 = (ipc_pset_t) 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); - } - } - - 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; - - assert(IE_BITS_TYPE(bits) != MACH_PORT_TYPE_NONE); + maxnames = (ipc_entry_num_t)(size / sizeof(mach_port_name_t)); - if (bits & MACH_PORT_TYPE_RECEIVE) { - ipc_port_t port = (ipc_port_t) tentry->ite_object; + ipc_mqueue_set_gather_member_names(&pset->ips_messages, maxnames, names, &actual); - mach_port_gst_helper(pset, port, maxnames, - names, &actual); - } - } - ipc_splay_traverse_finish(&space->is_tree); - is_read_unlock(space); + /* release the portset reference */ + ips_release(pset); 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) { @@ -1135,15 +1301,22 @@ 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, @@ -1191,6 +1364,9 @@ mach_port_move_member( ipc_port_t port; ipc_pset_t nset; kern_return_t kr; + wait_queue_link_t wql; + queue_head_t links_data; + queue_t links = &links_data; if (space == IS_NULL) return KERN_INVALID_TASK; @@ -1200,15 +1376,22 @@ mach_port_move_member( if (after == MACH_PORT_DEAD) return KERN_INVALID_RIGHT; + else if (after == MACH_PORT_NULL) + wql = WAIT_QUEUE_LINK_NULL; + else + wql = wait_queue_link_allocate(); + + queue_init(links); kr = ipc_right_lookup_read(space, member, &entry); if (kr != KERN_SUCCESS) - return kr; + goto done; /* space is read-locked and active */ if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0) { is_read_unlock(space); - return KERN_INVALID_RIGHT; + kr = KERN_INVALID_RIGHT; + goto done; } port = (ipc_port_t) entry->ie_object; @@ -1220,27 +1403,38 @@ mach_port_move_member( entry = ipc_entry_lookup(space, after); if (entry == IE_NULL) { is_read_unlock(space); - return KERN_INVALID_NAME; + kr = KERN_INVALID_NAME; + goto done; } if ((entry->ie_bits & MACH_PORT_TYPE_PORT_SET) == 0) { is_read_unlock(space); - return KERN_INVALID_RIGHT; + kr = KERN_INVALID_RIGHT; + goto done; } nset = (ipc_pset_t) entry->ie_object; assert(nset != IPS_NULL); } ip_lock(port); - ipc_pset_remove_from_all(port); + ipc_pset_remove_from_all(port, links); if (nset != IPS_NULL) { ips_lock(nset); - kr = ipc_pset_add(nset, port); + kr = ipc_pset_add(nset, port, wql); ips_unlock(nset); } ip_unlock(port); is_read_unlock(space); + + done: + if (kr != KERN_SUCCESS && wql != WAIT_QUEUE_LINK_NULL) + wait_queue_link_free(wql); + while(!queue_empty(links)) { + wql = (wait_queue_link_t) dequeue(links); + wait_queue_link_free(wql); + } + return kr; } @@ -1367,6 +1561,18 @@ mach_port_request_notification( 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: if (!MACH_PORT_VALID(name)) { @@ -1378,8 +1584,8 @@ mach_port_request_notification( return KERN_INVALID_ARGUMENT; } - kr = ipc_right_dnrequest(space, name, sync != 0, - notify, previousp); + kr = ipc_right_request_alloc(space, name, sync != 0, + FALSE, notify, previousp); if (kr != KERN_SUCCESS) return kr; break; @@ -1481,6 +1687,56 @@ mach_port_extract_right( 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. + */ +void mach_port_get_status_helper( + ipc_port_t port, + mach_port_status_t *statusp) +{ + spl_t s; + 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; + 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; + } + } + return; +} + + kern_return_t mach_port_get_attributes( @@ -1520,41 +1776,23 @@ mach_port_get_attributes( } 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; - + 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 */ - - 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; - } + 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; + } case MACH_PORT_DNREQUESTS_SIZE: { ipc_port_request_t table; @@ -1572,7 +1810,7 @@ mach_port_get_attributes( return kr; /* port is locked and active */ - table = port->ip_dnrequests; + table = port->ip_requests; if (table == IPR_NULL) *(int *)info = 0; else @@ -1582,6 +1820,25 @@ mach_port_get_attributes( 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: return KERN_INVALID_ARGUMENT; /*NOTREACHED*/ @@ -1639,11 +1896,93 @@ mach_port_set_attributes( return kr; /* port is locked and active */ - kr = ipc_port_dngrow(port, *(int *)info); + kr = ipc_port_request_grow(port, *(int *)info); if (kr != KERN_SUCCESS) return kr; break; } + 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). + */ + if (is_ipc_kobject(ip_kotype(port))) { + 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). + */ + if (is_ipc_kobject(ip_kotype(port))) { + 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*/ @@ -1679,6 +2018,7 @@ mach_port_insert_member( ipc_object_t obj; ipc_object_t psobj; kern_return_t kr; + wait_queue_link_t wql; if (space == IS_NULL) return KERN_INVALID_TASK; @@ -1686,19 +2026,26 @@ mach_port_insert_member( if (!MACH_PORT_VALID(name) || !MACH_PORT_VALID(psname)) return KERN_INVALID_RIGHT; + wql = wait_queue_link_allocate(); + 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; + 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((ipc_pset_t)psobj, (ipc_port_t)obj, wql); io_unlock(psobj); io_unlock(obj); + + done: + if (kr != KERN_SUCCESS) + wait_queue_link_free(wql); + return kr; } @@ -1729,6 +2076,7 @@ mach_port_extract_member( ipc_object_t psobj; ipc_object_t obj; kern_return_t kr; + wait_queue_link_t wql = WAIT_QUEUE_LINK_NULL; if (space == IS_NULL) return KERN_INVALID_TASK; @@ -1746,9 +2094,13 @@ mach_port_extract_member( assert(psobj != IO_NULL); assert(obj != IO_NULL); - kr = ipc_pset_remove((ipc_pset_t)psobj, (ipc_port_t)obj); + kr = ipc_pset_remove((ipc_pset_t)psobj, (ipc_port_t)obj, &wql); io_unlock(psobj); io_unlock(obj); + + if (wql != WAIT_QUEUE_LINK_NULL) + wait_queue_link_free(wql); + return kr; } @@ -1765,9 +2117,382 @@ task_set_port_space( kern_return_t kr; 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) 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, + boolean_t strict) +{ + if (port->ip_context) + return KERN_INVALID_ARGUMENT; + + port->ip_context = guard; + port->ip_guarded = 1; + port->ip_strict_guard = (strict)?1:0; + 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; + } + + port->ip_context = 0; + port->ip_guarded = port->ip_strict_guard = 0; + 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. + */ +kern_return_t +mach_port_guard_exception( + mach_port_name_t name, + uint64_t inguard, + uint64_t portguard, + unsigned reason) +{ + thread_t t = current_thread(); + uint64_t code, subcode; + + /* Log exception info to syslog */ + printf( "Mach Port Guard Exception - " + "Thread: 0x%x, " + "Port Name: 0x%x, " + "Expected Guard: 0x%x, " + "Received Guard: 0x%x\n", + (unsigned)t, + (unsigned)name, + (unsigned)portguard, + (unsigned)inguard); + + /* + * EXC_GUARD namespace for mach ports + * + * + * Mach Port guards use the exception codes like + * + * code: + * +----------------------------------------------------------------+ + * |[63:61] GUARD_TYPE_MACH_PORT | [60:32] flavor | [31:0] port name| + * +----------------------------------------------------------------+ + * + * subcode: + * +----------------------------------------------------------------+ + * | [63:0] guard value | + * +----------------------------------------------------------------+ + */ + + code = (((uint64_t)GUARD_TYPE_MACH_PORT) << 61) | + (((uint64_t)reason) << 32) | + ((uint64_t)name); + subcode = (uint64_t)(portguard); + + t->guard_exc_info.code = code; + t->guard_exc_info.subcode = subcode; + + /* Mark thread with AST_GUARD */ + thread_guard_violation(t, GUARD_TYPE_MACH_PORT); + return KERN_FAILURE; +} + + +/* + * 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[EXCEPTION_CODE_MAX]; + + code[0] = t->guard_exc_info.code; + code[1] = t->guard_exc_info.subcode; + + /* Raise an EXC_GUARD exception */ + exception_triage(EXC_GUARD, code, EXCEPTION_CODE_MAX); + + /* Terminate task which caused the exception */ + (void) task_terminate_internal(current_task()); + return; +} + +/* + * 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, name, &port); + if (kr != KERN_SUCCESS) + return kr; + + /* Port locked and active */ + if (options->flags & MPO_CONTEXT_AS_GUARD) { + kr = mach_port_guard_locked(port, (uint64_t) context, (options->flags & MPO_STRICT)); + /* 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; + } + + if (options->flags & MPO_INSERT_SEND_RIGHT) { + kr = ipc_object_copyin(space, *name, MACH_MSG_TYPE_MAKE_SEND, (ipc_object_t *)&port); + if (kr != KERN_SUCCESS) + goto cleanup; + + kr = mach_port_insert_right(space, *name, port, MACH_MSG_TYPE_PORT_SEND); + 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, 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) + 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; + + 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) + return kr; + + /* Port locked and active */ + kr = mach_port_guard_locked(port, guard, strict); + ip_unlock(port); + + 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) + return kr; + + /* Port locked and active */ + kr = mach_port_unguard_locked(port, name, guard); + ip_unlock(port); + return kr; +} +