]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/ipc/ipc_right.c
xnu-6153.61.1.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_right.c
index 9f228a43a7732bfe2149e912651254189f0835ea..00a256e177fa5ddd57bbb21b392495cbe90e0eb1 100644 (file)
@@ -1,52 +1,65 @@
 /*
- * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
  *
- * @APPLE_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 Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * @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
+ * 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.
+ *
+ * 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.
- * 
- * @APPLE_LICENSE_HEADER_END@
+ * 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_FREE_COPYRIGHT@
  */
-/* 
+/*
  * Mach Operating System
  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
  * All Rights Reserved.
- * 
+ *
  * Permission to use, copy, modify and distribute this software and its
  * documentation is hereby granted, provided that both the copyright
  * notice and this permission notice appear in all copies of the
  * software, derivative works or modified versions, and any portions
  * thereof, and that both notices appear in supporting documentation.
- * 
+ *
  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
- * 
+ *
  * Carnegie Mellon requests users of this software to return to
- * 
+ *
  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
  *  School of Computer Science
  *  Carnegie Mellon University
  *  Pittsburgh PA 15213-3890
- * 
+ *
  * any improvements or extensions that they make and grant Carnegie Mellon
  * the rights to redistribute these changes.
  */
+/*
+ * NOTICE: This file was modified by McAfee Research in 2004 to introduce
+ * support for mandatory and extensible security protections.  This notice
+ * is included in support of clause 2.2 (b) of the Apple Public License,
+ * Version 2.0.
+ * Copyright (c) 2005-2006 SPARTA, Inc.
+ */
 /*
  */
 /*
@@ -62,6 +75,7 @@
 #include <mach/port.h>
 #include <mach/message.h>
 #include <kern/assert.h>
+#include <kern/ipc_kobject.h>
 #include <kern/misc_protos.h>
 #include <ipc/port.h>
 #include <ipc/ipc_entry.h>
@@ -73,6 +87,8 @@
 #include <ipc/ipc_right.h>
 #include <ipc/ipc_notify.h>
 #include <ipc/ipc_table.h>
+#include <ipc/ipc_importance.h>
+#include <security/mac_mach_internal.h>
 
 /*
  *     Routine:        ipc_right_lookup_write
 
 kern_return_t
 ipc_right_lookup_write(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       ipc_entry_t             *entryp)
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       ipc_entry_t             *entryp)
 {
        ipc_entry_t entry;
 
@@ -98,7 +114,7 @@ ipc_right_lookup_write(
 
        is_write_lock(space);
 
-       if (!space->is_active) {
+       if (!is_active(space)) {
                is_write_unlock(space);
                return KERN_INVALID_TASK;
        }
@@ -127,11 +143,11 @@ ipc_right_lookup_write(
 
 kern_return_t
 ipc_right_lookup_two_write(
-       ipc_space_t             space,
-       mach_port_name_t        name1,
-       ipc_entry_t             *entryp1,
-       mach_port_name_t        name2,
-       ipc_entry_t             *entryp2)
+       ipc_space_t             space,
+       mach_port_name_t        name1,
+       ipc_entry_t             *entryp1,
+       mach_port_name_t        name2,
+       ipc_entry_t             *entryp2)
 {
        ipc_entry_t entry1;
        ipc_entry_t entry2;
@@ -140,17 +156,19 @@ ipc_right_lookup_two_write(
 
        is_write_lock(space);
 
-       if (!space->is_active) {
+       if (!is_active(space)) {
                is_write_unlock(space);
                return KERN_INVALID_TASK;
        }
 
        if ((entry1 = ipc_entry_lookup(space, name1)) == IE_NULL) {
                is_write_unlock(space);
+               mach_port_guard_exception(name1, 0, 0, kGUARD_EXC_INVALID_NAME);
                return KERN_INVALID_NAME;
        }
        if ((entry2 = ipc_entry_lookup(space, name2)) == IE_NULL) {
                is_write_unlock(space);
+               mach_port_guard_exception(name2, 0, 0, kGUARD_EXC_INVALID_NAME);
                return KERN_INVALID_NAME;
        }
        *entryp1 = entry1;
@@ -172,10 +190,10 @@ ipc_right_lookup_two_write(
 
 boolean_t
 ipc_right_reverse(
-       ipc_space_t             space,
-       ipc_object_t            object,
-       mach_port_name_t        *namep,
-       ipc_entry_t             *entryp)
+       ipc_space_t             space,
+       ipc_object_t            object,
+       mach_port_name_t        *namep,
+       ipc_entry_t             *entryp)
 {
        ipc_port_t port;
        mach_port_name_t name;
@@ -183,10 +201,10 @@ ipc_right_reverse(
 
        /* would switch on io_otype to handle multiple types of object */
 
-       assert(space->is_active);
+       assert(is_active(space));
        assert(io_otype(object) == IOT_PORT);
 
