/*
- * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- *
+ *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
- *
+ *
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
- *
+ *
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
+ *
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* @OSF_COPYRIGHT@
*/
-/*
+/*
* Mach Operating System
* Copyright (c) 1991,1990,1989 Carnegie Mellon University
* All Rights Reserved.
- *
+ *
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
- *
+ *
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
- *
+ *
* Carnegie Mellon requests users of this software to return to
- *
+ *
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
- *
+ *
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
* Functions to manipulate IPC objects.
*/
-#include <mach_rt.h>
-
#include <mach/mach_types.h>
#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <kern/ipc_kobject.h>
#include <ipc/ipc_types.h>
+#include <ipc/ipc_importance.h>
#include <ipc/port.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_entry.h>
#include <ipc/ipc_notify.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_pset.h>
-#include <ipc/ipc_labelh.h>
#include <security/mac_mach_internal.h>
-zone_t ipc_object_zones[IOT_NUMBER];
+SECURITY_READ_ONLY_LATE(zone_t) ipc_object_zones[IOT_NUMBER];
+
+ZONE_INIT(&ipc_object_zones[IOT_PORT], "ipc ports", sizeof(struct ipc_port),
+ ZC_NOENCRYPT | ZC_CACHING | ZC_ZFREE_CLEARMEM | ZC_NOSEQUESTER,
+ ZONE_ID_IPC_PORT, NULL);
+
+ZONE_INIT(&ipc_object_zones[IOT_PORT_SET], "ipc port sets",
+ sizeof(struct ipc_pset),
+ ZC_NOENCRYPT | ZC_ZFREE_CLEARMEM | ZC_NOSEQUESTER,
+ ZONE_ID_IPC_PORT_SET, NULL);
/*
* Routine: ipc_object_reference
void
ipc_object_reference(
- ipc_object_t object)
+ ipc_object_t object)
{
io_reference(object);
}
void
ipc_object_release(
- ipc_object_t object)
+ ipc_object_t object)
{
io_release(object);
}
* Look up an object in a space.
* Conditions:
* Nothing locked before. If successful, the object
- * is returned locked. The caller doesn't get a ref.
+ * is returned active and locked. The caller doesn't get a ref.
* Returns:
* KERN_SUCCESS Object returned locked.
* KERN_INVALID_TASK The space is dead.
- * KERN_INVALID_NAME The name doesn't denote a right.
- * KERN_INVALID_RIGHT Name doesn't denote the correct right.
+ * KERN_INVALID_NAME The name doesn't denote a right
+ * KERN_INVALID_RIGHT Name doesn't denote the correct right
*/
-
kern_return_t
ipc_object_translate(
- ipc_space_t space,
- mach_port_name_t name,
- mach_port_right_t right,
- ipc_object_t *objectp)
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_right_t right,
+ ipc_object_t *objectp)
{
ipc_entry_t entry;
ipc_object_t object;
kern_return_t kr;
+ if (!MACH_PORT_RIGHT_VALID_TRANSLATE(right)) {
+ return KERN_INVALID_RIGHT;
+ }
+
kr = ipc_right_lookup_read(space, name, &entry);
- if (kr != KERN_SUCCESS)
+ if (kr != KERN_SUCCESS) {
return kr;
+ }
/* space is read-locked and active */
if ((entry->ie_bits & MACH_PORT_TYPE(right)) == MACH_PORT_TYPE_NONE) {
io_lock(object);
is_read_unlock(space);
+ if (!io_active(object)) {
+ io_unlock(object);
+ return KERN_INVALID_NAME;
+ }
+
*objectp = object;
return KERN_SUCCESS;
}
kern_return_t
ipc_object_translate_two(
- ipc_space_t space,
- mach_port_name_t name1,
- mach_port_right_t right1,
- ipc_object_t *objectp1,
- mach_port_name_t name2,
- mach_port_right_t right2,
- ipc_object_t *objectp2)
+ ipc_space_t space,
+ mach_port_name_t name1,
+ mach_port_right_t right1,
+ ipc_object_t *objectp1,
+ mach_port_name_t name2,
+ mach_port_right_t right2,
+ ipc_object_t *objectp2)
{
ipc_entry_t entry1;
ipc_entry_t entry2;
- ipc_object_t object;
+ ipc_object_t object1, object2;
kern_return_t kr;
+ boolean_t doguard = TRUE;
kr = ipc_right_lookup_two_read(space, name1, &entry1, name2, &entry2);
- if (kr != KERN_SUCCESS)
+ if (kr != KERN_SUCCESS) {
return kr;
+ }
/* space is read-locked and active */
if ((entry1->ie_bits & MACH_PORT_TYPE(right1)) == MACH_PORT_TYPE_NONE) {
+ /* If looking for receive, and the entry used to hold one, give a pass on EXC_GUARD */
+ if ((right1 & MACH_PORT_RIGHT_RECEIVE) == MACH_PORT_RIGHT_RECEIVE &&
+ (entry1->ie_bits & MACH_PORT_TYPE_EX_RECEIVE) == MACH_PORT_TYPE_EX_RECEIVE) {
+ doguard = FALSE;
+ }
is_read_unlock(space);
+ if (doguard) {
+ mach_port_guard_exception(name1, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+ }
return KERN_INVALID_RIGHT;
}
if ((entry2->ie_bits & MACH_PORT_TYPE(right2)) == MACH_PORT_TYPE_NONE) {
+ /* If looking for receive, and the entry used to hold one, give a pass on EXC_GUARD */
+ if ((right2 & MACH_PORT_RIGHT_RECEIVE) == MACH_PORT_RIGHT_RECEIVE &&
+ (entry2->ie_bits & MACH_PORT_TYPE_EX_RECEIVE) == MACH_PORT_TYPE_EX_RECEIVE) {
+ doguard = FALSE;
+ }
is_read_unlock(space);
+ if (doguard) {
+ mach_port_guard_exception(name2, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+ }
return KERN_INVALID_RIGHT;
}
- object = entry1->ie_object;
- assert(object != IO_NULL);
- io_lock(object);
- *objectp1 = object;
+ object1 = entry1->ie_object;
+ assert(object1 != IO_NULL);
+ io_lock(object1);
+ if (!io_active(object1)) {
+ io_unlock(object1);
+ is_read_unlock(space);
+ return KERN_INVALID_NAME;
+ }
- object = entry2->ie_object;
- assert(object != IO_NULL);
- io_lock(object);
- *objectp2 = object;
+ object2 = entry2->ie_object;
+ assert(object2 != IO_NULL);
+ io_lock(object2);
+ if (!io_active(object2)) {
+ io_unlock(object1);
+ io_unlock(object2);
+ is_read_unlock(space);
+ return KERN_INVALID_NAME;
+ }
+
+ *objectp1 = object1;
+ *objectp2 = object2;
is_read_unlock(space);
return KERN_SUCCESS;
kern_return_t
ipc_object_alloc_dead(
- ipc_space_t space,
- mach_port_name_t *namep)
+ ipc_space_t space,
+ mach_port_name_t *namep)
{
ipc_entry_t entry;
kern_return_t kr;
kr = ipc_entry_alloc(space, namep, &entry);
- if (kr != KERN_SUCCESS)
+ if (kr != KERN_SUCCESS) {
return kr;
+ }
/* space is write-locked */
/* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */
kern_return_t
ipc_object_alloc_dead_name(
- ipc_space_t space,
- mach_port_name_t name)
+ ipc_space_t space,
+ mach_port_name_t name)
{
ipc_entry_t entry;
kern_return_t kr;
kr = ipc_entry_alloc_name(space, name, &entry);
- if (kr != KERN_SUCCESS)
+ if (kr != KERN_SUCCESS) {
return kr;
+ }
/* space is write-locked */
- if (ipc_right_inuse(space, name, entry))
+ if (ipc_right_inuse(space, name, entry)) {
return KERN_NAME_EXISTS;
+ }
/* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */
* Allocate an object.
* Conditions:
* Nothing locked. If successful, the object is returned locked.
+ * The space is write locked on successful return.
* The caller doesn't get a reference for the object.
* Returns:
* KERN_SUCCESS The object is allocated.
kern_return_t
ipc_object_alloc(
- ipc_space_t space,
- ipc_object_type_t otype,
- mach_port_type_t type,
- mach_port_urefs_t urefs,
- mach_port_name_t *namep,
- ipc_object_t *objectp)
+ ipc_space_t space,
+ ipc_object_type_t otype,
+ mach_port_type_t type,
+ mach_port_urefs_t urefs,
+ mach_port_name_t *namep,
+ ipc_object_t *objectp)
{
ipc_object_t object;
ipc_entry_t entry;
assert(urefs <= MACH_PORT_UREFS_MAX);
object = io_alloc(otype);
- if (object == IO_NULL)
+ if (object == IO_NULL) {
return KERN_RESOURCE_SHORTAGE;
+ }
if (otype == IOT_PORT) {
- ipc_port_t port = (ipc_port_t)object;
+ ipc_port_t port = ip_object_to_port(object);
bzero((char *)port, sizeof(*port));
-#if CONFIG_MACF_MACH
- mac_port_label_init(&port->ip_label);
-#endif
} else if (otype == IOT_PORT_SET) {
- ipc_pset_t pset = (ipc_pset_t)object;
+ ipc_pset_t pset = ips_object_to_pset(object);
bzero((char *)pset, sizeof(*pset));
}
entry->ie_object = object;
ipc_entry_modified(space, *namep, entry);
+ object->io_bits = io_makebits(TRUE, otype, 0);
io_lock(object);
- is_write_unlock(space);
object->io_references = 1; /* for entry, not caller */
- object->io_bits = io_makebits(TRUE, otype, 0);
*objectp = object;
return KERN_SUCCESS;
kern_return_t
ipc_object_alloc_name(
- ipc_space_t space,
- ipc_object_type_t otype,
- mach_port_type_t type,
- mach_port_urefs_t urefs,
- mach_port_name_t name,
- ipc_object_t *objectp)
+ ipc_space_t space,
+ ipc_object_type_t otype,
+ mach_port_type_t type,
+ mach_port_urefs_t urefs,
+ mach_port_name_t name,
+ ipc_object_t *objectp)
{
ipc_object_t object;
ipc_entry_t entry;
assert(urefs <= MACH_PORT_UREFS_MAX);
object = io_alloc(otype);
- if (object == IO_NULL)
+ if (object == IO_NULL) {
return KERN_RESOURCE_SHORTAGE;
+ }
if (otype == IOT_PORT) {
- ipc_port_t port = (ipc_port_t)object;
+ ipc_port_t port = ip_object_to_port(object);
bzero((char *)port, sizeof(*port));
-#if CONFIG_MACF_MACH
- mac_port_label_init(&port->ip_label);
-#endif
} else if (otype == IOT_PORT_SET) {
- ipc_pset_t pset = (ipc_pset_t)object;
+ ipc_pset_t pset = ips_object_to_pset(object);
bzero((char *)pset, sizeof(*pset));
}
entry->ie_object = object;
ipc_entry_modified(space, name, entry);
+ object->io_bits = io_makebits(TRUE, otype, 0);
+
io_lock(object);
is_write_unlock(space);
object->io_references = 1; /* for entry, not caller */
- object->io_bits = io_makebits(TRUE, otype, 0);
*objectp = object;
return KERN_SUCCESS;
}
+/* Routine: ipc_object_validate
+ * Purpose:
+ * Validates an ipc port or port set as belonging to the correct
+ * zone.
+ */
+
+void
+ipc_object_validate(
+ ipc_object_t object)
+{
+ if (io_otype(object) != IOT_PORT_SET) {
+ zone_id_require(ZONE_ID_IPC_PORT,
+ sizeof(struct ipc_port), object);
+ } else {
+ zone_id_require(ZONE_ID_IPC_PORT_SET,
+ sizeof(struct ipc_pset), object);
+ }
+}
+
/*
* Routine: ipc_object_copyin_type
* Purpose:
mach_msg_type_name_t
ipc_object_copyin_type(
- mach_msg_type_name_t msgt_name)
+ mach_msg_type_name_t msgt_name)
{
switch (msgt_name) {
-
- case MACH_MSG_TYPE_MOVE_RECEIVE:
- case MACH_MSG_TYPE_COPY_RECEIVE:
+ case MACH_MSG_TYPE_MOVE_RECEIVE:
return MACH_MSG_TYPE_PORT_RECEIVE;
- case MACH_MSG_TYPE_MOVE_SEND_ONCE:
- case MACH_MSG_TYPE_MAKE_SEND_ONCE:
+ case MACH_MSG_TYPE_MOVE_SEND_ONCE:
+ case MACH_MSG_TYPE_MAKE_SEND_ONCE:
return MACH_MSG_TYPE_PORT_SEND_ONCE;
- case MACH_MSG_TYPE_MOVE_SEND:
- case MACH_MSG_TYPE_MAKE_SEND:
- case MACH_MSG_TYPE_COPY_SEND:
+ case MACH_MSG_TYPE_MOVE_SEND:
+ case MACH_MSG_TYPE_MAKE_SEND:
+ case MACH_MSG_TYPE_COPY_SEND:
return MACH_MSG_TYPE_PORT_SEND;
- default:
+ case MACH_MSG_TYPE_DISPOSE_RECEIVE:
+ case MACH_MSG_TYPE_DISPOSE_SEND:
+ case MACH_MSG_TYPE_DISPOSE_SEND_ONCE:
+ /* fall thru */
+ default:
return MACH_MSG_TYPE_PORT_NONE;
}
}
kern_return_t
ipc_object_copyin(
- ipc_space_t space,
- mach_port_name_t name,
- mach_msg_type_name_t msgt_name,
- ipc_object_t *objectp)
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_msg_type_name_t msgt_name,
+ ipc_object_t *objectp,
+ mach_port_context_t context,
+ mach_msg_guard_flags_t *guard_flags,
+ ipc_kmsg_flags_t kmsg_flags)
{
ipc_entry_t entry;
ipc_port_t soright;
ipc_port_t release_port;
kern_return_t kr;
- queue_head_t links_data;
- queue_t links = &links_data;
- wait_queue_link_t wql;
+ int assertcnt = 0;
- queue_init(links);
+ ipc_right_copyin_flags_t irc_flags = IPC_RIGHT_COPYIN_FLAGS_DEADOK;
+ if (kmsg_flags & IPC_KMSG_FLAGS_ALLOW_IMMOVABLE_SEND) {
+ irc_flags |= IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND;
+ }
/*
* Could first try a read lock when doing
*/
kr = ipc_right_lookup_write(space, name, &entry);
- if (kr != KERN_SUCCESS)
+ if (kr != KERN_SUCCESS) {
return kr;
+ }
/* space is write-locked and active */
release_port = IP_NULL;
kr = ipc_right_copyin(space, name, entry,
- msgt_name, TRUE,
- objectp, &soright,
- &release_port,
- links);
- if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
+ msgt_name, irc_flags,
+ objectp, &soright,
+ &release_port,
+ &assertcnt,
+ context,
+ guard_flags);
+ if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) {
ipc_entry_dealloc(space, name, entry);
+ }
is_write_unlock(space);
- while(!queue_empty(links)) {
- wql = (wait_queue_link_t) dequeue(links);
- wait_queue_link_free(wql);
+#if IMPORTANCE_INHERITANCE
+ if (0 < assertcnt && ipc_importance_task_is_any_receiver_type(current_task()->task_imp_base)) {
+ ipc_importance_task_drop_internal_assertion(current_task()->task_imp_base, assertcnt);
}
+#endif /* IMPORTANCE_INHERITANCE */
- if (release_port != IP_NULL)
+ if (release_port != IP_NULL) {
ip_release(release_port);
+ }
- if ((kr == KERN_SUCCESS) && (soright != IP_NULL))
+ if ((kr == KERN_SUCCESS) && (soright != IP_NULL)) {
ipc_notify_port_deleted(soright, name);
+ }
return kr;
}
void
ipc_object_copyin_from_kernel(
- ipc_object_t object,
- mach_msg_type_name_t msgt_name)
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name)
{
assert(IO_VALID(object));
switch (msgt_name) {
- case MACH_MSG_TYPE_MOVE_RECEIVE: {
- ipc_port_t port = (ipc_port_t) object;
+ case MACH_MSG_TYPE_MOVE_RECEIVE: {
+ ipc_port_t port = ip_object_to_port(object);
ip_lock(port);
- assert(ip_active(port));
+ imq_lock(&port->ip_messages);
+ require_ip_active(port);
if (port->ip_destination != IP_NULL) {
assert(port->ip_receiver == ipc_space_kernel);
+ assert(port->ip_immovable_receive == 0);
/* relevant part of ipc_port_clear_receiver */
- ipc_port_set_mscount(port, 0);
-
+ port->ip_mscount = 0;
port->ip_receiver_name = MACH_PORT_NULL;
port->ip_destination = IP_NULL;
}
+ imq_unlock(&port->ip_messages);
ip_unlock(port);
break;
- }
+ }
- case MACH_MSG_TYPE_COPY_SEND: {
- ipc_port_t port = (ipc_port_t) object;
+ case MACH_MSG_TYPE_COPY_SEND: {
+ ipc_port_t port = ip_object_to_port(object);
ip_lock(port);
if (ip_active(port)) {
ip_reference(port);
ip_unlock(port);
break;
- }
+ }
- case MACH_MSG_TYPE_MAKE_SEND: {
- ipc_port_t port = (ipc_port_t) object;
+ case MACH_MSG_TYPE_MAKE_SEND: {
+ ipc_port_t port = ip_object_to_port(object);
ip_lock(port);
- assert(ip_active(port));
- assert(port->ip_receiver_name != MACH_PORT_NULL);
- assert(port->ip_receiver == ipc_space_kernel);
+ if (ip_active(port)) {
+ assert(port->ip_receiver_name != MACH_PORT_NULL);
+ assert((port->ip_receiver == ipc_space_kernel) ||
+ (port->ip_receiver->is_node_id != HOST_LOCAL_NODE));
+ port->ip_mscount++;
+ }
- ip_reference(port);
- port->ip_mscount++;
port->ip_srights++;
+ ip_reference(port);
ip_unlock(port);
break;
- }
+ }
- case MACH_MSG_TYPE_MOVE_SEND: {
+ case MACH_MSG_TYPE_MOVE_SEND: {
/* move naked send right into the message */
- assert(((ipc_port_t)object)->ip_srights);
+ assert(ip_object_to_port(object)->ip_srights);
break;
- }
+ }
- case MACH_MSG_TYPE_MAKE_SEND_ONCE: {
- ipc_port_t port = (ipc_port_t) object;
+ case MACH_MSG_TYPE_MAKE_SEND_ONCE: {
+ ipc_port_t port = ip_object_to_port(object);
ip_lock(port);
- assert(ip_active(port));
- assert(port->ip_receiver_name != MACH_PORT_NULL);
-
- ip_reference(port);
- port->ip_sorights++;
+ if (ip_active(port)) {
+ assert(port->ip_receiver_name != MACH_PORT_NULL);
+ }
+ ipc_port_make_sonce_locked(port);
ip_unlock(port);
break;
- }
+ }
- case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
+ case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
/* move naked send-once right into the message */
- assert(((ipc_port_t)object)->ip_sorights);
+ assert(ip_object_to_port(object)->ip_sorights);
break;
- }
+ }
- default:
+ default:
panic("ipc_object_copyin_from_kernel: strange rights");
}
}
void
ipc_object_destroy(
- ipc_object_t object,
- mach_msg_type_name_t msgt_name)
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name)
{
assert(IO_VALID(object));
assert(io_otype(object) == IOT_PORT);
switch (msgt_name) {
- case MACH_MSG_TYPE_PORT_SEND:
- ipc_port_release_send((ipc_port_t) object);
+ case MACH_MSG_TYPE_PORT_SEND:
+ ipc_port_release_send(ip_object_to_port(object));
break;
- case MACH_MSG_TYPE_PORT_SEND_ONCE:
- ipc_notify_send_once((ipc_port_t) object);
+ case MACH_MSG_TYPE_PORT_SEND_ONCE:
+ ipc_notify_send_once(ip_object_to_port(object));
break;
- case MACH_MSG_TYPE_PORT_RECEIVE:
- ipc_port_release_receive((ipc_port_t) object);
+ case MACH_MSG_TYPE_PORT_RECEIVE:
+ ipc_port_release_receive(ip_object_to_port(object));
break;
- default:
+ default:
panic("ipc_object_destroy: strange rights");
}
}
void
ipc_object_destroy_dest(
- ipc_object_t object,
- mach_msg_type_name_t msgt_name)
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name)
{
assert(IO_VALID(object));
assert(io_otype(object) == IOT_PORT);
switch (msgt_name) {
- case MACH_MSG_TYPE_PORT_SEND:
- ipc_port_release_send((ipc_port_t) object);
+ case MACH_MSG_TYPE_PORT_SEND:
+ ipc_port_release_send(ip_object_to_port(object));
break;
- case MACH_MSG_TYPE_PORT_SEND_ONCE:
- if (io_active(object) &&
- !ip_full_kernel((ipc_port_t) object))
- ipc_notify_send_once((ipc_port_t) object);
- else
- ipc_port_release_sonce((ipc_port_t) object);
+ case MACH_MSG_TYPE_PORT_SEND_ONCE:
+ if (io_active(object) &&
+ !ip_full_kernel(ip_object_to_port(object))) {
+ ipc_notify_send_once(ip_object_to_port(object));
+ } else {
+ ipc_port_release_sonce(ip_object_to_port(object));
+ }
break;
- default:
+ default:
panic("ipc_object_destroy_dest: strange rights");
}
}
+/*
+ * Routine: ipc_object_insert_send_right
+ * Purpose:
+ * Insert a send right into an object already in the space.
+ * The specified name must already point to a valid object.
+ *
+ * Note: This really is a combined copyin()/copyout(),
+ * that avoids most of the overhead of being implemented that way.
+ *
+ * This is the fastpath for mach_port_insert_right.
+ *
+ * Conditions:
+ * Nothing locked.
+ *
+ * msgt_name must be MACH_MSG_TYPE_MAKE_SEND_ONCE or
+ * MACH_MSG_TYPE_MOVE_SEND_ONCE.
+ *
+ * Returns:
+ * KERN_SUCCESS Copied out object, consumed ref.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME Name doesn't exist in space.
+ * KERN_INVALID_CAPABILITY The object is dead.
+ * KERN_RIGHT_EXISTS Space has rights under another name.
+ */
+kern_return_t
+ipc_object_insert_send_right(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_msg_type_name_t msgt_name)
+{
+ ipc_entry_bits_t bits;
+ ipc_object_t object;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ assert(msgt_name == MACH_MSG_TYPE_MAKE_SEND ||
+ msgt_name == MACH_MSG_TYPE_COPY_SEND);
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+ /* space is write-locked and active */
+
+ if (!IO_VALID(entry->ie_object)) {
+ is_write_unlock(space);
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ bits = entry->ie_bits;
+ object = entry->ie_object;
+
+ io_lock(object);
+ if (!io_active(object)) {
+ kr = KERN_INVALID_CAPABILITY;
+ } else if (msgt_name == MACH_MSG_TYPE_MAKE_SEND) {
+ if (bits & MACH_PORT_TYPE_RECEIVE) {
+ ipc_port_t port = ip_object_to_port(object);
+ port->ip_mscount++;
+ if ((bits & MACH_PORT_TYPE_SEND) == 0) {
+ port->ip_srights++;
+ bits |= MACH_PORT_TYPE_SEND;
+ }
+ /* leave urefs pegged to maximum if it overflowed */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+ bits += 1; /* increment urefs */
+ }
+ entry->ie_bits = bits;
+ ipc_entry_modified(space, name, entry);
+ kr = KERN_SUCCESS;
+ } else {
+ kr = KERN_INVALID_RIGHT;
+ }
+ } else { // MACH_MSG_TYPE_COPY_SEND
+ if (bits & MACH_PORT_TYPE_SEND) {
+ /* leave urefs pegged to maximum if it overflowed */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+ entry->ie_bits = bits + 1; /* increment urefs */
+ }
+ ipc_entry_modified(space, name, entry);
+ kr = KERN_SUCCESS;
+ } else {
+ kr = KERN_INVALID_RIGHT;
+ }
+ }
+
+ io_unlock(object);
+ is_write_unlock(space);
+
+ return kr;
+}
+
/*
* Routine: ipc_object_copyout
* Purpose:
kern_return_t
ipc_object_copyout(
- ipc_space_t space,
- ipc_object_t object,
- mach_msg_type_name_t msgt_name,
- boolean_t overflow,
- mach_port_name_t *namep)
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name,
+ mach_port_context_t *context,
+ mach_msg_guard_flags_t *guard_flags,
+ mach_port_name_t *namep)
{
+ struct knote *kn = current_thread()->ith_knote;
mach_port_name_t name;
ipc_entry_t entry;
kern_return_t kr;
assert(IO_VALID(object));
assert(io_otype(object) == IOT_PORT);
+ if (ITH_KNOTE_VALID(kn, msgt_name)) {
+ filt_machport_turnstile_prepare_lazily(kn,
+ msgt_name, ip_object_to_port(object));
+ }
+
is_write_lock(space);
for (;;) {
}
if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
- ipc_right_reverse(space, object, &name, &entry)) {
+ ipc_right_reverse(space, object, &name, &entry)) {
/* object is locked and active */
assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
break;
}
+
name = CAST_MACH_PORT_TO_NAME(object);
kr = ipc_entry_get(space, &name, &entry);
if (kr != KERN_SUCCESS) {
/* unlocks/locks space, so must start again */
kr = ipc_entry_grow_table(space, ITS_SIZE_NONE);
- if (kr != KERN_SUCCESS)
+ if (kr != KERN_SUCCESS) {
return kr; /* space is unlocked */
-
+ }
continue;
}
return KERN_INVALID_CAPABILITY;
}
+ /* Don't actually copyout rights we aren't allowed to */
+ if (!ip_label_check(space, ip_object_to_port(object), msgt_name)) {
+ io_unlock(object);
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ return KERN_INVALID_CAPABILITY;
+ }
+
entry->ie_object = object;
break;
}
/* space is write-locked and active, object is locked and active */
kr = ipc_right_copyout(space, name, entry,
- msgt_name, overflow, object);
+ msgt_name, context, guard_flags, object);
+
/* object is unlocked */
is_write_unlock(space);
- if (kr == KERN_SUCCESS)
+ if (kr == KERN_SUCCESS) {
*namep = name;
+ }
return kr;
}
kern_return_t
ipc_object_copyout_name(
- ipc_space_t space,
- ipc_object_t object,
- mach_msg_type_name_t msgt_name,
- boolean_t overflow,
- mach_port_name_t name)
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name,
+ mach_port_name_t name)
{
mach_port_name_t oname;
ipc_entry_t oentry;
ipc_entry_t entry;
kern_return_t kr;
+#if IMPORTANCE_INHERITANCE
+ int assertcnt = 0;
+ ipc_importance_task_t task_imp = IIT_NULL;
+#endif /* IMPORTANCE_INHERITANCE */
+
assert(IO_VALID(object));
assert(io_otype(object) == IOT_PORT);
kr = ipc_entry_alloc_name(space, name, &entry);
- if (kr != KERN_SUCCESS)
+ if (kr != KERN_SUCCESS) {
return kr;
+ }
/* space is write-locked and active */
if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
if (name != oname) {
io_unlock(object);
- if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
+ if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) {
ipc_entry_dealloc(space, name, entry);
+ }
is_write_unlock(space);
return KERN_RIGHT_EXISTS;
assert(entry == oentry);
assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
} else {
- if (ipc_right_inuse(space, name, entry))
+ if (ipc_right_inuse(space, name, entry)) {
return KERN_NAME_EXISTS;
+ }
assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
assert(entry->ie_object == IO_NULL);
return KERN_INVALID_CAPABILITY;
}
+ /* Don't actually copyout rights we aren't allowed to */
+ if (!ip_label_check(space, ip_object_to_port(object), msgt_name)) {
+ io_unlock(object);
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ return KERN_INVALID_CAPABILITY;
+ }
+
entry->ie_object = object;
}
/* space is write-locked and active, object is locked and active */
+#if IMPORTANCE_INHERITANCE
+ /*
+ * We are slamming a receive right into the space, without
+ * first having been enqueued on a port destined there. So,
+ * we have to arrange to boost the task appropriately if this
+ * port has assertions (and the task wants them).
+ */
+ if (msgt_name == MACH_MSG_TYPE_PORT_RECEIVE) {
+ ipc_port_t port = ip_object_to_port(object);
+
+ if (space->is_task != TASK_NULL) {
+ task_imp = space->is_task->task_imp_base;
+ if (ipc_importance_task_is_any_receiver_type(task_imp)) {
+ assertcnt = port->ip_impcount;
+ ipc_importance_task_reference(task_imp);
+ } else {
+ task_imp = IIT_NULL;
+ }
+ }
+
+ /* take port out of limbo */
+ assert(port->ip_tempowner != 0);
+ port->ip_tempowner = 0;
+ }
+
+#endif /* IMPORTANCE_INHERITANCE */
+
kr = ipc_right_copyout(space, name, entry,
- msgt_name, overflow, object);
+ msgt_name, NULL, NULL, object);
+
/* object is unlocked */
is_write_unlock(space);
+
+#if IMPORTANCE_INHERITANCE
+ /*
+ * Add the assertions to the task that we captured before
+ */
+ if (task_imp != IIT_NULL) {
+ ipc_importance_task_hold_internal_assertion(task_imp, assertcnt);
+ ipc_importance_task_release(task_imp);
+ }
+#endif /* IMPORTANCE_INHERITANCE */
+
return kr;
}
void
ipc_object_copyout_dest(
- ipc_space_t space,
- ipc_object_t object,
- mach_msg_type_name_t msgt_name,
- mach_port_name_t *namep)
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name,
+ mach_port_name_t *namep)
{
mach_port_name_t name;
assert(IO_VALID(object));
assert(io_active(object));
- io_release(object);
-
/*
* If the space is the receiver/owner of the object,
* then we quietly consume the right and return
*/
switch (msgt_name) {
- case MACH_MSG_TYPE_PORT_SEND: {
- ipc_port_t port = (ipc_port_t) object;
+ case MACH_MSG_TYPE_PORT_SEND: {
+ ipc_port_t port = ip_object_to_port(object);
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount;
- if (port->ip_receiver == space)
+ if (port->ip_receiver == space) {
name = port->ip_receiver_name;
- else
+ } else {
name = MACH_PORT_NULL;
+ }
assert(port->ip_srights > 0);
if (--port->ip_srights == 0 &&
nsrequest = port->ip_nsrequest;
port->ip_nsrequest = IP_NULL;
mscount = port->ip_mscount;
- ip_unlock(port);
+ ipc_port_clear_sync_rcv_thread_boost_locked(port);
+ /* port unlocked */
ipc_notify_no_senders(nsrequest, mscount);
- } else
- ip_unlock(port);
+ } else {
+ ipc_port_clear_sync_rcv_thread_boost_locked(port);
+ /* port unlocked */
+ }
+
+ ip_release(port);
break;
- }
+ }
- case MACH_MSG_TYPE_PORT_SEND_ONCE: {
- ipc_port_t port = (ipc_port_t) object;
+ case MACH_MSG_TYPE_PORT_SEND_ONCE: {
+ ipc_port_t port = ip_object_to_port(object);
assert(port->ip_sorights > 0);
port->ip_sorights--;
name = port->ip_receiver_name;
- ip_unlock(port);
+ ipc_port_clear_sync_rcv_thread_boost_locked(port);
+ /* port unlocked */
+ ip_release(port);
} else {
/*
* A very bizarre case. The message
* so generate a send-once notification.
*/
- ip_reference(port); /* restore ref */
ip_unlock(port);
ipc_notify_send_once(port);
}
break;
- }
+ }
- default:
+ default:
panic("ipc_object_copyout_dest: strange rights");
name = MACH_PORT_DEAD;
}
}
/*
- * Routine: ipc_object_rename
+ * Routine: io_lock
* Purpose:
- * Rename an entry in a space.
- * Conditions:
- * Nothing locked.
- * Returns:
- * KERN_SUCCESS Renamed the entry.
- * KERN_INVALID_TASK The space was dead.
- * KERN_INVALID_NAME oname didn't denote an entry.
- * KERN_NAME_EXISTS nname already denoted an entry.
- * KERN_RESOURCE_SHORTAGE Couldn't allocate new entry.
+ * Validate, then acquire a lock on an ipc object
*/
-kern_return_t
-ipc_object_rename(
- ipc_space_t space,
- mach_port_name_t oname,
- mach_port_name_t nname)
+void
+io_lock(ipc_object_t io)
{
- ipc_entry_t oentry, nentry;
- kern_return_t kr;
-
- kr = ipc_entry_alloc_name(space, nname, &nentry);
- if (kr != KERN_SUCCESS)
- return kr;
-
- /* space is write-locked and active */
-
- if (ipc_right_inuse(space, nname, nentry)) {
- /* space is unlocked */
- return KERN_NAME_EXISTS;
- }
-
- /* don't let ipc_entry_lookup see the uninitialized new entry */
-
- if ((oname == nname) ||
- ((oentry = ipc_entry_lookup(space, oname)) == IE_NULL)) {
- ipc_entry_dealloc(space, nname, nentry);
- is_write_unlock(space);
- return KERN_INVALID_NAME;
- }
-
- kr = ipc_right_rename(space, oname, oentry, nname, nentry);
- /* space is unlocked */
- return kr;
+ ipc_object_validate(io);
+ lck_spin_lock_grp(&(io)->io_lock_data, &ipc_lck_grp);
}
/*
- * Get a label out of a port, to be used by a kernel call
- * that takes a security label as a parameter. In this case, we want
- * to use the label stored in the label handle and not the label on its
- * port.
- *
- * The port should be locked for this call. The lock protecting
- * label handle contents should not be necessary, as they can only
- * be modified when a label handle with one reference is a task label.
- * User allocated label handles can never be modified.
+ * Routine: io_lock_try
+ * Purpose:
+ * Validate, then try to acquire a lock on an object,
+ * fail if there is an existing busy lock
*/
-#if CONFIG_MACF_MACH
-struct label *io_getlabel (ipc_object_t objp)
-{
- ipc_port_t port = (ipc_port_t)objp;
- assert(io_otype(objp) == IOT_PORT);
-
- if (ip_kotype(port) == IKOT_LABELH)
- return &((ipc_labelh_t) port->ip_kobject)->lh_label;
- else
- return &port->ip_label;
+boolean_t
+io_lock_try(ipc_object_t io)
+{
+ ipc_object_validate(io);
+ return lck_spin_try_lock_grp(&(io)->io_lock_data, &ipc_lck_grp);
}
-#endif
/*
* Check whether the object is a port if so, free it. But
*/
void
io_free(
- unsigned int otype,
- ipc_object_t object)
+ unsigned int otype,
+ ipc_object_t object)
{
- ipc_port_t port;
-
if (otype == IOT_PORT) {
- port = (ipc_port_t) object;
- ipc_port_finalize(port);
+ ipc_port_finalize(ip_object_to_port(object));
}
io_lock_destroy(object);
zfree(ipc_object_zones[otype], object);