]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/ipc/ipc_object.c
xnu-7195.60.75.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_object.c
index 49b7e46900197739ac9622f3be86d32a87cd6566..5086568f6f989124be8056cbfdb6d67aa52980bd 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * 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.
  */
@@ -70,8 +70,6 @@
  *     Functions to manipulate IPC objects.
  */
 
-#include <mach_rt.h>
-
 #include <mach/mach_types.h>
 #include <mach/boolean.h>
 #include <mach/kern_return.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
@@ -106,7 +113,7 @@ zone_t ipc_object_zones[IOT_NUMBER];
 
 void
 ipc_object_reference(
-       ipc_object_t    object)
+       ipc_object_t    object)
 {
        io_reference(object);
 }
@@ -119,7 +126,7 @@ ipc_object_reference(
 
 void
 ipc_object_release(
-       ipc_object_t    object)
+       ipc_object_t    object)
 {
        io_release(object);
 }
@@ -130,28 +137,32 @@ ipc_object_release(
  *             Look up an object in a space.
  *     Conditions:
  *             Nothing locked before.  If successful, the object
- *             is returned locked.  The caller doesn't get a ref.
+ *             is returned active and locked.  The caller doesn't get a ref.
  *     Returns:
  *             KERN_SUCCESS            Object returned locked.
  *             KERN_INVALID_TASK       The space is dead.
- *             KERN_INVALID_NAME       The name doesn't denote a right.
- *             KERN_INVALID_RIGHT      Name doesn't denote the correct right.
+ *             KERN_INVALID_NAME       The name doesn't denote a right
+ *             KERN_INVALID_RIGHT      Name doesn't denote the correct right
  */
-
 kern_return_t
 ipc_object_translate(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       mach_port_right_t       right,
-       ipc_object_t            *objectp)
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       mach_port_right_t       right,
+       ipc_object_t            *objectp)
 {
        ipc_entry_t entry;
        ipc_object_t object;
        kern_return_t kr;
 
+       if (!MACH_PORT_RIGHT_VALID_TRANSLATE(right)) {
+               return KERN_INVALID_RIGHT;
+       }
+
        kr = ipc_right_lookup_read(space, name, &entry);
-       if (kr != KERN_SUCCESS)
+       if (kr != KERN_SUCCESS) {
                return kr;
+       }
        /* space is read-locked and active */
 
        if ((entry->ie_bits & MACH_PORT_TYPE(right)) == MACH_PORT_TYPE_NONE) {
@@ -165,6 +176,11 @@ ipc_object_translate(
        io_lock(object);
        is_read_unlock(space);
 
+       if (!io_active(object)) {
+               io_unlock(object);
+               return KERN_INVALID_NAME;
+       }
+
        *objectp = object;
        return KERN_SUCCESS;
 }
@@ -185,43 +201,73 @@ ipc_object_translate(
 
 kern_return_t
 ipc_object_translate_two(
-       ipc_space_t             space,
-       mach_port_name_t        name1,
-       mach_port_right_t       right1,
-       ipc_object_t            *objectp1,
-       mach_port_name_t        name2,
-       mach_port_right_t       right2,
-       ipc_object_t            *objectp2)
+       ipc_space_t             space,
+       mach_port_name_t        name1,
+       mach_port_right_t       right1,
+       ipc_object_t            *objectp1,
+       mach_port_name_t        name2,
+       mach_port_right_t       right2,
+       ipc_object_t            *objectp2)
 {
        ipc_entry_t entry1;
        ipc_entry_t entry2;
-       ipc_object_t object;
+       ipc_object_t object1, object2;
        kern_return_t kr;
+       boolean_t doguard = TRUE;
 
        kr = ipc_right_lookup_two_read(space, name1, &entry1, name2, &entry2);
-       if (kr != KERN_SUCCESS)
+       if (kr != KERN_SUCCESS) {
                return kr;
+       }
        /* space is read-locked and active */
 
        if ((entry1->ie_bits & MACH_PORT_TYPE(right1)) == MACH_PORT_TYPE_NONE) {
+               /* If looking for receive, and the entry used to hold one, give a pass on EXC_GUARD */
+               if ((right1 & MACH_PORT_RIGHT_RECEIVE) == MACH_PORT_RIGHT_RECEIVE &&
+                   (entry1->ie_bits & MACH_PORT_TYPE_EX_RECEIVE) == MACH_PORT_TYPE_EX_RECEIVE) {
+                       doguard = FALSE;
+               }
                is_read_unlock(space);
+               if (doguard) {
+                       mach_port_guard_exception(name1, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+               }
                return KERN_INVALID_RIGHT;
        }
 
        if ((entry2->ie_bits & MACH_PORT_TYPE(right2)) == MACH_PORT_TYPE_NONE) {
+               /* If looking for receive, and the entry used to hold one, give a pass on EXC_GUARD */
+               if ((right2 & MACH_PORT_RIGHT_RECEIVE) == MACH_PORT_RIGHT_RECEIVE &&
+                   (entry2->ie_bits & MACH_PORT_TYPE_EX_RECEIVE) == MACH_PORT_TYPE_EX_RECEIVE) {
+                       doguard = FALSE;
+               }
                is_read_unlock(space);
+               if (doguard) {
+                       mach_port_guard_exception(name2, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+               }
                return KERN_INVALID_RIGHT;
        }
 
-       object = entry1->ie_object;
-       assert(object != IO_NULL);
-       io_lock(object);
-       *objectp1 = object;
+       object1 = entry1->ie_object;
+       assert(object1 != IO_NULL);
+       io_lock(object1);
+       if (!io_active(object1)) {
+               io_unlock(object1);
+               is_read_unlock(space);
+               return KERN_INVALID_NAME;
+       }
 
-       object = entry2->ie_object;
-       assert(object != IO_NULL);
-       io_lock(object);
-       *objectp2 = object;
+       object2 = entry2->ie_object;
+       assert(object2 != IO_NULL);
+       io_lock(object2);
+       if (!io_active(object2)) {
+               io_unlock(object1);
+               io_unlock(object2);
+               is_read_unlock(space);
+               return KERN_INVALID_NAME;
+       }
+
+       *objectp1 = object1;
+       *objectp2 = object2;
 
        is_read_unlock(space);
        return KERN_SUCCESS;
@@ -242,15 +288,16 @@ ipc_object_translate_two(
 
 kern_return_t
 ipc_object_alloc_dead(
-       ipc_space_t             space,
-       mach_port_name_t        *namep)
+       ipc_space_t             space,
+       mach_port_name_t        *namep)
 {
        ipc_entry_t entry;
        kern_return_t kr;
 
        kr = ipc_entry_alloc(space, namep, &entry);
-       if (kr != KERN_SUCCESS)
+       if (kr != KERN_SUCCESS) {
                return kr;
+       }
        /* space is write-locked */
 
        /* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */
@@ -277,19 +324,21 @@ ipc_object_alloc_dead(
 
 kern_return_t
 ipc_object_alloc_dead_name(
-       ipc_space_t             space,
-       mach_port_name_t        name)
+       ipc_space_t             space,
+       mach_port_name_t        name)
 {
        ipc_entry_t entry;
        kern_return_t kr;
 
        kr = ipc_entry_alloc_name(space, name, &entry);
-       if (kr != KERN_SUCCESS)
+       if (kr != KERN_SUCCESS) {
                return kr;
+       }
        /* space is write-locked */
 
-       if (ipc_right_inuse(space, name, entry))
+       if (ipc_right_inuse(space, name, entry)) {
                return KERN_NAME_EXISTS;
+       }
 
        /* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */
 
@@ -306,7 +355,7 @@ ipc_object_alloc_dead_name(
  *             Allocate an object.
  *     Conditions:
  *             Nothing locked.  If successful, the object is returned locked.
- *             The space is write locked on successful return. 
+ *             The space is write locked on successful return.
  *             The caller doesn't get a reference for the object.
  *     Returns:
  *             KERN_SUCCESS            The object is allocated.
@@ -317,12 +366,12 @@ ipc_object_alloc_dead_name(
 
 kern_return_t
 ipc_object_alloc(
-       ipc_space_t             space,
-       ipc_object_type_t       otype,
-       mach_port_type_t        type,
-       mach_port_urefs_t       urefs,
-       mach_port_name_t        *namep,
-       ipc_object_t            *objectp)
+       ipc_space_t             space,
+       ipc_object_type_t       otype,
+       mach_port_type_t        type,
+       mach_port_urefs_t       urefs,
+       mach_port_name_t        *namep,
+       ipc_object_t            *objectp)
 {
        ipc_object_t object;
        ipc_entry_t entry;
@@ -334,15 +383,16 @@ ipc_object_alloc(
        assert(urefs <= MACH_PORT_UREFS_MAX);
 
        object = io_alloc(otype);
-       if (object == IO_NULL)
+       if (object == IO_NULL) {
                return KERN_RESOURCE_SHORTAGE;
+       }
 
        if (otype == IOT_PORT) {
-               ipc_port_t port = (ipc_port_t)object;
+               ipc_port_t port = ip_object_to_port(object);
 
                bzero((char *)port, sizeof(*port));
        } 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));
        }
@@ -360,10 +410,10 @@ ipc_object_alloc(
        entry->ie_object = object;
        ipc_entry_modified(space, *namep, entry);
 
+       object->io_bits = io_makebits(TRUE, otype, 0);
        io_lock(object);
 
        object->io_references = 1; /* for entry, not caller */
-       object->io_bits = io_makebits(TRUE, otype, 0);
 
        *objectp = object;
        return KERN_SUCCESS;
@@ -385,12 +435,12 @@ ipc_object_alloc(
 
 kern_return_t
 ipc_object_alloc_name(
-       ipc_space_t             space,
-       ipc_object_type_t       otype,
-       mach_port_type_t        type,
-       mach_port_urefs_t       urefs,
-       mach_port_name_t        name,
-       ipc_object_t            *objectp)
+       ipc_space_t             space,
+       ipc_object_type_t       otype,
+       mach_port_type_t        type,
+       mach_port_urefs_t       urefs,
+       mach_port_name_t        name,
+       ipc_object_t            *objectp)
 {
        ipc_object_t object;
        ipc_entry_t entry;
@@ -402,15 +452,16 @@ ipc_object_alloc_name(
        assert(urefs <= MACH_PORT_UREFS_MAX);
 
        object = io_alloc(otype);
-       if (object == IO_NULL)
+       if (object == IO_NULL) {
                return KERN_RESOURCE_SHORTAGE;
+       }
 
        if (otype == IOT_PORT) {
-               ipc_port_t port = (ipc_port_t)object;
+               ipc_port_t port = ip_object_to_port(object);
 
                bzero((char *)port, sizeof(*port));
        } else if (otype == IOT_PORT_SET) {
-               ipc_pset_t pset = (ipc_pset_t)object;
+               ipc_pset_t pset = ips_object_to_pset(object);
 
                bzero((char *)pset, sizeof(*pset));
        }
@@ -432,16 +483,36 @@ ipc_object_alloc_name(
        entry->ie_object = object;
        ipc_entry_modified(space, name, entry);
 
+       object->io_bits = io_makebits(TRUE, otype, 0);
+
        io_lock(object);
        is_write_unlock(space);
 
        object->io_references = 1; /* for entry, not caller */
-       object->io_bits = io_makebits(TRUE, otype, 0);
 
        *objectp = object;
        return KERN_SUCCESS;
 }
 
+/*     Routine:        ipc_object_validate
+ *     Purpose:
+ *             Validates an ipc port or port set as belonging to the correct
+ *             zone.
+ */
+
+void
+ipc_object_validate(
+       ipc_object_t    object)
+{
+       if (io_otype(object) != IOT_PORT_SET) {
+               zone_id_require(ZONE_ID_IPC_PORT,
+                   sizeof(struct ipc_port), object);
+       } else {
+               zone_id_require(ZONE_ID_IPC_PORT_SET,
+                   sizeof(struct ipc_pset), object);
+       }
+}
+
 /*
  *     Routine:        ipc_object_copyin_type
  *     Purpose:
@@ -450,27 +521,26 @@ ipc_object_alloc_name(
 
 mach_msg_type_name_t
 ipc_object_copyin_type(
-       mach_msg_type_name_t    msgt_name)
+       mach_msg_type_name_t    msgt_name)
 {
        switch (msgt_name) {
-
-           case MACH_MSG_TYPE_MOVE_RECEIVE:
+       case MACH_MSG_TYPE_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;
 
-           case MACH_MSG_TYPE_DISPOSE_RECEIVE:
-           case MACH_MSG_TYPE_DISPOSE_SEND:
-           case MACH_MSG_TYPE_DISPOSE_SEND_ONCE:
-               /* fall thru */
-           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;
        }
 }
@@ -492,24 +562,24 @@ ipc_object_copyin_type(
 
 kern_return_t
 ipc_object_copyin(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       mach_msg_type_name_t    msgt_name,
-       ipc_object_t            *objectp)
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       mach_msg_type_name_t    msgt_name,
+       ipc_object_t            *objectp,
+       mach_port_context_t     context,
+       mach_msg_guard_flags_t  *guard_flags,
+       ipc_kmsg_flags_t        kmsg_flags)
 {
        ipc_entry_t entry;
        ipc_port_t soright;
        ipc_port_t release_port;
        kern_return_t kr;
-       queue_head_t links_data;
-       queue_t links = &links_data;
-       wait_queue_link_t wql;
-
-#if IMPORTANCE_INHERITANCE
        int assertcnt = 0;
-#endif
 
-       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
@@ -518,27 +588,23 @@ ipc_object_copyin(
         */
 
        kr = ipc_right_lookup_write(space, name, &entry);
-       if (kr != KERN_SUCCESS)
+       if (kr != KERN_SUCCESS) {
                return kr;
+       }
        /* space is write-locked and active */
 
        release_port = IP_NULL;
        kr = ipc_right_copyin(space, name, entry,
-                             msgt_name, TRUE,
-                             objectp, &soright,
-                             &release_port,
-#if IMPORTANCE_INHERITANCE
-                             &assertcnt,
-#endif /* IMPORTANCE_INHERITANCE */
-                             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);
        }
+       is_write_unlock(space);
 
 #if IMPORTANCE_INHERITANCE
        if (0 < assertcnt && ipc_importance_task_is_any_receiver_type(current_task()->task_imp_base)) {
@@ -546,11 +612,13 @@ ipc_object_copyin(
        }
 #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;
 }
@@ -585,32 +653,34 @@ ipc_object_copyin(
 
 void
 ipc_object_copyin_from_kernel(
-       ipc_object_t            object,
-       mach_msg_type_name_t    msgt_name)
+       ipc_object_t            object,
+       mach_msg_type_name_t    msgt_name)
 {
        assert(IO_VALID(object));
 
        switch (msgt_name) {
-           case MACH_MSG_TYPE_MOVE_RECEIVE: {
-               ipc_port_t port = (ipc_port_t) object;
+       case MACH_MSG_TYPE_MOVE_RECEIVE: {
+               ipc_port_t port = ip_object_to_port(object);
 
                ip_lock(port);
-               assert(ip_active(port));
+               imq_lock(&port->ip_messages);
+               require_ip_active(port);
                if (port->ip_destination != IP_NULL) {
                        assert(port->ip_receiver == ipc_space_kernel);
+                       assert(port->ip_immovable_receive == 0);
 
                        /* relevant part of ipc_port_clear_receiver */
-                       ipc_port_set_mscount(port, 0);
-
+                       port->ip_mscount = 0;
                        port->ip_receiver_name = MACH_PORT_NULL;
                        port->ip_destination = IP_NULL;
                }
+               imq_unlock(&port->ip_messages);
                ip_unlock(port);
                break;
-           }
+       }
 
-           case MACH_MSG_TYPE_COPY_SEND: {
-               ipc_port_t port = (ipc_port_t) object;
+       case MACH_MSG_TYPE_COPY_SEND: {
+               ipc_port_t port = ip_object_to_port(object);
 
                ip_lock(port);
                if (ip_active(port)) {
@@ -620,15 +690,16 @@ ipc_object_copyin_from_kernel(
                ip_reference(port);
                ip_unlock(port);
                break;
-           }
+       }
 
-           case MACH_MSG_TYPE_MAKE_SEND: {
-               ipc_port_t port = (ipc_port_t) object;
+       case MACH_MSG_TYPE_MAKE_SEND: {
+               ipc_port_t port = ip_object_to_port(object);
 
                ip_lock(port);
                if (ip_active(port)) {
                        assert(port->ip_receiver_name != MACH_PORT_NULL);
-                       assert(port->ip_receiver == ipc_space_kernel);
+                       assert((port->ip_receiver == ipc_space_kernel) ||
+                           (port->ip_receiver->is_node_id != HOST_LOCAL_NODE));
                        port->ip_mscount++;
                }
 
@@ -636,34 +707,33 @@ ipc_object_copyin_from_kernel(
                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);
                if (ip_active(port)) {
                        assert(port->ip_receiver_name != MACH_PORT_NULL);
                }
-               port->ip_sorights++;
-               ip_reference(port);
+               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");
        }
 }
@@ -681,26 +751,26 @@ ipc_object_copyin_from_kernel(
 
 void
 ipc_object_destroy(
-       ipc_object_t            object,
-       mach_msg_type_name_t    msgt_name)
+       ipc_object_t            object,
+       mach_msg_type_name_t    msgt_name)
 {
        assert(IO_VALID(object));
        assert(io_otype(object) == IOT_PORT);
 
        switch (msgt_name) {
-           case MACH_MSG_TYPE_PORT_SEND:
-               ipc_port_release_send((ipc_port_t) object);
+       case MACH_MSG_TYPE_PORT_SEND:
+               ipc_port_release_send(ip_object_to_port(object));
                break;
 
-           case MACH_MSG_TYPE_PORT_SEND_ONCE:
-               ipc_notify_send_once((ipc_port_t) object);
+       case MACH_MSG_TYPE_PORT_SEND_ONCE:
+               ipc_notify_send_once(ip_object_to_port(object));
                break;
 
-           case MACH_MSG_TYPE_PORT_RECEIVE:
-               ipc_port_release_receive((ipc_port_t) object);
+       case MACH_MSG_TYPE_PORT_RECEIVE:
+               ipc_port_release_receive(ip_object_to_port(object));
                break;
 
-           default:
+       default:
                panic("ipc_object_destroy: strange rights");
        }
 }
@@ -717,30 +787,123 @@ ipc_object_destroy(
 
 void
 ipc_object_destroy_dest(
-       ipc_object_t            object,
-       mach_msg_type_name_t    msgt_name)
+       ipc_object_t            object,
+       mach_msg_type_name_t    msgt_name)
 {
        assert(IO_VALID(object));
        assert(io_otype(object) == IOT_PORT);
 
        switch (msgt_name) {
-           case MACH_MSG_TYPE_PORT_SEND:
-               ipc_port_release_send((ipc_port_t) object);
+       case MACH_MSG_TYPE_PORT_SEND:
+               ipc_port_release_send(ip_object_to_port(object));
                break;
 
-           case MACH_MSG_TYPE_PORT_SEND_ONCE:
-               if (io_active(object) && 
-                   !ip_full_kernel((ipc_port_t) object))
-                       ipc_notify_send_once((ipc_port_t) object);
-               else
-                       ipc_port_release_sonce((ipc_port_t) object);
+       case MACH_MSG_TYPE_PORT_SEND_ONCE:
+               if (io_active(object) &&
+                   !ip_full_kernel(ip_object_to_port(object))) {
+                       ipc_notify_send_once(ip_object_to_port(object));
+               } else {
+                       ipc_port_release_sonce(ip_object_to_port(object));
+               }
                break;
 
-           default:
+       default:
                panic("ipc_object_destroy_dest: strange rights");
        }
 }
 
+/*
+ *     Routine:        ipc_object_insert_send_right
+ *     Purpose:
+ *             Insert a send right into an object already in the space.
+ *             The specified name must already point to a valid object.
+ *
+ *             Note: This really is a combined copyin()/copyout(),
+ *             that avoids most of the overhead of being implemented that way.
+ *
+ *             This is the fastpath for mach_port_insert_right.
+ *
+ *     Conditions:
+ *             Nothing locked.
+ *
+ *             msgt_name must be MACH_MSG_TYPE_MAKE_SEND_ONCE or
+ *             MACH_MSG_TYPE_MOVE_SEND_ONCE.
+ *
+ *     Returns:
+ *             KERN_SUCCESS            Copied out object, consumed ref.
+ *             KERN_INVALID_TASK       The space is dead.
+ *             KERN_INVALID_NAME       Name doesn't exist in space.
+ *             KERN_INVALID_CAPABILITY The object is dead.
+ *             KERN_RIGHT_EXISTS       Space has rights under another name.
+ */
+kern_return_t
+ipc_object_insert_send_right(
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       mach_msg_type_name_t    msgt_name)
+{
+       ipc_entry_bits_t bits;
+       ipc_object_t object;
+       ipc_entry_t entry;
+       kern_return_t kr;
+
+       assert(msgt_name == MACH_MSG_TYPE_MAKE_SEND ||
+           msgt_name == MACH_MSG_TYPE_COPY_SEND);
+
+       kr = ipc_right_lookup_write(space, name, &entry);
+       if (kr != KERN_SUCCESS) {
+               return kr;
+       }
+       /* space is write-locked and active */
+
+       if (!IO_VALID(entry->ie_object)) {
+               is_write_unlock(space);
+               return KERN_INVALID_CAPABILITY;
+       }
+
+       bits = entry->ie_bits;
+       object = entry->ie_object;
+
+       io_lock(object);
+       if (!io_active(object)) {
+               kr = KERN_INVALID_CAPABILITY;
+       } else if (msgt_name == MACH_MSG_TYPE_MAKE_SEND) {
+               if (bits & MACH_PORT_TYPE_RECEIVE) {
+                       ipc_port_t port = ip_object_to_port(object);
+                       port->ip_mscount++;
+                       if ((bits & MACH_PORT_TYPE_SEND) == 0) {
+                               port->ip_srights++;
+                               bits |= MACH_PORT_TYPE_SEND;
+                       }
+                       /* leave urefs pegged to maximum if it overflowed */
+                       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+                               bits += 1; /* increment urefs */
+                       }
+                       entry->ie_bits = bits;
+                       ipc_entry_modified(space, name, entry);
+                       kr = KERN_SUCCESS;
+               } else {
+                       kr = KERN_INVALID_RIGHT;
+               }
+       } else { // MACH_MSG_TYPE_COPY_SEND
+               if (bits & MACH_PORT_TYPE_SEND) {
+                       /* leave urefs pegged to maximum if it overflowed */
+                       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+                               entry->ie_bits = bits + 1; /* increment urefs */
+                       }
+                       ipc_entry_modified(space, name, entry);
+                       kr = KERN_SUCCESS;
+               } else {
+                       kr = KERN_INVALID_RIGHT;
+               }
+       }
+
+       io_unlock(object);
+       is_write_unlock(space);
+
+       return kr;
+}
+
 /*
  *     Routine:        ipc_object_copyout
  *     Purpose:
@@ -760,12 +923,14 @@ ipc_object_destroy_dest(
 
 kern_return_t
 ipc_object_copyout(
-       ipc_space_t             space,
-       ipc_object_t            object,
-       mach_msg_type_name_t    msgt_name,
-       boolean_t               overflow,
-       mach_port_name_t        *namep)
+       ipc_space_t             space,
+       ipc_object_t            object,
+       mach_msg_type_name_t    msgt_name,
+       mach_port_context_t     *context,
+       mach_msg_guard_flags_t  *guard_flags,
+       mach_port_name_t        *namep)
 {
+       struct knote *kn = current_thread()->ith_knote;
        mach_port_name_t name;
        ipc_entry_t entry;
        kern_return_t kr;
@@ -773,6 +938,11 @@ ipc_object_copyout(
        assert(IO_VALID(object));
        assert(io_otype(object) == IOT_PORT);
 
+       if (ITH_KNOTE_VALID(kn, msgt_name)) {
+               filt_machport_turnstile_prepare_lazily(kn,
+                   msgt_name, ip_object_to_port(object));
+       }
+
        is_write_lock(space);
 
        for (;;) {
@@ -782,22 +952,23 @@ ipc_object_copyout(
                }
 
                if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
-                   ipc_right_reverse(space, object, &name, &entry)) { 
+                   ipc_right_reverse(space, object, &name, &entry)) {
                        /* object is locked and active */
 
                        assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
                        break;
                }
 
+
                name = CAST_MACH_PORT_TO_NAME(object);
                kr = ipc_entry_get(space, &name, &entry);
                if (kr != KERN_SUCCESS) {
                        /* unlocks/locks space, so must start again */
 
                        kr = ipc_entry_grow_table(space, ITS_SIZE_NONE);
-                       if (kr != KERN_SUCCESS)
+                       if (kr != KERN_SUCCESS) {
                                return kr; /* space is unlocked */
-
+                       }
                        continue;
                }
 
@@ -812,6 +983,14 @@ ipc_object_copyout(
                        return KERN_INVALID_CAPABILITY;
                }
 
+               /* Don't actually copyout rights we aren't allowed to */
+               if (!ip_label_check(space, ip_object_to_port(object), msgt_name)) {
+                       io_unlock(object);
+                       ipc_entry_dealloc(space, name, entry);
+                       is_write_unlock(space);
+                       return KERN_INVALID_CAPABILITY;
+               }
+
                entry->ie_object = object;
                break;
        }
@@ -819,13 +998,14 @@ ipc_object_copyout(
        /* space is write-locked and active, object is locked and active */
 
        kr = ipc_right_copyout(space, name, entry,
-                              msgt_name, overflow, object);
+           msgt_name, context, guard_flags, object);
 
        /* object is unlocked */
        is_write_unlock(space);
 
-       if (kr == KERN_SUCCESS)
+       if (kr == KERN_SUCCESS) {
                *namep = name;
+       }
        return kr;
 }
 
@@ -850,11 +1030,10 @@ ipc_object_copyout(
 
 kern_return_t
 ipc_object_copyout_name(
-       ipc_space_t             space,
-       ipc_object_t            object,
-       mach_msg_type_name_t    msgt_name,
-       boolean_t               overflow,
-       mach_port_name_t        name)
+       ipc_space_t             space,
+       ipc_object_t            object,
+       mach_msg_type_name_t    msgt_name,
+       mach_port_name_t        name)
 {
        mach_port_name_t oname;
        ipc_entry_t oentry;
@@ -870,8 +1049,9 @@ ipc_object_copyout_name(
        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) &&
@@ -881,8 +1061,9 @@ ipc_object_copyout_name(
                if (name != oname) {
                        io_unlock(object);
 
-                       if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
+                       if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) {
                                ipc_entry_dealloc(space, name, entry);
+                       }
 
                        is_write_unlock(space);
                        return KERN_RIGHT_EXISTS;
@@ -891,8 +1072,9 @@ ipc_object_copyout_name(
                assert(entry == oentry);
                assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
        } else {
-               if (ipc_right_inuse(space, name, entry))
+               if (ipc_right_inuse(space, name, entry)) {
                        return KERN_NAME_EXISTS;
+               }
 
                assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
                assert(entry->ie_object == IO_NULL);
@@ -905,6 +1087,14 @@ ipc_object_copyout_name(
                        return KERN_INVALID_CAPABILITY;
                }
 
+               /* Don't actually copyout rights we aren't allowed to */
+               if (!ip_label_check(space, ip_object_to_port(object), msgt_name)) {
+                       io_unlock(object);
+                       ipc_entry_dealloc(space, name, entry);
+                       is_write_unlock(space);
+                       return KERN_INVALID_CAPABILITY;
+               }
+
                entry->ie_object = object;
        }
 
@@ -918,13 +1108,15 @@ ipc_object_copyout_name(
         * port has assertions (and the task wants them).
         */
        if (msgt_name == MACH_MSG_TYPE_PORT_RECEIVE) {
-               ipc_port_t port = (ipc_port_t)object;
+               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;
                        }
                }
 
@@ -936,7 +1128,7 @@ ipc_object_copyout_name(
 #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);
@@ -969,18 +1161,16 @@ ipc_object_copyout_name(
 
 void
 ipc_object_copyout_dest(
-       ipc_space_t             space,
-       ipc_object_t            object,
-       mach_msg_type_name_t    msgt_name,
-       mach_port_name_t        *namep)
+       ipc_space_t             space,
+       ipc_object_t            object,
+       mach_msg_type_name_t    msgt_name,
+       mach_port_name_t        *namep)
 {
        mach_port_name_t name;
 
        assert(IO_VALID(object));
        assert(io_active(object));
 
-       io_release(object);
-
        /*
         *      If the space is the receiver/owner of the object,
         *      then we quietly consume the right and return
@@ -989,15 +1179,16 @@ ipc_object_copyout_dest(
         */
 
        switch (msgt_name) {
-           case MACH_MSG_TYPE_PORT_SEND: {
-               ipc_port_t port = (ipc_port_t) object;
+       case MACH_MSG_TYPE_PORT_SEND: {
+               ipc_port_t port = ip_object_to_port(object);
                ipc_port_t nsrequest = IP_NULL;
                mach_port_mscount_t mscount;
 
-               if (port->ip_receiver == space)
+               if (port->ip_receiver == space) {
                        name = port->ip_receiver_name;
-               else
+               } else {
                        name = MACH_PORT_NULL;
+               }
 
                assert(port->ip_srights > 0);
                if (--port->ip_srights == 0 &&
@@ -1005,15 +1196,20 @@ ipc_object_copyout_dest(
                        nsrequest = port->ip_nsrequest;
                        port->ip_nsrequest = IP_NULL;
                        mscount = port->ip_mscount;
-                       ip_unlock(port);
+                       ipc_port_clear_sync_rcv_thread_boost_locked(port);
+                       /* port unlocked */
                        ipc_notify_no_senders(nsrequest, mscount);
-               } else
-                       ip_unlock(port);
+               } else {
+                       ipc_port_clear_sync_rcv_thread_boost_locked(port);
+                       /* port unlocked */
+               }
+
+               ip_release(port);
                break;
-           }
+       }
 
-           case MACH_MSG_TYPE_PORT_SEND_ONCE: {
-               ipc_port_t port = (ipc_port_t) object;
+       case MACH_MSG_TYPE_PORT_SEND_ONCE: {
+               ipc_port_t port = ip_object_to_port(object);
 
                assert(port->ip_sorights > 0);
 
@@ -1022,7 +1218,9 @@ ipc_object_copyout_dest(
 
                        port->ip_sorights--;
                        name = port->ip_receiver_name;
-                       ip_unlock(port);
+                       ipc_port_clear_sync_rcv_thread_boost_locked(port);
+                       /* port unlocked */
+                       ip_release(port);
                } else {
                        /*
                         *      A very bizarre case.  The message
@@ -1033,7 +1231,6 @@ ipc_object_copyout_dest(
                         *      so generate a send-once notification.
                         */
 
-                       ip_reference(port); /* restore ref */
                        ip_unlock(port);
 
                        ipc_notify_send_once(port);
@@ -1041,9 +1238,9 @@ ipc_object_copyout_dest(
                }
 
                break;
-           }
+       }
 
-           default:
+       default:
                panic("ipc_object_copyout_dest: strange rights");
                name = MACH_PORT_DEAD;
        }
@@ -1052,51 +1249,30 @@ ipc_object_copyout_dest(
 }
 
 /*
- *     Routine:        ipc_object_rename
+ *     Routine:        io_lock
  *     Purpose:
- *             Rename an entry in a space.
- *     Conditions:
- *             Nothing locked.
- *     Returns:
- *             KERN_SUCCESS            Renamed the entry.
- *             KERN_INVALID_TASK       The space was dead.
- *             KERN_INVALID_NAME       oname didn't denote an entry.
- *             KERN_NAME_EXISTS        nname already denoted an entry.
- *             KERN_RESOURCE_SHORTAGE  Couldn't allocate new entry.
+ *             Validate, then acquire a lock on an ipc object
  */
 
-kern_return_t
-ipc_object_rename(
-       ipc_space_t             space,
-       mach_port_name_t        oname,
-       mach_port_name_t        nname)
+void
+io_lock(ipc_object_t io)
 {
-       ipc_entry_t oentry, nentry;
-       kern_return_t kr;
-       
-       kr = ipc_entry_alloc_name(space, nname, &nentry);
-       if (kr != KERN_SUCCESS)
-               return kr;
-
-       /* space is write-locked and active */
-
-       if (ipc_right_inuse(space, nname, nentry)) {
-               /* space is unlocked */
-               return KERN_NAME_EXISTS;
-       }
-
-       /* don't let ipc_entry_lookup see the uninitialized new entry */
+       ipc_object_validate(io);
+       lck_spin_lock_grp(&(io)->io_lock_data, &ipc_lck_grp);
+}
 
-       if ((oname == nname) ||
-           ((oentry = ipc_entry_lookup(space, oname)) == IE_NULL)) {
-               ipc_entry_dealloc(space, nname, nentry);
-               is_write_unlock(space);
-               return KERN_INVALID_NAME;
-       }
+/*
+ *     Routine:        io_lock_try
+ *     Purpose:
+ *             Validate, then try to acquire a lock on an object,
+ *             fail if there is an existing busy lock
+ */
 
-       kr = ipc_right_rename(space, oname, oentry, nname, nentry);
-       /* space is unlocked */
-       return kr;
+boolean_t
+io_lock_try(ipc_object_t io)
+{
+       ipc_object_validate(io);
+       return lck_spin_try_lock_grp(&(io)->io_lock_data, &ipc_lck_grp);
 }
 
 /*
@@ -1105,14 +1281,11 @@ ipc_object_rename(
  */
 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);