-       port = (ipc_port_t) object;
+       port = ip_object_to_port(object);
 
        ip_lock(port);
        if (!ip_active(port)) {
@@ -203,17 +221,17 @@ ipc_right_reverse(
 
                assert(entry != IE_NULL);
                assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
-               assert(port == (ipc_port_t) entry->ie_object);
+               assert(port == ip_object_to_port(entry->ie_object));
 
                *namep = name;
                *entryp = entry;
                return TRUE;
        }
 
-       if (ipc_hash_lookup(space, (ipc_object_t) port, namep, entryp)) {
+       if (ipc_hash_lookup(space, ip_to_object(port), namep, entryp)) {
                assert((entry = *entryp) != IE_NULL);
                assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND);
-               assert(port == (ipc_port_t) entry->ie_object);
+               assert(port == ip_object_to_port(entry->ie_object));
 
                return TRUE;
        }
@@ -229,12 +247,6 @@ ipc_right_reverse(
  *             registered send-once right.  If notify is IP_NULL,
  *             just cancels the previously registered request.
  *
- *             This interacts with the IE_BITS_COMPAT, because they
- *             both use ie_request.  If this is a compat entry, then
- *             previous always gets IP_NULL.  If notify is IP_NULL,
- *             then the entry remains a compat entry.  Otherwise
- *             the real dead-name request is registered and the entry
- *             is no longer a compat entry.
  *     Conditions:
  *             Nothing locked.  May allocate memory.
  *             Only consumes/returns refs if successful.
@@ -245,125 +257,172 @@ ipc_right_reverse(
  *             KERN_INVALID_RIGHT      Name doesn't denote port/dead rights.
  *             KERN_INVALID_ARGUMENT   Name denotes dead name, but
  *                     immediate is FALSE or notify is IP_NULL.
- *             KERN_UREFS_OVERFLOW     Name denotes dead name, but
- *                     generating immediate notif. would overflow urefs.
  *             KERN_RESOURCE_SHORTAGE  Couldn't allocate memory.
  */
 
 kern_return_t
-ipc_right_dnrequest(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       boolean_t               immediate,
-       ipc_port_t              notify,
-       ipc_port_t              *previousp)
+ipc_right_request_alloc(
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       boolean_t               immediate,
+       boolean_t               send_possible,
+       ipc_port_t              notify,
+       ipc_port_t              *previousp)
 {
-       ipc_port_t previous;
+       ipc_port_request_index_t prev_request;
+       ipc_port_t previous = IP_NULL;
+       ipc_entry_t entry;
+       kern_return_t kr;
+
+#if IMPORTANCE_INHERITANCE
+       boolean_t needboost = FALSE;
+#endif /* IMPORTANCE_INHERITANCE */
 
        for (;;) {
-               ipc_entry_t entry;
-               ipc_entry_bits_t bits;
-               kern_return_t kr;
+               ipc_port_t port = IP_NULL;
 
                kr = ipc_right_lookup_write(space, name, &entry);
-               if (kr != KERN_SUCCESS)
+               if (kr != KERN_SUCCESS) {
                        return kr;
+               }
+
                /* space is write-locked and active */
-               bits = entry->ie_bits;
-               if (bits & MACH_PORT_TYPE_PORT_RIGHTS) {
-                       ipc_port_t port;
-                       ipc_port_request_index_t request;
 
-                       port = (ipc_port_t) entry->ie_object;
+               prev_request = entry->ie_request;
+
+               /* if nothing to do or undo, we're done */
+               if (notify == IP_NULL && prev_request == IE_REQ_NONE) {
+                       is_write_unlock(space);
+                       *previousp = IP_NULL;
+                       return KERN_SUCCESS;
+               }
+
+               /* see if the entry is of proper type for requests */
+               if (entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) {
+                       ipc_port_request_index_t new_request;
+
+                       port = ip_object_to_port(entry->ie_object);
                        assert(port != IP_NULL);
 
-                       if (!ipc_right_check(space, port, name, entry)) {
+                       if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
                                /* port is locked and active */
 
+                               /* if no new request, just cancel previous */
                                if (notify == IP_NULL) {
-                                       previous = ipc_right_dncancel_macro(
-                                                  space, port, name, entry);
-
+                                       if (prev_request != IE_REQ_NONE) {
+                                               previous = ipc_port_request_cancel(port, name, prev_request);
+                                       }
                                        ip_unlock(port);
+                                       entry->ie_request = IE_REQ_NONE;
+                                       ipc_entry_modified(space, name, entry);
                                        is_write_unlock(space);
                                        break;
                                }
 
                                /*
-                                *      If a registered soright exists,
-                                *      want to atomically switch with it.
-                                *      If ipc_port_dncancel finds us a
-                                *      soright, then the following
-                                *      ipc_port_dnrequest will reuse
-                                *      that slot, so we are guaranteed
-                                *      not to unlock and retry.
+                                * send-once rights, kernel objects, and non-full other queues
+                                * fire immediately (if immediate specified).
                                 */
+                               if (send_possible && immediate &&
+                                   ((entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE) ||
+                                   port->ip_receiver == ipc_space_kernel || !ip_full(port))) {
+                                       if (prev_request != IE_REQ_NONE) {
+                                               previous = ipc_port_request_cancel(port, name, prev_request);
+                                       }
+                                       ip_unlock(port);
+                                       entry->ie_request = IE_REQ_NONE;
+                                       ipc_entry_modified(space, name, entry);
+                                       is_write_unlock(space);
+
+                                       ipc_notify_send_possible(notify, name);
+                                       break;
+                               }
 
-                               previous = ipc_right_dncancel_macro(space,
-                                               port, name, entry);
+                               /*
+                                * If there is a previous request, free it.  Any subsequent
+                                * allocation cannot fail, thus assuring an atomic swap.
+                                */
+                               if (prev_request != IE_REQ_NONE) {
+                                       previous = ipc_port_request_cancel(port, name, prev_request);
+                               }
 
-                               kr = ipc_port_dnrequest(port, name, notify,
-                                                       &request);
+#if IMPORTANCE_INHERITANCE
+                               kr = ipc_port_request_alloc(port, name, notify,
+                                   send_possible, immediate,
+                                   &new_request, &needboost);
+#else
+                               kr = ipc_port_request_alloc(port, name, notify,
+                                   send_possible, immediate,
+                                   &new_request);
+#endif /* IMPORTANCE_INHERITANCE */
                                if (kr != KERN_SUCCESS) {
                                        assert(previous == IP_NULL);
                                        is_write_unlock(space);
 
-                                       kr = ipc_port_dngrow(port,
-                                                            ITS_SIZE_NONE);
+                                       kr = ipc_port_request_grow(port, ITS_SIZE_NONE);
                                        /* port is unlocked */
-                                       if (kr != KERN_SUCCESS)
+
+                                       if (kr != KERN_SUCCESS) {
                                                return kr;
+                                       }
 
                                        continue;
                                }
 
-                               assert(request != 0);
-                               ip_unlock(port);
 
-                               entry->ie_request = request;
+                               assert(new_request != IE_REQ_NONE);
+                               entry->ie_request = new_request;
+                               ipc_entry_modified(space, name, entry);
                                is_write_unlock(space);
-                               break;
-                       } else {
 
-                         /*
-                          * Our capability bits were changed by ipc_right_check
-                          * because it found an inactive port and removed our
-                          * references to it (converting our entry into a dead
-                          * one).  Reload the bits (and obviously we can't use
-                          * the port name anymore).
-                          */
-                         bits = entry->ie_bits;
+#if IMPORTANCE_INHERITANCE
+                               if (needboost == TRUE) {
+                                       if (ipc_port_importance_delta(port, IPID_OPTION_SENDPOSSIBLE, 1) == FALSE) {
+                                               ip_unlock(port);
+                                       }
+                               } else
+#endif /* IMPORTANCE_INHERITANCE */
+                               ip_unlock(port);
 
+                               break;
                        }
-
-                       assert(bits & MACH_PORT_TYPE_DEAD_NAME);
+                       /* entry may have changed to dead-name by ipc_right_check() */
                }
 
-               if ((bits & MACH_PORT_TYPE_DEAD_NAME) &&
-                   immediate && (notify != IP_NULL)) {
-                       mach_port_urefs_t urefs = IE_BITS_UREFS(bits);
+               /* treat send_possible requests as immediate w.r.t. dead-name */
+               if ((send_possible || immediate) && notify != IP_NULL &&
+                   (entry->ie_bits & MACH_PORT_TYPE_DEAD_NAME)) {
+                       mach_port_urefs_t urefs = IE_BITS_UREFS(entry->ie_bits);
 
-                       assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
                        assert(urefs > 0);
 
-                       if (MACH_PORT_UREFS_OVERFLOW(urefs, 1)) {
-                               is_write_unlock(space);
-                               return KERN_UREFS_OVERFLOW;
+                       /* leave urefs pegged to maximum if it overflowed */
+                       if (urefs < MACH_PORT_UREFS_MAX) {
+                               (entry->ie_bits)++; /* increment urefs */
                        }
+                       ipc_entry_modified(space, name, entry);
 
-                       (entry->ie_bits)++; /* increment urefs */
                        is_write_unlock(space);
 
+                       if (port != IP_NULL) {
+                               ip_release(port);
+                       }
+
                        ipc_notify_dead_name(notify, name);
                        previous = IP_NULL;
                        break;
                }
 
+               kr = (entry->ie_bits & MACH_PORT_TYPE_PORT_OR_DEAD) ?
+                   KERN_INVALID_ARGUMENT : KERN_INVALID_RIGHT;
+
                is_write_unlock(space);
-               if (bits & MACH_PORT_TYPE_PORT_OR_DEAD)
-                       return KERN_INVALID_ARGUMENT;
-               else
-                       return KERN_INVALID_RIGHT;
+
+               if (port != IP_NULL) {
+                       ip_release(port);
+               }
+
+               return kr;
        }
 
        *previousp = previous;
@@ -371,9 +430,9 @@ ipc_right_dnrequest(
 }
 
 /*
- *     Routine:        ipc_right_dncancel
+ *     Routine:        ipc_right_request_cancel
  *     Purpose:
- *             Cancel a dead-name request and return the send-once right.
+ *             Cancel a notification request and return the send-once right.
  *             Afterwards, entry->ie_request == 0.
  *     Conditions:
  *             The space must be write-locked; the port must be locked.
@@ -381,21 +440,25 @@ ipc_right_dnrequest(
  */
 
 ipc_port_t
-ipc_right_dncancel(
-       __unused ipc_space_t            space,
-       ipc_port_t                      port,
-       mach_port_name_t                name,
-       ipc_entry_t                     entry)
+ipc_right_request_cancel(
+       __unused ipc_space_t            space,
+       ipc_port_t                      port,
+       mach_port_name_t                name,
+       ipc_entry_t                     entry)
 {
-       ipc_port_t dnrequest;
+       ipc_port_t previous;
 
-       assert(ip_active(port));
-       assert(port == (ipc_port_t) entry->ie_object);
+       require_ip_active(port);
+       assert(port == ip_object_to_port(entry->ie_object));
 
-       dnrequest = ipc_port_dncancel(port, name, entry->ie_request);
-       entry->ie_request = 0;
+       if (entry->ie_request == IE_REQ_NONE) {
+               return IP_NULL;
+       }
 
-       return dnrequest;
+       previous = ipc_port_request_cancel(port, name, entry->ie_request);
+       entry->ie_request = IE_REQ_NONE;
+       ipc_entry_modified(space, name, entry);
+       return previous;
 }
 
 /*
@@ -410,9 +473,9 @@ ipc_right_dncancel(
 
 boolean_t
 ipc_right_inuse(
-       ipc_space_t                     space,
-       __unused mach_port_name_t       name,
-       ipc_entry_t                     entry)
+       ipc_space_t                     space,
+       __unused mach_port_name_t       name,
+       ipc_entry_t                     entry)
 {
        if (IE_BITS_TYPE(entry->ie_bits) != MACH_PORT_TYPE_NONE) {
                is_write_unlock(space);
@@ -425,30 +488,38 @@ ipc_right_inuse(
  *     Routine:        ipc_right_check
  *     Purpose:
  *             Check if the port has died.  If it has,
+ *              and IPC_RIGHT_COPYIN_FLAGS_ALLOW_DEAD_SEND_ONCE is not
+ *              passed and it is not a send once right then
  *             clean up the entry and return TRUE.
  *     Conditions:
  *             The space is write-locked; the port is not locked.
- *             If returns FALSE, the port is also locked and active.
- *             Otherwise, entry is converted to a dead name, freeing
- *             a reference to port.
+ *             If returns FALSE, the port is also locked.
+ *             Otherwise, entry is converted to a dead name.
+ *
+ *             Caller is responsible for a reference to port if it
+ *             had died (returns TRUE).
  */
 
 boolean_t
 ipc_right_check(
-       ipc_space_t             space,
-       ipc_port_t              port,
-       mach_port_name_t        name,
-       ipc_entry_t             entry)
+       ipc_space_t              space,
+       ipc_port_t               port,
+       mach_port_name_t         name,
+       ipc_entry_t              entry,
+       ipc_right_copyin_flags_t flags)
 {
        ipc_entry_bits_t bits;
 
-       assert(space->is_active);
-       assert(port == (ipc_port_t) entry->ie_object);
+       assert(is_active(space));
+       assert(port == ip_object_to_port(entry->ie_object));
 
        ip_lock(port);
-       if (ip_active(port))
+       if (ip_active(port) ||
+           ((flags & IPC_RIGHT_COPYIN_FLAGS_ALLOW_DEAD_SEND_ONCE) &&
+           entry->ie_request == IE_REQ_NONE &&
+           (entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE))) {
                return FALSE;
-       ip_unlock(port);
+       }
 
        /* this was either a pure send right or a send-once right */
 
@@ -456,53 +527,65 @@ ipc_right_check(
        assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
        assert(IE_BITS_UREFS(bits) > 0);
 
-       if (bits & MACH_PORT_TYPE_SEND) {   
-                assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
-        } else {
-                assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
-                assert(IE_BITS_UREFS(bits) == 1);
-        }
+       if (bits & MACH_PORT_TYPE_SEND) {
+               assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
+               assert(IE_BITS_UREFS(bits) > 0);
+               assert(port->ip_srights > 0);
+               port->ip_srights--;
+       } else {
+               assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
+               assert(IE_BITS_UREFS(bits) == 1);
+               assert(port->ip_sorights > 0);
+               port->ip_sorights--;
+       }
+       ip_unlock(port);
 
+       /*
+        * delete SEND rights from ipc hash.
+        */
 
-       ipc_port_release(port);
+       if ((bits & MACH_PORT_TYPE_SEND) != 0) {
+               ipc_hash_delete(space, ip_to_object(port), name, entry);
+       }
 
        /* convert entry to dead name */
+       bits = (bits & ~IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME;
 
-       if ((bits & MACH_PORT_TYPE_SEND) && !(bits & MACH_PORT_TYPE_RECEIVE))
-               ipc_hash_delete(space, (ipc_object_t)port, name, entry);
-
-       bits = (bits &~ IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME;
-       
        /*
         * If there was a notification request outstanding on this
-        * name, and since the port went dead, that notification
-        * must already be on its way up from the port layer. We
-        * don't need the index of the notification port anymore.
+        * name, and the port went dead, that notification
+        * must already be on its way up from the port layer.
         *
-        * JMM - We also add a reference to the entry since the
-        * notification only carries the name and NOT a reference
-        * (or right). This makes for pretty loose reference
-        * counting, since it is only happenstance that we
-        * detected the notification in progress like this.
-        * But most (all?) calls that try to deal with this entry
-        * will also come through here, so the reference gets added
-        * before the entry gets used eventually (I would rather it
-        * be explicit in the notification generation, though)
+        * Add the reference that the notification carries. It
+        * is done here, and not in the notification delivery,
+        * because the latter doesn't have a space reference and
+        * trying to actually move a send-right reference would
+        * get short-circuited into a MACH_PORT_DEAD by IPC. Since
+        * all calls that deal with the right eventually come
+        * through here, it has the same result.
+        *
+        * Once done, clear the request index so we only account
+        * for it once.
         */
-       if (entry->ie_request != 0) {
-               assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
-               entry->ie_request = 0; 
-               bits++; 
+       if (entry->ie_request != IE_REQ_NONE) {
+               if (ipc_port_request_type(port, name, entry->ie_request) != 0) {
+                       /* if urefs are pegged due to overflow, leave them pegged */
+                       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+                               bits++; /* increment urefs */
+                       }
+               }
+               entry->ie_request = IE_REQ_NONE;
        }
        entry->ie_bits = bits;
        entry->ie_object = IO_NULL;
+       ipc_entry_modified(space, name, entry);
        return TRUE;
 }
 
 /*
- *     Routine:        ipc_right_clean
+ *     Routine:        ipc_right_terminate
  *     Purpose:
- *             Cleans up an entry in a dead space.
+ *             Cleans up an entry in a terminated space.
  *             The entry isn't deallocated or removed
  *             from reverse hash tables.
  *     Conditions:
@@ -510,10 +593,10 @@ ipc_right_check(
  */
 
 void
-ipc_right_clean(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       ipc_entry_t             entry)
+ipc_right_terminate(
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       ipc_entry_t             entry)
 {
        ipc_entry_bits_t bits;
        mach_port_type_t type;
@@ -521,7 +604,7 @@ ipc_right_clean(
        bits = entry->ie_bits;
        type = IE_BITS_TYPE(bits);
 
-       assert(!space->is_active);
+       assert(!is_active(space));
 
        /*
         *      IE_BITS_COMPAT/ipc_right_dncancel doesn't have this
@@ -533,30 +616,29 @@ ipc_right_clean(
         */
 
        switch (type) {
-           case MACH_PORT_TYPE_DEAD_NAME:
-               assert(entry->ie_request == 0);
+       case MACH_PORT_TYPE_DEAD_NAME:
+               assert(entry->ie_request == IE_REQ_NONE);
                assert(entry->ie_object == IO_NULL);
                break;
 
-           case MACH_PORT_TYPE_PORT_SET: {
-               ipc_pset_t pset = (ipc_pset_t) entry->ie_object;
+       case MACH_PORT_TYPE_PORT_SET: {
+               ipc_pset_t pset = ips_object_to_pset(entry->ie_object);
 
-               assert(entry->ie_request == 0);
+               assert(entry->ie_request == IE_REQ_NONE);
                assert(pset != IPS_NULL);
 
                ips_lock(pset);
                assert(ips_active(pset));
-
-               ipc_pset_destroy(pset); /* consumes ref, unlocks */
+               ipc_pset_destroy(space, pset); /* consumes ref, unlocks */
                break;
-           }
-
-           case MACH_PORT_TYPE_SEND:
-           case MACH_PORT_TYPE_RECEIVE:
-           case MACH_PORT_TYPE_SEND_RECEIVE:
-           case MACH_PORT_TYPE_SEND_ONCE: {
-               ipc_port_t port = (ipc_port_t) entry->ie_object;
-               ipc_port_t dnrequest;
+       }
+
+       case MACH_PORT_TYPE_SEND:
+       case MACH_PORT_TYPE_RECEIVE:
+       case MACH_PORT_TYPE_SEND_RECEIVE:
+       case MACH_PORT_TYPE_SEND_ONCE: {
+               ipc_port_t port = ip_object_to_port(entry->ie_object);
+               ipc_port_t request;
                ipc_port_t nsrequest = IP_NULL;
                mach_port_mscount_t mscount = 0;
 
@@ -564,13 +646,13 @@ ipc_right_clean(
                ip_lock(port);
 
                if (!ip_active(port)) {
+                       ip_unlock(port);
                        ip_release(port);
-                       ip_check_unlock(port);
                        break;
                }
 
-               dnrequest = ipc_right_dncancel_macro(space, port, 
-                                       name, entry);
+               request = ipc_right_request_cancel_macro(space, port,
+                   name, entry);
 
                if (type & MACH_PORT_TYPE_SEND) {
                        assert(port->ip_srights > 0);
@@ -588,30 +670,32 @@ ipc_right_clean(
                        assert(port->ip_receiver_name == name);
                        assert(port->ip_receiver == space);
 
-                       ipc_port_clear_receiver(port);
-                       ipc_port_destroy(port); /* consumes our ref, unlocks */
+                       ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */
                } else if (type & MACH_PORT_TYPE_SEND_ONCE) {
                        assert(port->ip_sorights > 0);
+                       port->ip_reply_context = 0;
                        ip_unlock(port);
 
                        ipc_notify_send_once(port); /* consumes our ref */
                } else {
                        assert(port->ip_receiver != space);
 
+                       ip_unlock(port);
                        ip_release(port);
-                       ip_unlock(port); /* port is active */
                }
 
-               if (nsrequest != IP_NULL)
+               if (nsrequest != IP_NULL) {
                        ipc_notify_no_senders(nsrequest, mscount);
+               }
 
-               if (dnrequest != IP_NULL)
-                       ipc_notify_port_deleted(dnrequest, name);
+               if (request != IP_NULL) {
+                       ipc_notify_port_deleted(request, name);
+               }
                break;
-           }
+       }
 
-           default:
-               panic("ipc_right_clean: strange type");
+       default:
+               panic("ipc_right_terminate: strange type - 0x%x", type);
        }
 }
 
@@ -620,7 +704,7 @@ ipc_right_clean(
  *     Purpose:
  *             Destroys an entry in a space.
  *     Conditions:
- *             The space is write-locked.
+ *             The space is write-locked (returns unlocked).
  *             The space must be active.
  *     Returns:
  *             KERN_SUCCESS            The entry was destroyed.
@@ -628,9 +712,11 @@ ipc_right_clean(
 
 kern_return_t
 ipc_right_destroy(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       ipc_entry_t             entry)
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       ipc_entry_t             entry,
+       boolean_t               check_guard,
+       uint64_t                guard)
 {
        ipc_entry_bits_t bits;
        mach_port_type_t type;
@@ -639,65 +725,81 @@ ipc_right_destroy(
        entry->ie_bits &= ~IE_BITS_TYPE_MASK;
        type = IE_BITS_TYPE(bits);
 
-       assert(space->is_active);
+       assert(is_active(space));
 
        switch (type) {
-           case MACH_PORT_TYPE_DEAD_NAME:
-               assert(entry->ie_request == 0);
+       case MACH_PORT_TYPE_DEAD_NAME:
+               assert(entry->ie_request == IE_REQ_NONE);
                assert(entry->ie_object == IO_NULL);
 
                ipc_entry_dealloc(space, name, entry);
+               is_write_unlock(space);
                break;
 
-           case MACH_PORT_TYPE_PORT_SET: {
-               ipc_pset_t pset = (ipc_pset_t) entry->ie_object;
+       case MACH_PORT_TYPE_PORT_SET: {
+               ipc_pset_t pset = ips_object_to_pset(entry->ie_object);
 
-               assert(entry->ie_request == 0);
+               assert(entry->ie_request == IE_REQ_NONE);
                assert(pset != IPS_NULL);
 
                entry->ie_object = IO_NULL;
                ipc_entry_dealloc(space, name, entry);
 
                ips_lock(pset);
-               assert(ips_active(pset));
+               is_write_unlock(space);
 
-               ipc_pset_destroy(pset); /* consumes ref, unlocks */
+               assert(ips_active(pset));
+               ipc_pset_destroy(space, pset); /* consumes ref, unlocks */
                break;
-           }
+       }
 
-           case MACH_PORT_TYPE_SEND:
-           case MACH_PORT_TYPE_RECEIVE:
-           case MACH_PORT_TYPE_SEND_RECEIVE:
-           case MACH_PORT_TYPE_SEND_ONCE: {
-               ipc_port_t port = (ipc_port_t) entry->ie_object;
+       case MACH_PORT_TYPE_SEND:
+       case MACH_PORT_TYPE_RECEIVE:
+       case MACH_PORT_TYPE_SEND_RECEIVE:
+       case MACH_PORT_TYPE_SEND_ONCE: {
+               ipc_port_t port = ip_object_to_port(entry->ie_object);
                ipc_port_t nsrequest = IP_NULL;
                mach_port_mscount_t mscount = 0;
-               ipc_port_t dnrequest;
+               ipc_port_t request;
 
                assert(port != IP_NULL);
 
-               if (type == MACH_PORT_TYPE_SEND)
-                       ipc_hash_delete(space, (ipc_object_t) port,
-                                       name, entry);
+               if (type == MACH_PORT_TYPE_SEND) {
+                       ipc_hash_delete(space, ip_to_object(port), name, entry);
+               }
 
                ip_lock(port);
 
                if (!ip_active(port)) {
                        assert((type & MACH_PORT_TYPE_RECEIVE) == 0);
-                       ip_release(port);
-                       ip_check_unlock(port);
-
-                       entry->ie_request = 0;
+                       ip_unlock(port);
+                       entry->ie_request = IE_REQ_NONE;
                        entry->ie_object = IO_NULL;
                        ipc_entry_dealloc(space, name, entry);
-
+                       is_write_unlock(space);
+                       ip_release(port);
                        break;
                }
 
-               dnrequest = ipc_right_dncancel_macro(space, port, name, entry);
+               /* For receive rights, check for guarding */
+               if ((type & MACH_PORT_TYPE_RECEIVE) &&
+                   (check_guard) && (port->ip_guarded) &&
+                   (guard != port->ip_context)) {
+                       /* Guard Violation */
+                       uint64_t portguard = port->ip_context;
+                       ip_unlock(port);
+                       is_write_unlock(space);
+                       /* Raise mach port guard exception */
+                       mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_DESTROY);
+                       return KERN_INVALID_RIGHT;
+               }
+
+
+               request = ipc_right_request_cancel_macro(space, port, name, entry);
 
                entry->ie_object = IO_NULL;
                ipc_entry_dealloc(space, name, entry);
+               is_write_unlock(space);
 
                if (type & MACH_PORT_TYPE_SEND) {
                        assert(port->ip_srights > 0);
@@ -711,32 +813,36 @@ ipc_right_destroy(
                }
 
                if (type & MACH_PORT_TYPE_RECEIVE) {
-                       assert(ip_active(port));
+                       require_ip_active(port);
                        assert(port->ip_receiver == space);
 
-                       ipc_port_clear_receiver(port);
-                       ipc_port_destroy(port); /* consumes our ref, unlocks */
+                       ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */
                } else if (type & MACH_PORT_TYPE_SEND_ONCE) {
                        assert(port->ip_sorights > 0);
+                       port->ip_reply_context = 0;
                        ip_unlock(port);
 
                        ipc_notify_send_once(port); /* consumes our ref */
                } else {
                        assert(port->ip_receiver != space);
 
-                       ip_release(port);
                        ip_unlock(port);
+                       ip_release(port);
                }
 
-               if (nsrequest != IP_NULL)
+               if (nsrequest != IP_NULL) {
                        ipc_notify_no_senders(nsrequest, mscount);
+               }
+
+               if (request != IP_NULL) {
+                       ipc_notify_port_deleted(request, name);
+               }
+
 
-               if (dnrequest != IP_NULL)
-                       ipc_notify_port_deleted(dnrequest, name);
                break;
-           }
+       }
 
-           default:
+       default:
                panic("ipc_right_destroy: strange type");
        }
 
@@ -746,7 +852,7 @@ ipc_right_destroy(
 /*
  *     Routine:        ipc_right_dealloc
  *     Purpose:
- *             Releases a send/send-once/dead-name user ref.
+ *             Releases a send/send-once/dead-name/port_set user ref.
  *             Like ipc_right_delta with a delta of -1,
  *             but looks at the entry to determine the right.
  *     Conditions:
@@ -759,11 +865,11 @@ ipc_right_destroy(
 
 kern_return_t
 ipc_right_dealloc(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       ipc_entry_t             entry)
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       ipc_entry_t             entry)
 {
-
+       ipc_port_t port = IP_NULL;
        ipc_entry_bits_t bits;
        mach_port_type_t type;
 
@@ -771,45 +877,79 @@ ipc_right_dealloc(
        type = IE_BITS_TYPE(bits);
 
 
-       assert(space->is_active);
+       assert(is_active(space));
 
        switch (type) {
-           case MACH_PORT_TYPE_DEAD_NAME: {
-           dead_name:
+       case MACH_PORT_TYPE_PORT_SET: {
+               ipc_pset_t pset;
+
+               assert(IE_BITS_UREFS(bits) == 0);
+               assert(entry->ie_request == IE_REQ_NONE);
+
+               pset = ips_object_to_pset(entry->ie_object);
+               assert(pset != IPS_NULL);
+
+               entry->ie_object = IO_NULL;
+               ipc_entry_dealloc(space, name, entry);
+
+               ips_lock(pset);
+               assert(ips_active(pset));
+               is_write_unlock(space);
+
+               ipc_pset_destroy(space, pset); /* consumes ref, unlocks */
+               break;
+       }
+
+       case MACH_PORT_TYPE_DEAD_NAME: {
+dead_name:
 
                assert(IE_BITS_UREFS(bits) > 0);
-               assert(entry->ie_request == 0);
+               assert(entry->ie_request == IE_REQ_NONE);
                assert(entry->ie_object == IO_NULL);
 
                if (IE_BITS_UREFS(bits) == 1) {
                        ipc_entry_dealloc(space, name, entry);
+               } else {
+                       /* if urefs are pegged due to overflow, leave them pegged */
+                       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+                               entry->ie_bits = bits - 1; /* decrement urefs */
+                       }
+                       ipc_entry_modified(space, name, entry);
                }
-               else
-                       entry->ie_bits = bits-1; /* decrement urefs */
-
                is_write_unlock(space);
+
+               /* release any port that got converted to dead name below */
+               if (port != IP_NULL) {
+                       ip_release(port);
+               }
                break;
-           }
+       }
 
-           case MACH_PORT_TYPE_SEND_ONCE: {
-               ipc_port_t port, dnrequest;
+       case MACH_PORT_TYPE_SEND_ONCE: {
+               ipc_port_t request;
 
                assert(IE_BITS_UREFS(bits) == 1);
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
-               if (ipc_right_check(space, port, name, entry)) {
-
+               if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
                        bits = entry->ie_bits;
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
-                       goto dead_name;
+                       goto dead_name;     /* it will release port */
                }
                /* port is locked and active */
 
                assert(port->ip_sorights > 0);
 
-               dnrequest = ipc_right_dncancel_macro(space, port, name, entry);
+               /*
+                * clear any reply context:
+                * no one will be sending the response b/c we are destroying
+                * the single, outstanding send once right.
+                */
+               port->ip_reply_context = 0;
+
+               request = ipc_right_request_cancel_macro(space, port, name, entry);
                ip_unlock(port);
 
                entry->ie_object = IO_NULL;
@@ -819,27 +959,27 @@ ipc_right_dealloc(
 
                ipc_notify_send_once(port);
 
-               if (dnrequest != IP_NULL)
-                       ipc_notify_port_deleted(dnrequest, name);
+               if (request != IP_NULL) {
+                       ipc_notify_port_deleted(request, name);
+               }
                break;
-           }
+       }
 
-           case MACH_PORT_TYPE_SEND: {
-               ipc_port_t port;
-               ipc_port_t dnrequest = IP_NULL;
+       case MACH_PORT_TYPE_SEND: {
+               ipc_port_t request = IP_NULL;
                ipc_port_t nsrequest = IP_NULL;
                mach_port_mscount_t mscount =  0;
 
 
                assert(IE_BITS_UREFS(bits) > 0);
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
-               if (ipc_right_check(space, port, name, entry)) {
+               if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
                        bits = entry->ie_bits;
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
-                       goto dead_name;
+                       goto dead_name;     /* it will release port */
                }
                /* port is locked and active */
 
@@ -854,42 +994,46 @@ ipc_right_dealloc(
                                }
                        }
 
-                       dnrequest = ipc_right_dncancel_macro(space, port,
-                                                            name, entry);
-                       ipc_hash_delete(space, (ipc_object_t) port,
-                                       name, entry);
+                       request = ipc_right_request_cancel_macro(space, port,
+                           name, entry);
+                       ipc_hash_delete(space, ip_to_object(port), name, entry);
 
-                       ip_release(port);
+                       ip_unlock(port);
                        entry->ie_object = IO_NULL;
                        ipc_entry_dealloc(space, name, entry);
+                       is_write_unlock(space);
+                       ip_release(port);
+               } else {
+                       ip_unlock(port);
+                       /* if urefs are pegged due to overflow, leave them pegged */
+                       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+                               entry->ie_bits = bits - 1; /* decrement urefs */
+                       }
+                       ipc_entry_modified(space, name, entry);
+                       is_write_unlock(space);
+               }
 
-               } else
-                       entry->ie_bits = bits-1; /* decrement urefs */
-
-               /* even if dropped a ref, port is active */
-               ip_unlock(port);
-               is_write_unlock(space);
-
-               if (nsrequest != IP_NULL)
+               if (nsrequest != IP_NULL) {
                        ipc_notify_no_senders(nsrequest, mscount);
+               }
 
-               if (dnrequest != IP_NULL)
-                       ipc_notify_port_deleted(dnrequest, name);
+               if (request != IP_NULL) {
+                       ipc_notify_port_deleted(request, name);
+               }
                break;
-           }
+       }
 
-           case MACH_PORT_TYPE_SEND_RECEIVE: {
-               ipc_port_t port;
+       case MACH_PORT_TYPE_SEND_RECEIVE: {
                ipc_port_t nsrequest = IP_NULL;
                mach_port_mscount_t mscount = 0;
 
                assert(IE_BITS_UREFS(bits) > 0);
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
                ip_lock(port);
-               assert(ip_active(port));
+               require_ip_active(port);
                assert(port->ip_receiver_name == name);
                assert(port->ip_receiver == space);
                assert(port->ip_srights > 0);
@@ -903,21 +1047,28 @@ ipc_right_dealloc(
                                }
                        }
 
-                       entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK |
-                                                 MACH_PORT_TYPE_SEND);
-               } else
-                       entry->ie_bits = bits-1; /* decrement urefs */
-
+                       entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK |
+                           MACH_PORT_TYPE_SEND);
+               } else {
+                       /* if urefs are pegged due to overflow, leave them pegged */
+                       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+                               entry->ie_bits = bits - 1; /* decrement urefs */
+                       }
+               }
                ip_unlock(port);
+
+               ipc_entry_modified(space, name, entry);
                is_write_unlock(space);
 
-               if (nsrequest != IP_NULL)
+               if (nsrequest != IP_NULL) {
                        ipc_notify_no_senders(nsrequest, mscount);
+               }
                break;
-           }
+       }
 
-           default:
+       default:
                is_write_unlock(space);
+               mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
                return KERN_INVALID_RIGHT;
        }
 
@@ -936,21 +1087,20 @@ ipc_right_dealloc(
  *             KERN_SUCCESS            Count was modified.
  *             KERN_INVALID_RIGHT      Entry has wrong type.
  *             KERN_INVALID_VALUE      Bad delta for the right.
- *             KERN_UREFS_OVERFLOW     OK delta, except would overflow.
  */
 
 kern_return_t
 ipc_right_delta(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       ipc_entry_t             entry,
-       mach_port_right_t       right,
-       mach_port_delta_t       delta)
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       ipc_entry_t             entry,
+       mach_port_right_t       right,
+       mach_port_delta_t       delta)
 {
+       ipc_port_t port = IP_NULL;
        ipc_entry_bits_t bits;
-       
-       bits = entry->ie_bits;
 
+       bits = entry->ie_bits;
 
 /*
  *     The following is used (for case MACH_PORT_RIGHT_DEAD_NAME) in the
@@ -960,59 +1110,65 @@ ipc_right_delta(
  *     we postpone doing so when we are holding the space lock.
  */
 
-       assert(space->is_active);
+       assert(is_active(space));
        assert(right < MACH_PORT_RIGHT_NUMBER);
 
        /* Rights-specific restrictions and operations. */
 
        switch (right) {
-           case MACH_PORT_RIGHT_PORT_SET: {
+       case MACH_PORT_RIGHT_PORT_SET: {
                ipc_pset_t pset;
 
-               if ((bits & MACH_PORT_TYPE_PORT_SET) == 0)
+               if ((bits & MACH_PORT_TYPE_PORT_SET) == 0) {
+                       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
                        goto invalid_right;
+               }
 
                assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_PORT_SET);
                assert(IE_BITS_UREFS(bits) == 0);
-               assert(entry->ie_request == 0);
+               assert(entry->ie_request == IE_REQ_NONE);
 
-               if (delta == 0)
+               if (delta == 0) {
                        goto success;
+               }
 
-               if (delta != -1)
+               if (delta != -1) {
                        goto invalid_value;
+               }
 
-               pset = (ipc_pset_t) entry->ie_object;
+               pset = ips_object_to_pset(entry->ie_object);
                assert(pset != IPS_NULL);
 
-
-
                entry->ie_object = IO_NULL;
                ipc_entry_dealloc(space, name, entry);
 
-
                ips_lock(pset);
                assert(ips_active(pset));
                is_write_unlock(space);
 
-               ipc_pset_destroy(pset); /* consumes ref, unlocks */
+               ipc_pset_destroy(space, pset); /* consumes ref, unlocks */
                break;
-           }
+       }
 
-           case MACH_PORT_RIGHT_RECEIVE: {
-               ipc_port_t port;
-               ipc_port_t dnrequest = IP_NULL;
+       case MACH_PORT_RIGHT_RECEIVE: {
+               ipc_port_t request = IP_NULL;
 
-               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
+               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
+                       if ((bits & MACH_PORT_TYPE_EX_RECEIVE) == 0) {
+                               mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+                       }
                        goto invalid_right;
+               }
 
-               if (delta == 0)
+               if (delta == 0) {
                        goto success;
+               }
 
-               if (delta != -1)
+               if (delta != -1) {
                        goto invalid_value;
+               }
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
                /*
@@ -1022,65 +1178,99 @@ ipc_right_delta(
                 */
 
                ip_lock(port);
-               assert(ip_active(port));
+               require_ip_active(port);
                assert(port->ip_receiver_name == name);
                assert(port->ip_receiver == space);
 
+               /* Mach Port Guard Checking */
+               if (port->ip_guarded) {
+                       uint64_t portguard = port->ip_context;
+                       ip_unlock(port);
+                       is_write_unlock(space);
+                       /* Raise mach port guard exception */
+                       mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_MOD_REFS);
+                       goto guard_failure;
+               }
+
                if (bits & MACH_PORT_TYPE_SEND) {
                        assert(IE_BITS_TYPE(bits) ==
-                                       MACH_PORT_TYPE_SEND_RECEIVE);
+                           MACH_PORT_TYPE_SEND_RECEIVE);
                        assert(IE_BITS_UREFS(bits) > 0);
-                       assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
                        assert(port->ip_srights > 0);
 
-                       /*
-                        *      The remaining send right turns into a
-                        *      dead name.  Notice we don't decrement
-                        *      ip_srights, generate a no-senders notif,
-                        *      or use ipc_right_dncancel, because the
-                        *      port is destroyed "first".
-                        */
-                       bits &= ~IE_BITS_TYPE_MASK;
-                       bits |= MACH_PORT_TYPE_DEAD_NAME;
-                       if (entry->ie_request) {
-                               entry->ie_request = 0;
-                               bits++;
+                       if (port->ip_pdrequest != NULL) {
+                               /*
+                                * Since another task has requested a
+                                * destroy notification for this port, it
+                                * isn't actually being destroyed - the receive
+                                * right is just being moved to another task.
+                                * Since we still have one or more send rights,
+                                * we need to record the loss of the receive
+                                * right and enter the remaining send right
+                                * into the hash table.
+                                */
+                               ipc_entry_modified(space, name, entry);
+                               entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE;
+                               entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE;
+                               ipc_hash_insert(space, ip_to_object(port),
+                                   name, entry);
+                               ip_reference(port);
+                       } else {
+                               /*
+                                *      The remaining send right turns into a
+                                *      dead name.  Notice we don't decrement
+                                *      ip_srights, generate a no-senders notif,
+                                *      or use ipc_right_dncancel, because the
+                                *      port is destroyed "first".
+                                */
+                               bits &= ~IE_BITS_TYPE_MASK;
+                               bits |= (MACH_PORT_TYPE_DEAD_NAME | MACH_PORT_TYPE_EX_RECEIVE);
+                               if (entry->ie_request) {
+                                       entry->ie_request = IE_REQ_NONE;
+                                       /* if urefs are pegged due to overflow, leave them pegged */
+                                       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+                                               bits++; /* increment urefs */
+                                       }
+                               }
+                               entry->ie_bits = bits;
+                               entry->ie_object = IO_NULL;
+                               ipc_entry_modified(space, name, entry);
                        }
-                       entry->ie_bits = bits;
-                       entry->ie_object = IO_NULL;
                } else {
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
                        assert(IE_BITS_UREFS(bits) == 0);
 
-                       dnrequest = ipc_right_dncancel_macro(space, port,
-                                                            name, entry);
+                       request = ipc_right_request_cancel_macro(space, port,
+                           name, entry);
                        entry->ie_object = IO_NULL;
                        ipc_entry_dealloc(space, name, entry);
                }
                is_write_unlock(space);
 
-               ipc_port_clear_receiver(port);
-               ipc_port_destroy(port); /* consumes ref, unlocks */
+               ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */
 
-               if (dnrequest != IP_NULL)
-                       ipc_notify_port_deleted(dnrequest, name);
+               if (request != IP_NULL) {
+                       ipc_notify_port_deleted(request, name);
+               }
                break;
-           }
+       }
 
-           case MACH_PORT_RIGHT_SEND_ONCE: {
-               ipc_port_t port, dnrequest;
+       case MACH_PORT_RIGHT_SEND_ONCE: {
+               ipc_port_t request;
 
-               if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0)
+               if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
                        goto invalid_right;
+               }
 
                assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
                assert(IE_BITS_UREFS(bits) == 1);
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
-               if (ipc_right_check(space, port, name, entry)) {
+               if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
                        assert(!(entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE));
+                       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
                        goto invalid_right;
                }
                /* port is locked and active */
@@ -1097,7 +1287,14 @@ ipc_right_delta(
                        goto success;
                }
 
-               dnrequest = ipc_right_dncancel_macro(space, port, name, entry);
+               /*
+                * clear any reply context:
+                * no one will be sending the response b/c we are destroying
+                * the single, outstanding send once right.
+                */
+               port->ip_reply_context = 0;
+
+               request = ipc_right_request_cancel_macro(space, port, name, entry);
                ip_unlock(port);
 
                entry->ie_object = IO_NULL;
@@ -1107,67 +1304,111 @@ ipc_right_delta(
 
                ipc_notify_send_once(port);
 
-               if (dnrequest != IP_NULL)
-                       ipc_notify_port_deleted(dnrequest, name);
+               if (request != IP_NULL) {
+                       ipc_notify_port_deleted(request, name);
+               }
                break;
-           }
+       }
 
-           case MACH_PORT_RIGHT_DEAD_NAME: {
+       case MACH_PORT_RIGHT_DEAD_NAME: {
+               ipc_port_t relport = IP_NULL;
                mach_port_urefs_t urefs;
 
                if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
-                       ipc_port_t port;
-
-                       port = (ipc_port_t) entry->ie_object;
+                       port = ip_object_to_port(entry->ie_object);
                        assert(port != IP_NULL);
 
-                       if (!ipc_right_check(space, port, name, entry)) {
+                       if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
                                /* port is locked and active */
                                ip_unlock(port);
+                               port = IP_NULL;
+                               mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
                                goto invalid_right;
                        }
                        bits = entry->ie_bits;
-               } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0)
+                       relport = port;
+                       port = IP_NULL;
+               } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0) {
+                       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
                        goto invalid_right;
+               }
 
                assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
                assert(IE_BITS_UREFS(bits) > 0);
                assert(entry->ie_object == IO_NULL);
-               assert(entry->ie_request == 0);
+               assert(entry->ie_request == IE_REQ_NONE);
 
-               urefs = IE_BITS_UREFS(bits);
-               if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta))
+               if (delta > ((mach_port_delta_t)MACH_PORT_UREFS_MAX) ||
+                   delta < (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
                        goto invalid_value;
-               if (MACH_PORT_UREFS_OVERFLOW(urefs, delta))
-                       goto urefs_overflow;
+               }
+
+               urefs = IE_BITS_UREFS(bits);
+
+               if (urefs == MACH_PORT_UREFS_MAX) {
+                       /*
+                        * urefs are pegged due to an overflow
+                        * only a delta removing all refs at once can change it
+                        */
+
+                       if (delta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
+                               delta = 0;
+                       }
+               } else {
+                       if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) {
+                               goto invalid_value;
+                       }
+                       if (MACH_PORT_UREFS_OVERFLOW(urefs, delta)) {
+                               /* leave urefs pegged to maximum if it overflowed */
+                               delta = MACH_PORT_UREFS_MAX - urefs;
+                       }
+               }
 
                if ((urefs + delta) == 0) {
                        ipc_entry_dealloc(space, name, entry);
-               }
-               else
+               } else if (delta != 0) {
                        entry->ie_bits = bits + delta;
+                       ipc_entry_modified(space, name, entry);
+               }
 
                is_write_unlock(space);
 
+               if (relport != IP_NULL) {
+                       ip_release(relport);
+               }
+
                break;
-           }
+       }
 
-           case MACH_PORT_RIGHT_SEND: {
+       case MACH_PORT_RIGHT_SEND: {
                mach_port_urefs_t urefs;
-               ipc_port_t port;
-               ipc_port_t dnrequest = IP_NULL;
+               ipc_port_t request = IP_NULL;
                ipc_port_t nsrequest = IP_NULL;
+               ipc_port_t port_to_release = IP_NULL;
                mach_port_mscount_t mscount = 0;
 
-               if ((bits & MACH_PORT_TYPE_SEND) == 0)
+               if ((bits & MACH_PORT_TYPE_SEND) == 0) {
+                       /* invalid right exception only when not live/dead confusion */
+                       if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0
+#if !defined(AE_MAKESENDRIGHT_FIXED)
+                           /*
+                            * AE tries to add single send right without knowing if it already owns one.
+                            * But if it doesn't, it should own the receive right and delta should be 1.
+                            */
+                           && (((bits & MACH_PORT_TYPE_RECEIVE) == 0) || (delta != 1))
+#endif
+                           ) {
+                               mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+                       }
                        goto invalid_right;
+               }
 
-               /* maximum urefs for send is MACH_PORT_UREFS_MAX-1 */
+               /* maximum urefs for send is MACH_PORT_UREFS_MAX */
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
-               if (ipc_right_check(space, port, name, entry)) {
+               if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
                        assert((entry->ie_bits & MACH_PORT_TYPE_SEND) == 0);
                        goto invalid_right;
                }
@@ -1175,14 +1416,32 @@ ipc_right_delta(
 
                assert(port->ip_srights > 0);
 
-               urefs = IE_BITS_UREFS(bits);
-               if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) {
+               if (delta > ((mach_port_delta_t)MACH_PORT_UREFS_MAX) ||
+                   delta < (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
                        ip_unlock(port);
                        goto invalid_value;
                }
-               if (MACH_PORT_UREFS_OVERFLOW(urefs+1, delta)) {
-                       ip_unlock(port);
-                       goto urefs_overflow;
+
+               urefs = IE_BITS_UREFS(bits);
+
+               if (urefs == MACH_PORT_UREFS_MAX) {
+                       /*
+                        * urefs are pegged due to an overflow
+                        * only a delta removing all refs at once can change it
+                        */
+
+                       if (delta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
+                               delta = 0;
+                       }
+               } else {
+                       if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) {
+                               ip_unlock(port);
+                               goto invalid_value;
+                       }
+                       if (MACH_PORT_UREFS_OVERFLOW(urefs, delta)) {
+                               /* leave urefs pegged to maximum if it overflowed */
+                               delta = MACH_PORT_UREFS_MAX - urefs;
+                       }
                }
 
                if ((urefs + delta) == 0) {
@@ -1197,103 +1456,339 @@ ipc_right_delta(
                        if (bits & MACH_PORT_TYPE_RECEIVE) {
                                assert(port->ip_receiver_name == name);
                                assert(port->ip_receiver == space);
+                               ip_unlock(port);
                                assert(IE_BITS_TYPE(bits) ==
-                                               MACH_PORT_TYPE_SEND_RECEIVE);
+                                   MACH_PORT_TYPE_SEND_RECEIVE);
 
-                               entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|
-                                                      MACH_PORT_TYPE_SEND);
+                               entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK |
+                                   MACH_PORT_TYPE_SEND);
+                               ipc_entry_modified(space, name, entry);
                        } else {
                                assert(IE_BITS_TYPE(bits) ==
-                                               MACH_PORT_TYPE_SEND);
+                                   MACH_PORT_TYPE_SEND);
 
-                               dnrequest = ipc_right_dncancel_macro(space, port,
-                                                                    name, entry);
-                               ipc_hash_delete(space, (ipc_object_t) port,
-                                               name, entry);
+                               request = ipc_right_request_cancel_macro(space, port,
+                                   name, entry);
+                               ipc_hash_delete(space, ip_to_object(port),
+                                   name, entry);
 
-                               ip_release(port);
+                               ip_unlock(port);
+                               port_to_release = port;
 
                                entry->ie_object = IO_NULL;
                                ipc_entry_dealloc(space, name, entry);
                        }
-               } else
+               } else if (delta != 0) {
+                       ip_unlock(port);
                        entry->ie_bits = bits + delta;
+                       ipc_entry_modified(space, name, entry);
+               } else {
+                       ip_unlock(port);
+               }
 
-               /* even if dropped a ref, port is active */
-               ip_unlock(port);
                is_write_unlock(space);
 
-               if (nsrequest != IP_NULL)
+               if (port_to_release != IP_NULL) {
+                       ip_release(port_to_release);
+               }
+
+               if (nsrequest != IP_NULL) {
                        ipc_notify_no_senders(nsrequest, mscount);
+               }
 
-               if (dnrequest != IP_NULL)
-                       ipc_notify_port_deleted(dnrequest, name);
+               if (request != IP_NULL) {
+                       ipc_notify_port_deleted(request, name);
+               }
                break;
-           }
-
-           default:
-               panic("ipc_right_delta: strange right");
        }
 
-       return KERN_SUCCESS;
+       case MACH_PORT_RIGHT_LABELH:
+               goto invalid_right;
+
+       default:
+               panic("ipc_right_delta: strange right %d for 0x%x (%p) in space:%p",
+                   right, name, (void *)entry, (void *)space);
+       }
 
-    success:
+       return KERN_SUCCESS;
+
+success:
        is_write_unlock(space);
        return KERN_SUCCESS;
 
-    invalid_right:
+invalid_right:
        is_write_unlock(space);
+       if (port != IP_NULL) {
+               ip_release(port);
+       }
        return KERN_INVALID_RIGHT;
 
-    invalid_value:
+invalid_value:
        is_write_unlock(space);
+       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_VALUE);
        return KERN_INVALID_VALUE;
 
-    urefs_overflow:
+guard_failure:
+       return KERN_INVALID_RIGHT;
+}
+
+/*
+ *     Routine:        ipc_right_destruct
+ *     Purpose:
+ *             Deallocates the receive right and modifies the
+ *             user-reference count for the send rights as requested.
+ *     Conditions:
+ *             The space is write-locked, and is unlocked upon return.
+ *             The space must be active.
+ *     Returns:
+ *             KERN_SUCCESS            Count was modified.
+ *             KERN_INVALID_RIGHT      Entry has wrong type.
+ *             KERN_INVALID_VALUE      Bad delta for the right.
+ */
+
+kern_return_t
+ipc_right_destruct(
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       ipc_entry_t             entry,
+       mach_port_delta_t       srdelta,
+       uint64_t                guard)
+{
+       ipc_port_t port = IP_NULL;
+       ipc_entry_bits_t bits;
+
+       mach_port_urefs_t urefs;
+       ipc_port_t request = IP_NULL;
+       ipc_port_t nsrequest = IP_NULL;
+       mach_port_mscount_t mscount = 0;
+
+       bits = entry->ie_bits;
+
+       assert(is_active(space));
+
+       if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
+               is_write_unlock(space);
+
+               /* No exception if we used to have receive and held entry since */
+               if ((bits & MACH_PORT_TYPE_EX_RECEIVE) == 0) {
+                       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+               }
+               return KERN_INVALID_RIGHT;
+       }
+
+       if (srdelta && (bits & MACH_PORT_TYPE_SEND) == 0) {
+               is_write_unlock(space);
+               mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+               return KERN_INVALID_RIGHT;
+       }
+
+       if (srdelta > 0) {
+               goto invalid_value;
+       }
+
+       port = ip_object_to_port(entry->ie_object);
+       assert(port != IP_NULL);
+
+       ip_lock(port);
+       require_ip_active(port);
+       assert(port->ip_receiver_name == name);
+       assert(port->ip_receiver == space);
+
+       /* Mach Port Guard Checking */
+       if (port->ip_guarded && (guard != port->ip_context)) {
+               uint64_t portguard = port->ip_context;
+               ip_unlock(port);
+               is_write_unlock(space);
+               mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_DESTROY);
+               return KERN_INVALID_ARGUMENT;
+       }
+
+       /*
+        * First reduce the send rights as requested and
+        * adjust the entry->ie_bits accordingly. The
+        * ipc_entry_modified() call is made once the receive
+        * right is destroyed too.
+        */
+
+       if (srdelta) {
+               assert(port->ip_srights > 0);
+
+               urefs = IE_BITS_UREFS(bits);
+
+               /*
+                * Since we made sure that srdelta is negative,
+                * the check for urefs overflow is not required.
+                */
+               if (MACH_PORT_UREFS_UNDERFLOW(urefs, srdelta)) {
+                       ip_unlock(port);
+                       goto invalid_value;
+               }
+
+               if (urefs == MACH_PORT_UREFS_MAX) {
+                       /*
+                        * urefs are pegged due to an overflow
+                        * only a delta removing all refs at once can change it
+                        */
+                       if (srdelta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
+                               srdelta = 0;
+                       }
+               }
+
+               if ((urefs + srdelta) == 0) {
+                       if (--port->ip_srights == 0) {
+                               nsrequest = port->ip_nsrequest;
+                               if (nsrequest != IP_NULL) {
+                                       port->ip_nsrequest = IP_NULL;
+                                       mscount = port->ip_mscount;
+                               }
+                       }
+                       assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_RECEIVE);
+                       entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK |
+                           MACH_PORT_TYPE_SEND);
+               } else {
+                       entry->ie_bits = bits + srdelta;
+               }
+       }
+
+       /*
+        * Now destroy the receive right. Update space and
+        * entry accordingly.
+        */
+
+       bits = entry->ie_bits;
+       if (bits & MACH_PORT_TYPE_SEND) {
+               assert(IE_BITS_UREFS(bits) > 0);
+               assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX);
+
+               if (port->ip_pdrequest != NULL) {
+                       /*
+                        * Since another task has requested a
+                        * destroy notification for this port, it
+                        * isn't actually being destroyed - the receive
+                        * right is just being moved to another task.
+                        * Since we still have one or more send rights,
+                        * we need to record the loss of the receive
+                        * right and enter the remaining send right
+                        * into the hash table.
+                        */
+                       ipc_entry_modified(space, name, entry);
+                       entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE;
+                       entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE;
+                       ipc_hash_insert(space, ip_to_object(port),
+                           name, entry);
+                       ip_reference(port);
+               } else {
+                       /*
+                        *      The remaining send right turns into a
+                        *      dead name.  Notice we don't decrement
+                        *      ip_srights, generate a no-senders notif,
+                        *      or use ipc_right_dncancel, because the
+                        *      port is destroyed "first".
+                        */
+                       bits &= ~IE_BITS_TYPE_MASK;
+                       bits |= (MACH_PORT_TYPE_DEAD_NAME | MACH_PORT_TYPE_EX_RECEIVE);
+                       if (entry->ie_request) {
+                               entry->ie_request = IE_REQ_NONE;
+                               if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+                                       bits++; /* increment urefs */
+                               }
+                       }
+                       entry->ie_bits = bits;
+                       entry->ie_object = IO_NULL;
+                       ipc_entry_modified(space, name, entry);
+               }
+       } else {
+               assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
+               assert(IE_BITS_UREFS(bits) == 0);
+               request = ipc_right_request_cancel_macro(space, port,
+                   name, entry);
+               entry->ie_object = IO_NULL;
+               ipc_entry_dealloc(space, name, entry);
+       }
+
+       /* Unlock space */
+       is_write_unlock(space);
+
+       if (nsrequest != IP_NULL) {
+               ipc_notify_no_senders(nsrequest, mscount);
+       }
+
+       ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */
+
+       if (request != IP_NULL) {
+               ipc_notify_port_deleted(request, name);
+       }
+
+       return KERN_SUCCESS;
+
+invalid_value:
        is_write_unlock(space);
-       return KERN_UREFS_OVERFLOW;
+       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_VALUE);
+       return KERN_INVALID_VALUE;
 }
 
+
 /*
  *     Routine:        ipc_right_info
  *     Purpose:
  *             Retrieves information about the right.
  *     Conditions:
- *             The space is write-locked, and is unlocked upon return
- *             if the call is unsuccessful.  The space must be active.
+ *             The space is active and write-locked.
+ *             The space is unlocked upon return.
  *     Returns:
- *             KERN_SUCCESS            Retrieved info; space still locked.
+ *             KERN_SUCCESS            Retrieved info
  */
 
 kern_return_t
 ipc_right_info(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       ipc_entry_t             entry,
-       mach_port_type_t        *typep,
-       mach_port_urefs_t       *urefsp)
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       ipc_entry_t             entry,
+       mach_port_type_t        *typep,
+       mach_port_urefs_t       *urefsp)
 {
+       ipc_port_t port;
        ipc_entry_bits_t bits;
-       mach_port_type_t type;
+       mach_port_type_t type = 0;
        ipc_port_request_index_t request;
 
        bits = entry->ie_bits;
+       request = entry->ie_request;
+       port = ip_object_to_port(entry->ie_object);
 
-       if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
-               ipc_port_t port = (ipc_port_t) entry->ie_object;
+       if (bits & MACH_PORT_TYPE_RECEIVE) {
+               assert(IP_VALID(port));
 
-               if (ipc_right_check(space, port, name, entry)) {
+               if (request != IE_REQ_NONE) {
+                       ip_lock(port);
+                       require_ip_active(port);
+                       type |= ipc_port_request_type(port, name, request);
+                       ip_unlock(port);
+               }
+               is_write_unlock(space);
+       } else if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
+               /*
+                * validate port is still alive - if so, get request
+                * types while we still have it locked.  Otherwise,
+                * recapture the (now dead) bits.
+                */
+               if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
+                       if (request != IE_REQ_NONE) {
+                               type |= ipc_port_request_type(port, name, request);
+                       }
+                       ip_unlock(port);
+                       is_write_unlock(space);
+               } else {
                        bits = entry->ie_bits;
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
-               } else
-                       ip_unlock(port);
+                       is_write_unlock(space);
+                       ip_release(port);
+               }
+       } else {
+               is_write_unlock(space);
        }
 
-       type = IE_BITS_TYPE(bits);
-       request = entry->ie_request;
-
-       if (request != 0)
-               type |= MACH_PORT_TYPE_DNREQUEST;
+       type |= IE_BITS_TYPE(bits);
 
        *typep = type;
        *urefsp = IE_BITS_UREFS(bits);
@@ -1301,75 +1796,132 @@ ipc_right_info(
 }
 
 /*
- *     Routine:        ipc_right_copyin_check
+ *     Routine:        ipc_right_copyin_check_reply
  *     Purpose:
- *             Check if a subsequent ipc_right_copyin would succeed.
+ *             Check if a subsequent ipc_right_copyin would succeed. Used only
+ *             by ipc_kmsg_copyin_header to check if reply_port can be copied in.
+ *             If the reply port is an immovable send right, it errors out.
  *     Conditions:
  *             The space is locked (read or write) and active.
  */
 
 boolean_t
-ipc_right_copyin_check(
-       __assert_only ipc_space_t       space,
-       __unused mach_port_name_t       name,
-       ipc_entry_t                     entry,
-       mach_msg_type_name_t            msgt_name)
+ipc_right_copyin_check_reply(
+       __assert_only ipc_space_t       space,
+       mach_port_name_t                reply_name,
+       ipc_entry_t                     reply_entry,
+       mach_msg_type_name_t            reply_type)
 {
        ipc_entry_bits_t bits;
+       ipc_port_t reply_port;
 
-       bitsentry->ie_bits;
-       assert(space->is_active);
+       bits = reply_entry->ie_bits;
+       assert(is_active(space));
 
-       switch (msgt_name) {
-           case MACH_MSG_TYPE_MAKE_SEND:
-           case MACH_MSG_TYPE_MAKE_SEND_ONCE:
-           case MACH_MSG_TYPE_MOVE_RECEIVE:
-               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
+       switch (reply_type) {
+       case MACH_MSG_TYPE_MAKE_SEND:
+               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
                        return FALSE;
+               }
+               break;
 
+       case MACH_MSG_TYPE_MAKE_SEND_ONCE:
+               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
+                       return FALSE;
+               }
                break;
 
-           case MACH_MSG_TYPE_COPY_SEND:
-           case MACH_MSG_TYPE_MOVE_SEND:
-           case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
-               ipc_port_t port;
-               boolean_t active;
+       case MACH_MSG_TYPE_MOVE_RECEIVE:
+               /* ipc_kmsg_copyin_header already filters it out */
+               return FALSE;
 
-               if (bits & MACH_PORT_TYPE_DEAD_NAME)
+       case MACH_MSG_TYPE_COPY_SEND:
+       case MACH_MSG_TYPE_MOVE_SEND:
+       case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
+               if (bits & MACH_PORT_TYPE_DEAD_NAME) {
                        break;
+               }
 
-               if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
+               if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) {
                        return FALSE;
+               }
 
-               port = (ipc_port_t) entry->ie_object;
-               assert(port != IP_NULL);
+               reply_port = ip_object_to_port(reply_entry->ie_object);
+               assert(reply_port != IP_NULL);
 
-               ip_lock(port);
-               active = ip_active(port);
-               ip_unlock(port);
-
-               if (!active) {
+               /*
+                * active status peek to avoid checks that will be skipped
+                * on copyin for dead ports.  Lock not held, so will not be
+                * atomic (but once dead, there's no going back).
+                */
+               if (!ip_active(reply_port)) {
                        break;
                }
 
-               if (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
-                       if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0)
+               /*
+                * Can't copyin a send right that is marked immovable. This bit
+                * is set only during port creation and never unset. So it can
+                * be read without a lock.
+                */
+               if (reply_port->ip_immovable_send) {
+                       mach_port_guard_exception(reply_name, 0, 0, kGUARD_EXC_IMMOVABLE);
+                       return FALSE;
+               }
+
+               if (reply_type == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
+                       if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
                                return FALSE;
+                       }
                } else {
-                       if ((bits & MACH_PORT_TYPE_SEND) == 0)
+                       if ((bits & MACH_PORT_TYPE_SEND) == 0) {
                                return FALSE;
+                       }
                }
 
                break;
-           }
+       }
 
-           default:
+       default:
                panic("ipc_right_copyin_check: strange rights");
        }
 
        return TRUE;
 }
 
+/*
+ *     Routine:        ipc_right_copyin_check_guard_locked
+ *     Purpose:
+ *             Check if the port is guarded and the guard
+ *             value matches the one passed in the arguments.
+ *             If MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND is set,
+ *             check if the port is unguarded.
+ *     Conditions:
+ *             The port is locked.
+ *     Returns:
+ *             KERN_SUCCESS            Port is either unguarded
+ *                                     or guarded with expected value
+ *             KERN_INVALID_ARGUMENT   Port is either unguarded already or guard mismatch.
+ *                                     This also raises a EXC_GUARD exception.
+ */
+static kern_return_t
+ipc_right_copyin_check_guard_locked(
+       mach_port_name_t name,
+       ipc_port_t port,
+       mach_port_context_t context,
+       mach_msg_guard_flags_t *guard_flags)
+{
+       mach_msg_guard_flags_t flags = *guard_flags;
+       if ((flags & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) && !port->ip_guarded && !context) {
+               return KERN_SUCCESS;
+       } else if (port->ip_guarded && (port->ip_context == context)) {
+               return KERN_SUCCESS;
+       }
+
+       /* Incorrect guard; Raise exception */
+       mach_port_guard_exception(name, context, port->ip_context, kGUARD_EXC_INCORRECT_GUARD);
+       return KERN_INVALID_ARGUMENT;
+}
+
 /*
  *     Routine:        ipc_right_copyin
  *     Purpose:
@@ -1391,135 +1943,201 @@ ipc_right_copyin_check(
  *     Returns:
  *             KERN_SUCCESS            Acquired an object, possibly IO_DEAD.
  *             KERN_INVALID_RIGHT      Name doesn't denote correct right.
+ *             KERN_INVALID_CAPABILITY Trying to move an kobject port or an immovable right
+ *             KERN_INVALID_ARGUMENT   Port is unguarded or guard mismatch
  */
 
 kern_return_t
 ipc_right_copyin(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       ipc_entry_t             entry,
-       mach_msg_type_name_t    msgt_name,
-       boolean_t               deadok,
-       ipc_object_t            *objectp,
-       ipc_port_t              *sorightp)
+       ipc_space_t                space,
+       mach_port_name_t           name,
+       ipc_entry_t                entry,
+       mach_msg_type_name_t       msgt_name,
+       ipc_right_copyin_flags_t   flags,
+       ipc_object_t               *objectp,
+       ipc_port_t                 *sorightp,
+       ipc_port_t                 *releasep,
+       int                        *assertcntp,
+       mach_port_context_t        context,
+       mach_msg_guard_flags_t     *guard_flags)
 {
        ipc_entry_bits_t bits;
-       
+       ipc_port_t port;
+       kern_return_t kr;
+       boolean_t deadok = flags & IPC_RIGHT_COPYIN_FLAGS_DEADOK? TRUE : FALSE;
+       boolean_t allow_imm_send = flags & IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND? TRUE : FALSE;
+
+       *releasep = IP_NULL;
+       *assertcntp = 0;
+
        bits = entry->ie_bits;
 
-       assert(space->is_active);
+       assert(is_active(space));
 
        switch (msgt_name) {
-           case MACH_MSG_TYPE_MAKE_SEND: {
-               ipc_port_t port;
-
-               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
+       case MACH_MSG_TYPE_MAKE_SEND: {
+               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
                        goto invalid_right;
+               }
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
                ip_lock(port);
-               assert(ip_active(port));
                assert(port->ip_receiver_name == name);
                assert(port->ip_receiver == space);
 
-               port->ip_mscount++;
-               port->ip_srights++;
-               ip_reference(port);
+               ipc_port_make_send_locked(port);
                ip_unlock(port);
 
-               *objectp = (ipc_object_t) port;
+               *objectp = ip_to_object(port);
                *sorightp = IP_NULL;
                break;
-           }
-
-           case MACH_MSG_TYPE_MAKE_SEND_ONCE: {
-               ipc_port_t port;
+       }
 
-               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
+       case MACH_MSG_TYPE_MAKE_SEND_ONCE: {
+               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
                        goto invalid_right;
+               }
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
                ip_lock(port);
-               assert(ip_active(port));
+               require_ip_active(port);
                assert(port->ip_receiver_name == name);
                assert(port->ip_receiver == space);
 
-               port->ip_sorights++;
-               ip_reference(port);
+               ipc_port_make_sonce_locked(port);
                ip_unlock(port);
 
-               *objectp = (ipc_object_t) port;
+               *objectp = ip_to_object(port);
                *sorightp = IP_NULL;
                break;
-           }
+       }
 
-           case MACH_MSG_TYPE_MOVE_RECEIVE: {
-               ipc_port_t port;
-               ipc_port_t dnrequest = IP_NULL;
+       case MACH_MSG_TYPE_MOVE_RECEIVE: {
+               ipc_port_t request = IP_NULL;
 
-               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
+               if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
                        goto invalid_right;
+               }
 
-               port = (ipc_port_t) entry->ie_object;
+               /*
+                * Disallow moving receive-right kobjects, e.g. mk_timer ports
+                * The ipc_port structure uses the kdata union of kobject and
+                * imp_task exclusively. Thus, general use of a kobject port as
+                * a receive right can cause type confusion in the importance
+                * code.
+                */
+               if (io_kotype(entry->ie_object) != IKOT_NONE) {
+                       /*
+                        * Distinguish an invalid right, e.g., trying to move
+                        * a send right as a receive right, from this
+                        * situation which is, "This is a valid receive right,
+                        * but it's also a kobject and you can't move it."
+                        */
+                       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
+                       return KERN_INVALID_CAPABILITY;
+               }
+
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
                ip_lock(port);
-               assert(ip_active(port));
+               require_ip_active(port);
                assert(port->ip_receiver_name == name);
                assert(port->ip_receiver == space);
 
+               if (port->ip_immovable_receive) {
+                       assert(port->ip_receiver != ipc_space_kernel);
+                       ip_unlock(port);
+                       assert(current_task() != kernel_task);
+                       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
+                       return KERN_INVALID_CAPABILITY;
+               }
+
+               if (guard_flags != NULL) {
+                       kr = ipc_right_copyin_check_guard_locked(name, port, context, guard_flags);
+                       if (kr != KERN_SUCCESS) {
+                               ip_unlock(port);
+                               return kr;
+                       }
+               }
+
                if (bits & MACH_PORT_TYPE_SEND) {
                        assert(IE_BITS_TYPE(bits) ==
-                                       MACH_PORT_TYPE_SEND_RECEIVE);
+                           MACH_PORT_TYPE_SEND_RECEIVE);
                        assert(IE_BITS_UREFS(bits) > 0);
                        assert(port->ip_srights > 0);
 
-                       ipc_hash_insert(space, (ipc_object_t) port,
-                                       name, entry);
+                       ipc_hash_insert(space, ip_to_object(port),
+                           name, entry);
                        ip_reference(port);
                } else {
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
                        assert(IE_BITS_UREFS(bits) == 0);
 
-                       dnrequest = ipc_right_dncancel_macro(space, port,
-                                                            name, entry);
+                       request = ipc_right_request_cancel_macro(space, port,
+                           name, entry);
                        entry->ie_object = IO_NULL;
                }
-               entry->ie_bits = bits &~ MACH_PORT_TYPE_RECEIVE;
+               entry->ie_bits = bits & ~MACH_PORT_TYPE_RECEIVE;
+               entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE;
+               ipc_entry_modified(space, name, entry);
+
+               /* ipc_port_clear_receiver unguards the port and clears the ip_immovable_receive bit */
+               (void)ipc_port_clear_receiver(port, FALSE); /* don't destroy the port/mqueue */
+               if (guard_flags != NULL) {
+                       /* this flag will be cleared during copyout */
+                       *guard_flags = *guard_flags | MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND;
+               }
+
+#if IMPORTANCE_INHERITANCE
+               /*
+                * Account for boosts the current task is going to lose when
+                * copying this right in.  Tempowner ports have either not
+                * been accounting to any task (and therefore are already in
+                * "limbo" state w.r.t. assertions) or to some other specific
+                * task. As we have no way to drop the latter task's assertions
+                * here, We'll deduct those when we enqueue it on its
+                * destination port (see ipc_port_check_circularity()).
+                */
+               if (port->ip_tempowner == 0) {
+                       assert(IIT_NULL == port->ip_imp_task);
 
-               ipc_port_clear_receiver(port);
+                       /* ports in limbo have to be tempowner */
+                       port->ip_tempowner = 1;
+                       *assertcntp = port->ip_impcount;
+               }
+#endif /* IMPORTANCE_INHERITANCE */
 
-               port->ip_receiver_name = MACH_PORT_NULL;
-               port->ip_destination = IP_NULL;
                ip_unlock(port);
 
-               *objectp = (ipc_object_t) port;
-               *sorightp = dnrequest;
+               *objectp = ip_to_object(port);
+               *sorightp = request;
                break;
-           }
-
-           case MACH_MSG_TYPE_COPY_SEND: {
-               ipc_port_t port;
+       }
 
-               if (bits & MACH_PORT_TYPE_DEAD_NAME)
+       case MACH_MSG_TYPE_COPY_SEND: {
+               if (bits & MACH_PORT_TYPE_DEAD_NAME) {
                        goto copy_dead;
+               }
 
                /* allow for dead send-once rights */
 
-               if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
+               if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) {
                        goto invalid_right;
+               }
 
                assert(IE_BITS_UREFS(bits) > 0);
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
-               if (ipc_right_check(space, port, name, entry)) {
+               if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
                        bits = entry->ie_bits;
+                       *releasep = port;
                        goto copy_dead;
                }
                /* port is locked and active */
@@ -1532,36 +2150,41 @@ ipc_right_copyin(
                        goto invalid_right;
                }
 
-               assert(port->ip_srights > 0);
+               if (!allow_imm_send && port->ip_immovable_send) {
+                       ip_unlock(port);
+                       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
+                       return KERN_INVALID_CAPABILITY;
+               }
 
-               port->ip_srights++;
-               ip_reference(port);
+               ipc_port_copy_send_locked(port);
                ip_unlock(port);
 
-               *objectp = (ipc_object_t) port;
+               *objectp = ip_to_object(port);
                *sorightp = IP_NULL;
                break;
-           }
+       }
 
-           case MACH_MSG_TYPE_MOVE_SEND: {
-               ipc_port_t port;
-               ipc_port_t dnrequest = IP_NULL;
+       case MACH_MSG_TYPE_MOVE_SEND: {
+               ipc_port_t request = IP_NULL;
 
-               if (bits & MACH_PORT_TYPE_DEAD_NAME)
+               if (bits & MACH_PORT_TYPE_DEAD_NAME) {
                        goto move_dead;
+               }
 
                /* allow for dead send-once rights */
 
-               if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
+               if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) {
                        goto invalid_right;
+               }
 
                assert(IE_BITS_UREFS(bits) > 0);
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
-               if (ipc_right_check(space, port, name, entry)) {
+               if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
                        bits = entry->ie_bits;
+                       *releasep = port;
                        goto move_dead;
                }
                /* port is locked and active */
@@ -1574,63 +2197,79 @@ ipc_right_copyin(
                        goto invalid_right;
                }
 
-               assert(port->ip_srights > 0);
+               if (!allow_imm_send && port->ip_immovable_send) {
+                       ip_unlock(port);
+                       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
+                       return KERN_INVALID_CAPABILITY;
+               }
 
                if (IE_BITS_UREFS(bits) == 1) {
+                       assert(port->ip_srights > 0);
                        if (bits & MACH_PORT_TYPE_RECEIVE) {
                                assert(port->ip_receiver_name == name);
                                assert(port->ip_receiver == space);
                                assert(IE_BITS_TYPE(bits) ==
-                                               MACH_PORT_TYPE_SEND_RECEIVE);
+                                   MACH_PORT_TYPE_SEND_RECEIVE);
 
                                ip_reference(port);
                        } else {
                                assert(IE_BITS_TYPE(bits) ==
-                                               MACH_PORT_TYPE_SEND);
+                                   MACH_PORT_TYPE_SEND);
 
-                               dnrequest = ipc_right_dncancel_macro(space, port,
-                                                                    name, entry);
-                               ipc_hash_delete(space, (ipc_object_t) port,
-                                               name, entry);
+                               request = ipc_right_request_cancel_macro(space, port,
+                                   name, entry);
+                               ipc_hash_delete(space, ip_to_object(port),
+                                   name, entry);
                                entry->ie_object = IO_NULL;
+                               /* transfer entry's reference to caller */
                        }
-                       entry->ie_bits = bits &~
-                               (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND);
+                       entry->ie_bits = bits & ~
+                           (IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND);
                } else {
-                       port->ip_srights++;
-                       ip_reference(port);
-                       entry->ie_bits = bits-1; /* decrement urefs */
+                       ipc_port_copy_send_locked(port);
+                       /* if urefs are pegged due to overflow, leave them pegged */
+                       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+                               entry->ie_bits = bits - 1; /* decrement urefs */
+                       }
                }
 
+               ipc_entry_modified(space, name, entry);
                ip_unlock(port);
 
-               *objectp = (ipc_object_t) port;
-               *sorightp = dnrequest;
+               *objectp = ip_to_object(port);
+               *sorightp = request;
                break;
-           }
+       }
 
-           case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
-               ipc_port_t port;
-               ipc_port_t dnrequest;
+       case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
+               ipc_port_t request;
 
-               if (bits & MACH_PORT_TYPE_DEAD_NAME)
+               if (bits & MACH_PORT_TYPE_DEAD_NAME) {
                        goto move_dead;
+               }
 
                /* allow for dead send rights */
 
-               if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
+               if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) {
                        goto invalid_right;
+               }
 
                assert(IE_BITS_UREFS(bits) > 0);
 
-               port = (ipc_port_t) entry->ie_object;
+               port = ip_object_to_port(entry->ie_object);
                assert(port != IP_NULL);
 
-               if (ipc_right_check(space, port, name, entry)) {
+               if (ipc_right_check(space, port, name, entry, flags)) {
                        bits = entry->ie_bits;
+                       *releasep = port;
                        goto move_dead;
                }
-               /* port is locked and active */
+               /*
+                * port is locked, but may not be active:
+                * Allow copyin of inactive ports with no dead name request and treat it
+                * as if the copyin of the port was successful and port became inactive
+                * later.
+                */
 
                if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
                        assert(bits & MACH_PORT_TYPE_SEND);
@@ -1640,145 +2279,74 @@ ipc_right_copyin(
                        goto invalid_right;
                }
 
+               if (!allow_imm_send && port->ip_immovable_send) {
+                       ip_unlock(port);
+                       mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
+                       return KERN_INVALID_CAPABILITY;
+               }
+
                assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
                assert(IE_BITS_UREFS(bits) == 1);
                assert(port->ip_sorights > 0);
 
-               dnrequest = ipc_right_dncancel_macro(space, port, name, entry);
+               request = ipc_right_request_cancel_macro(space, port, name, entry);
                ip_unlock(port);
 
                entry->ie_object = IO_NULL;
-               entry->ie_bits = bits &~
-                       (IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND_ONCE);
-
-               *objectp = (ipc_object_t) port;
-               *sorightp = dnrequest;
+               entry->ie_bits = bits & ~
+                   (IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND_ONCE);
+               ipc_entry_modified(space, name, entry);
+               *objectp = ip_to_object(port);
+               *sorightp = request;
                break;
-           }
+       }
 
-           default:
-           invalid_right:
+       default:
+invalid_right:
                return KERN_INVALID_RIGHT;
        }
 
        return KERN_SUCCESS;
 
-    copy_dead:
+copy_dead:
        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
        assert(IE_BITS_UREFS(bits) > 0);
-       assert(entry->ie_request == 0);
+       assert(entry->ie_request == IE_REQ_NONE);
        assert(entry->ie_object == 0);
 
-       if (!deadok)
+       if (!deadok) {
                goto invalid_right;
+       }
 
        *objectp = IO_DEAD;
        *sorightp = IP_NULL;
        return KERN_SUCCESS;
 
-    move_dead:
+move_dead:
        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
        assert(IE_BITS_UREFS(bits) > 0);
-       assert(entry->ie_request == 0);
+       assert(entry->ie_request == IE_REQ_NONE);
        assert(entry->ie_object == 0);
 
-       if (!deadok)
+       if (!deadok) {
                goto invalid_right;
+       }
 
        if (IE_BITS_UREFS(bits) == 1) {
                bits &= ~MACH_PORT_TYPE_DEAD_NAME;
        }
-       entry->ie_bits = bits-1; /* decrement urefs */
-
+       /* if urefs are pegged due to overflow, leave them pegged */
+       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+               entry->ie_bits = bits - 1; /* decrement urefs */
+       }
+       ipc_entry_modified(space, name, entry);
        *objectp = IO_DEAD;
        *sorightp = IP_NULL;
        return KERN_SUCCESS;
-
 }
 
 /*
- *     Routine:        ipc_right_copyin_undo
- *     Purpose:
- *             Undoes the effects of an ipc_right_copyin
- *             of a send/send-once right that is dead.
- *             (Object is either IO_DEAD or a dead port.)
- *     Conditions:
- *             The space is write-locked and active.
- */
-
-void
-ipc_right_copyin_undo(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       ipc_entry_t             entry,
-       mach_msg_type_name_t    msgt_name,
-       ipc_object_t            object,
-       ipc_port_t              soright)
-{
-       ipc_entry_bits_t bits;
-
-       bits = entry->ie_bits;
-
-       assert(space->is_active);
-
-       assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
-              (msgt_name == MACH_MSG_TYPE_COPY_SEND) ||
-              (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE));
-
-       if (soright != IP_NULL) {
-               assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
-                      (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE));
-               assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
-               assert(object != IO_DEAD);
-
-               entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) |
-                                 MACH_PORT_TYPE_DEAD_NAME | 2);
-
-       } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE) {
-               assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
-                      (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE));
-
-               entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) |
-                                 MACH_PORT_TYPE_DEAD_NAME | 1);
-       } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME) {
-               assert(object == IO_DEAD);
-               assert(IE_BITS_UREFS(bits) > 0);
-
-               if (msgt_name != MACH_MSG_TYPE_COPY_SEND) {
-                       assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
-                       entry->ie_bits = bits+1; /* increment urefs */
-               }
-       } else {
-               assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
-                      (msgt_name == MACH_MSG_TYPE_COPY_SEND));
-               assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
-               assert(object != IO_DEAD);
-               assert(entry->ie_object == object);
-               assert(IE_BITS_UREFS(bits) > 0);
-
-               if (msgt_name != MACH_MSG_TYPE_COPY_SEND) {
-                       assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX-1);
-                       entry->ie_bits = bits+1; /* increment urefs */
-               }
-
-               /*
-                *      May as well convert the entry to a dead name.
-                *      (Or if it is a compat entry, destroy it.)
-                */
-
-               (void) ipc_right_check(space, (ipc_port_t) object,
-                                      name, entry);
-               /* object is dead so it is not locked */
-       }
-
-       /* release the reference acquired by copyin */
-
-       if (object != IO_DEAD)
-               ipc_object_release(object);
-}
-
-/*
- *     Routine:        ipc_right_copyin_two
+ *     Routine:        ipc_right_copyin_two_move_sends
  *     Purpose:
  *             Like ipc_right_copyin with MACH_MSG_TYPE_MOVE_SEND
  *             and deadok == FALSE, except that this moves two
@@ -1790,80 +2358,246 @@ ipc_right_copyin_undo(
  *             KERN_SUCCESS            Acquired an object.
  *             KERN_INVALID_RIGHT      Name doesn't denote correct right.
  */
-
+static
 kern_return_t
-ipc_right_copyin_two(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       ipc_entry_t             entry,
-       ipc_object_t            *objectp,
-       ipc_port_t              *sorightp)
+ipc_right_copyin_two_move_sends(
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       ipc_entry_t             entry,
+       ipc_object_t            *objectp,
+       ipc_port_t              *sorightp,
+       ipc_port_t              *releasep)
 {
        ipc_entry_bits_t bits;
        mach_port_urefs_t urefs;
        ipc_port_t port;
-       ipc_port_t dnrequest = IP_NULL;
+       ipc_port_t request = IP_NULL;
 
-       assert(space->is_active);
+       *releasep = IP_NULL;
+
+       assert(is_active(space));
 
        bits = entry->ie_bits;
 
-       if ((bits & MACH_PORT_TYPE_SEND) == 0)
+       if ((bits & MACH_PORT_TYPE_SEND) == 0) {
                goto invalid_right;
+       }
 
        urefs = IE_BITS_UREFS(bits);
-       if (urefs < 2)
+       if (urefs < 2) {
                goto invalid_right;
+       }
 
-       port = (ipc_port_t) entry->ie_object;
+       port = ip_object_to_port(entry->ie_object);
        assert(port != IP_NULL);
 
-       if (ipc_right_check(space, port, name, entry)) {
+       if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
+               *releasep = port;
                goto invalid_right;
        }
        /* port is locked and active */
 
-       assert(port->ip_srights > 0);
+       if (urefs > 2) {
+               /*
+                * We are moving 2 urefs as naked send rights, which is decomposed as:
+                * - two copy sends (which doesn't affect the make send count)
+                * - decrementing the local urefs twice.
+                */
+               ipc_port_copy_send_locked(port);
+               ipc_port_copy_send_locked(port);
+               /* if urefs are pegged due to overflow, leave them pegged */
+               if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+                       entry->ie_bits = bits - 2; /* decrement urefs */
+               }
+       } else {
+               /*
+                * We have exactly 2 send rights for this port in this space,
+                * which means that we will liberate the naked send right held
+                * by this entry.
+                *
+                * However refcounting rules around entries are that naked send rights
+                * on behalf of spaces do not have an associated port reference,
+                * so we need to donate one ...
+                */
+               ipc_port_copy_send_locked(port);
 
-       if (urefs == 2) {
                if (bits & MACH_PORT_TYPE_RECEIVE) {
                        assert(port->ip_receiver_name == name);
                        assert(port->ip_receiver == space);
                        assert(IE_BITS_TYPE(bits) ==
-                                       MACH_PORT_TYPE_SEND_RECEIVE);
+                           MACH_PORT_TYPE_SEND_RECEIVE);
 
-                       port->ip_srights++;
-                       ip_reference(port);
+                       /* ... that we inject manually when the entry stays alive */
                        ip_reference(port);
                } else {
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
 
-                       dnrequest = ipc_right_dncancel_macro(space, port,
-                                                            name, entry);
+                       /* ... that we steal from the entry when it dies */
+                       request = ipc_right_request_cancel_macro(space, port,
+                           name, entry);
 
-                       port->ip_srights++;
-                       ip_reference(port);
-                       ipc_hash_delete(space, (ipc_object_t) port,
-                                       name, entry);
+                       ipc_hash_delete(space, ip_to_object(port),
+                           name, entry);
                        entry->ie_object = IO_NULL;
                }
-               entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND);
-       } else {
-               port->ip_srights += 2;
-               ip_reference(port);
-               ip_reference(port);
-               entry->ie_bits = bits-2; /* decrement urefs */
+
+               entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND);
        }
+       ipc_entry_modified(space, name, entry);
+
        ip_unlock(port);
 
-       *objectp = (ipc_object_t) port;
-       *sorightp = dnrequest;
+       *objectp = ip_to_object(port);
+       *sorightp = request;
        return KERN_SUCCESS;
 
-    invalid_right:
+invalid_right:
        return KERN_INVALID_RIGHT;
 }
 
+
+/*
+ *     Routine:        ipc_right_copyin_two
+ *     Purpose:
+ *             Like ipc_right_copyin with two dispositions,
+ *             each of which results in a send or send-once right,
+ *             and deadok = FALSE.
+ *     Conditions:
+ *             The space is write-locked and active.
+ *             The object is returned with two refs/rights.
+ *             Msgt_one refers to the dest_type
+ *     Returns:
+ *             KERN_SUCCESS            Acquired an object.
+ *             KERN_INVALID_RIGHT      Name doesn't denote correct right(s).
+ *             KERN_INVALID_CAPABILITY Name doesn't denote correct right for msgt_two.
+ */
+kern_return_t
+ipc_right_copyin_two(
+       ipc_space_t               space,
+       mach_port_name_t          name,
+       ipc_entry_t               entry,
+       mach_msg_type_name_t      msgt_one,
+       mach_msg_type_name_t      msgt_two,
+       ipc_object_t              *objectp,
+       ipc_port_t                *sorightp,
+       ipc_port_t                *releasep)
+{
+       kern_return_t kr;
+       int assertcnt = 0;
+
+       assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_one));
+       assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_two));
+
+       /*
+        *      This is a little tedious to make atomic, because
+        *      there are 25 combinations of valid dispositions.
+        *      However, most are easy.
+        */
+
+       /*
+        *      If either is move-sonce, then there must be an error.
+        */
+       if (msgt_one == MACH_MSG_TYPE_MOVE_SEND_ONCE ||
+           msgt_two == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
+               return KERN_INVALID_RIGHT;
+       }
+
+       if ((msgt_one == MACH_MSG_TYPE_MAKE_SEND) ||
+           (msgt_one == MACH_MSG_TYPE_MAKE_SEND_ONCE) ||
+           (msgt_two == MACH_MSG_TYPE_MAKE_SEND) ||
+           (msgt_two == MACH_MSG_TYPE_MAKE_SEND_ONCE)) {
+               /*
+                *      One of the dispositions needs a receive right.
+                *
+                *      If the copyin below succeeds, we know the receive
+                *      right is there (because the pre-validation of
+                *      the second disposition already succeeded in our
+                *      caller).
+                *
+                *      Hence the port is not in danger of dying.
+                */
+               ipc_object_t object_two;
+
+               kr = ipc_right_copyin(space, name, entry,
+                   msgt_one, IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND,
+                   objectp, sorightp, releasep,
+                   &assertcnt, 0, NULL);
+               assert(assertcnt == 0);
+               if (kr != KERN_SUCCESS) {
+                       return kr;
+               }
+
+               assert(IO_VALID(*objectp));
+               assert(*sorightp == IP_NULL);
+               assert(*releasep == IP_NULL);
+
+               /*
+                *      Now copyin the second (previously validated)
+                *      disposition.  The result can't be a dead port,
+                *      as no valid disposition can make us lose our
+                *      receive right.
+                */
+               kr = ipc_right_copyin(space, name, entry,
+                   msgt_two, IPC_RIGHT_COPYIN_FLAGS_NONE,
+                   &object_two, sorightp, releasep,
+                   &assertcnt, 0, NULL);
+               assert(assertcnt == 0);
+               assert(kr == KERN_SUCCESS);
+               assert(*sorightp == IP_NULL);
+               assert(*releasep == IP_NULL);
+               assert(object_two == *objectp);
+               assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
+       } else if ((msgt_one == MACH_MSG_TYPE_MOVE_SEND) &&
+           (msgt_two == MACH_MSG_TYPE_MOVE_SEND)) {
+               /*
+                *      This is an easy case.  Just use our
+                *      handy-dandy special-purpose copyin call
+                *      to get two send rights for the price of one.
+                */
+               kr = ipc_right_copyin_two_move_sends(space, name, entry,
+                   objectp, sorightp,
+                   releasep);
+               if (kr != KERN_SUCCESS) {
+                       return kr;
+               }
+       } else {
+               mach_msg_type_name_t msgt_name;
+
+               /*
+                *      Must be either a single move-send and a
+                *      copy-send, or two copy-send dispositions.
+                *      Use the disposition with the greatest side
+                *      effects for the actual copyin - then just
+                *      duplicate the send right you get back.
+                */
+               if (msgt_one == MACH_MSG_TYPE_MOVE_SEND ||
+                   msgt_two == MACH_MSG_TYPE_MOVE_SEND) {
+                       msgt_name = MACH_MSG_TYPE_MOVE_SEND;
+               } else {
+                       msgt_name = MACH_MSG_TYPE_COPY_SEND;
+               }
+
+               kr = ipc_right_copyin(space, name, entry,
+                   msgt_name, IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND,
+                   objectp, sorightp, releasep,
+                   &assertcnt, 0, NULL);
+               assert(assertcnt == 0);
+               if (kr != KERN_SUCCESS) {
+                       return kr;
+               }
+
+               /*
+                *      Copy the right we got back.  If it is dead now,
+                *      that's OK.  Neither right will be usable to send
+                *      a message anyway.
+                */
+               (void)ipc_port_copy_send(ip_object_to_port(*objectp));
+       }
+
+       return KERN_SUCCESS;
+}
+
+
 /*
  *     Routine:        ipc_right_copyout
  *     Purpose:
@@ -1874,28 +2608,26 @@ ipc_right_copyin_two(
  *             because user-reference overflow isn't a possibility.
  *
  *             If copying out the object would cause the user-reference
- *             count in the entry to overflow, and overflow is TRUE,
- *             then instead the user-reference count is left pegged
- *             to its maximum value and the copyout succeeds anyway.
+ *             count in the entry to overflow, then the user-reference
+ *             count is left pegged to its maximum value and the copyout
+ *             succeeds anyway.
  *     Conditions:
  *             The space is write-locked and active.
  *             The object is locked and active.
  *             The object is unlocked; the space isn't.
  *     Returns:
  *             KERN_SUCCESS            Copied out capability.
- *             KERN_UREFS_OVERFLOW     User-refs would overflow;
- *                     guaranteed not to happen with a fresh entry
- *                     or if overflow=TRUE was specified.
  */
 
 kern_return_t
 ipc_right_copyout(
-       ipc_space_t             space,
-       mach_port_name_t        name,
-       ipc_entry_t             entry,
-       mach_msg_type_name_t    msgt_name,
-       boolean_t               overflow,
-       ipc_object_t            object)
+       ipc_space_t             space,
+       mach_port_name_t        name,
+       ipc_entry_t             entry,
+       mach_msg_type_name_t    msgt_name,
+       mach_port_context_t     *context,
+       mach_msg_guard_flags_t  *guard_flags,
+       ipc_object_t            object)
 {
        ipc_entry_bits_t bits;
        ipc_port_t port;
@@ -1907,21 +2639,28 @@ ipc_right_copyout(
        assert(io_active(object));
        assert(entry->ie_object == object);
 
-       port = (ipc_port_t) object;
+       port = ip_object_to_port(object);
 
        switch (msgt_name) {
-           case MACH_MSG_TYPE_PORT_SEND_ONCE:
-               
+       case MACH_MSG_TYPE_PORT_SEND_ONCE:
+
                assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
+               assert(IE_BITS_UREFS(bits) == 0);
                assert(port->ip_sorights > 0);
 
-               /* transfer send-once right and ref to entry */
-               ip_unlock(port);
+               if (port->ip_specialreply) {
+                       ipc_port_adjust_special_reply_port_locked(port,
+                           current_thread()->ith_knote, IPC_PORT_ADJUST_SR_LINK_WORKLOOP, FALSE);
+                       /* port unlocked on return */
+               } else {
+                       ip_unlock(port);
+               }
 
-               entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1);
+               entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1); /* set urefs to 1 */
+               ipc_entry_modified(space, name, entry);
                break;
 
-           case MACH_MSG_TYPE_PORT_SEND:
+       case MACH_MSG_TYPE_PORT_SEND:
                assert(port->ip_srights > 0);
 
                if (bits & MACH_PORT_TYPE_SEND) {
@@ -1929,32 +2668,31 @@ ipc_right_copyout(
 
                        assert(port->ip_srights > 1);
                        assert(urefs > 0);
-                       assert(urefs < MACH_PORT_UREFS_MAX);
+                       assert(urefs <= MACH_PORT_UREFS_MAX);
 
-                       if (urefs+1 == MACH_PORT_UREFS_MAX) {
-                               if (overflow) {
-                                       /* leave urefs pegged to maximum */
-
-                                       port->ip_srights--;
-                                       ip_release(port);
-                                       ip_unlock(port);
-                                       return KERN_SUCCESS;
-                               }
+                       if (urefs == MACH_PORT_UREFS_MAX) {
+                               /*
+                                * leave urefs pegged to maximum,
+                                * consume send right and ref
+                                */
 
+                               port->ip_srights--;
                                ip_unlock(port);
-                               return KERN_UREFS_OVERFLOW;
+                               ip_release(port);
+                               return KERN_SUCCESS;
                        }
 
+                       /* consume send right and ref */
                        port->ip_srights--;
-                       ip_release(port);
                        ip_unlock(port);
+                       ip_release(port);
                } else if (bits & MACH_PORT_TYPE_RECEIVE) {
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
                        assert(IE_BITS_UREFS(bits) == 0);
 
-                       /* transfer send right to entry */
-                       ip_release(port);
+                       /* transfer send right to entry, consume ref */
                        ip_unlock(port);
+                       ip_release(port);
                } else {
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
                        assert(IE_BITS_UREFS(bits) == 0);
@@ -1964,181 +2702,94 @@ ipc_right_copyout(
 
                        /* entry is locked holding ref, so can use port */
 
-                       ipc_hash_insert(space, (ipc_object_t) port,
-                                       name, entry);
+                       ipc_hash_insert(space, ip_to_object(port), name, entry);
                }
 
-               entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1;
+               entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1; /* increment urefs */
+               ipc_entry_modified(space, name, entry);
                break;
 
-           case MACH_MSG_TYPE_PORT_RECEIVE: {
+       case MACH_MSG_TYPE_PORT_RECEIVE: {
                ipc_port_t dest;
+#if IMPORTANCE_INHERITANCE
+               natural_t assertcnt = port->ip_impcount;
+#endif /* IMPORTANCE_INHERITANCE */
 
                assert(port->ip_mscount == 0);
                assert(port->ip_receiver_name == MACH_PORT_NULL);
+
+               imq_lock(&port->ip_messages);
                dest = port->ip_destination;
 
                port->ip_receiver_name = name;
                port->ip_receiver = space;
 
-               assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
+               struct knote *kn = current_thread()->ith_knote;
+
+               if ((guard_flags != NULL) && ((*guard_flags & MACH_MSG_GUARD_FLAGS_IMMOVABLE_RECEIVE) != 0)) {
+                       assert(port->ip_immovable_receive == 0);
+                       port->ip_guarded = 1;
+                       port->ip_strict_guard = 0;
+                       /* pseudo receive shouldn't set the receive right as immovable in the sender's space */
+                       if (kn != ITH_KNOTE_PSEUDO) {
+                               port->ip_immovable_receive = 1;
+                       }
+                       port->ip_context = current_thread()->ith_msg_addr;
+                       *context = port->ip_context;
+                       *guard_flags = *guard_flags & ~MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND;
+               }
 
+               assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
                if (bits & MACH_PORT_TYPE_SEND) {
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
                        assert(IE_BITS_UREFS(bits) > 0);
                        assert(port->ip_srights > 0);
-
-                       ip_release(port);
-                       ip_unlock(port);
-
-                       /* entry is locked holding ref, so can use port */
-
-                       ipc_hash_delete(space, (ipc_object_t) port,
-                                       name, entry);
                } else {
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
                        assert(IE_BITS_UREFS(bits) == 0);
-
-                       /* transfer ref to entry */
-                       ip_unlock(port);
                }
-               entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE;
-
-               if (dest != IP_NULL)
-                       ipc_port_release(dest);
-               break;
-           }
-
-           default:
-               panic("ipc_right_copyout: strange rights");
-       }
-
-       return KERN_SUCCESS;
-}
-
-/*
- *     Routine:        ipc_right_rename
- *     Purpose:
- *             Transfer an entry from one name to another.
- *             The old entry is deallocated.
- *     Conditions:
- *             The space is write-locked and active.
- *             The new entry is unused.  Upon return,
- *             the space is unlocked.
- *     Returns:
- *             KERN_SUCCESS            Moved entry to new name.
- */
-
-kern_return_t
-ipc_right_rename(
-       ipc_space_t             space,
-       mach_port_name_t        oname,
-       ipc_entry_t             oentry,
-       mach_port_name_t        nname,
-       ipc_entry_t             nentry)
-{
-       ipc_port_request_index_t request = oentry->ie_request;
-       ipc_entry_bits_t bits = oentry->ie_bits;
-       ipc_object_t object = oentry->ie_object;
-
-       assert(space->is_active);
-       assert(oname != nname);
-
-       /*
-        *      If IE_BITS_COMPAT, we can't allow the entry to be renamed
-        *      if the port is dead.  (This would foil ipc_port_destroy.)
-        *      Instead we should fail because oentry shouldn't exist.
-        *      Note IE_BITS_COMPAT implies ie_request != 0.
-        */
 
-       if (request != 0) {
-               ipc_port_t port;
-
-               assert(bits & MACH_PORT_TYPE_PORT_RIGHTS);
-               port = (ipc_port_t) object;
-               assert(port != IP_NULL);
-
-               if (ipc_right_check(space, port, oname, oentry)) {
-                       request = 0;
-                       object = IO_NULL;
-                       bits = oentry->ie_bits;
-                       assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
-                       assert(oentry->ie_request == 0);
-               } else {
-                       /* port is locked and active */
-
-                       ipc_port_dnrename(port, request, oname, nname);
-                       ip_unlock(port);
-                       oentry->ie_request = 0;
+               boolean_t sync_bootstrap_checkin = FALSE;
+               if (kn != ITH_KNOTE_PSEUDO && port->ip_sync_bootstrap_checkin) {
+                       sync_bootstrap_checkin = TRUE;
                }
-       }
-
-       /* initialize nentry before letting ipc_hash_insert see it */
-
-       assert((nentry->ie_bits & IE_BITS_RIGHT_MASK) == 0);
-       nentry->ie_bits |= bits & IE_BITS_RIGHT_MASK;
-       nentry->ie_request = request;
-       nentry->ie_object = object;
-
-       switch (IE_BITS_TYPE(bits)) {
-           case MACH_PORT_TYPE_SEND: {
-               ipc_port_t port;
-
-               port = (ipc_port_t) object;
-               assert(port != IP_NULL);
-
-               /* remember, there are no other share entries possible */
-               /* or we can't do the rename.  Therefore we do not need */
-               /* to check the other subspaces */
-               ipc_hash_delete(space, (ipc_object_t) port, oname, oentry);
-               ipc_hash_insert(space, (ipc_object_t) port, nname, nentry);
-               break;
-           }
-
-           case MACH_PORT_TYPE_RECEIVE:
-           case MACH_PORT_TYPE_SEND_RECEIVE: {
-               ipc_port_t port;
-
-               port = (ipc_port_t) object;
-               assert(port != IP_NULL);
-
-               ip_lock(port);
-               assert(ip_active(port));
-               assert(port->ip_receiver_name == oname);
-               assert(port->ip_receiver == space);
-
-               port->ip_receiver_name = nname;
-               ip_unlock(port);
-               break;
-           }
-
-           case MACH_PORT_TYPE_PORT_SET: {
-               ipc_pset_t pset;
-
-               pset = (ipc_pset_t) object;
-               assert(pset != IPS_NULL);
+               if (!ITH_KNOTE_VALID(kn, MACH_MSG_TYPE_PORT_RECEIVE)) {
+                       kn = NULL;
+               }
+               ipc_port_adjust_port_locked(port, kn, sync_bootstrap_checkin);
+               /* port & message queue are unlocked */
 
-               ips_lock(pset);
-               assert(ips_active(pset));
-               assert(pset->ips_local_name == oname);
+               if (bits & MACH_PORT_TYPE_SEND) {
+                       ip_release(port);
 
-               pset->ips_local_name = nname;
-               ips_unlock(pset);
-               break;
-           }
+                       /* entry is locked holding ref, so can use port */
+                       ipc_hash_delete(space, ip_to_object(port), name, entry);
+               }
+               entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE;
+               ipc_entry_modified(space, name, entry);
 
-           case MACH_PORT_TYPE_SEND_ONCE:
-           case MACH_PORT_TYPE_DEAD_NAME:
+               if (dest != IP_NULL) {
+#if IMPORTANCE_INHERITANCE
+                       /*
+                        * Deduct the assertion counts we contributed to
+                        * the old destination port.  They've already
+                        * been reflected into the task as a result of
+                        * getting enqueued.
+                        */
+                       ip_lock(dest);
+                       ipc_port_impcount_delta(dest, 0 - assertcnt, IP_NULL);
+                       ip_unlock(dest);
+#endif /* IMPORTANCE_INHERITANCE */
+
+                       /* Drop turnstile ref on dest */
+                       ipc_port_send_turnstile_complete(dest);
+                       ip_release(dest);
+               }
                break;
-
-           default:
-               panic("ipc_right_rename: strange rights");
        }
 
-       assert(oentry->ie_request == 0);
-       oentry->ie_object = IO_NULL;
-       ipc_entry_dealloc(space, oname, oentry);
-       is_write_unlock(space);
-
+       default:
+               panic("ipc_right_copyout: strange rights");
+       }
        return KERN_SUCCESS;
 }