]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/ipc/ipc_right.c
xnu-3247.1.106.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_right.c
index 46d7f1ec7e821df8f6f3a6e26583c55c741faf6f..b8feb54dc2cc826e8512b6e4d3b5c9434227271c 100644 (file)
 #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>
 
+/* Allow IPC to generate mach port guard exceptions */
+extern kern_return_t
+mach_port_guard_exception(
+       mach_port_name_t        name,
+       uint64_t                inguard,
+       uint64_t                portguard,
+       unsigned                reason);
 /*
  *     Routine:        ipc_right_lookup_write
  *     Purpose:
@@ -272,6 +280,10 @@ ipc_right_request_alloc(
        ipc_entry_t entry;
        kern_return_t kr;
 
+#if IMPORTANCE_INHERITANCE
+       boolean_t needboost = FALSE;
+#endif /* IMPORTANCE_INHERITANCE */
+
        for (;;) {
                ipc_port_t port = IP_NULL;
 
@@ -336,9 +348,15 @@ ipc_right_request_alloc(
                                if (prev_request != IE_REQ_NONE)
                                        previous = ipc_port_request_cancel(port, name, prev_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);
@@ -352,11 +370,20 @@ ipc_right_request_alloc(
                                        continue;
                                }
 
+
                                assert(new_request != IE_REQ_NONE);
-                               ip_unlock(port);
                                entry->ie_request = new_request;
                                ipc_entry_modified(space, name, entry);
                                is_write_unlock(space);
+
+#if IMPORTANCE_INHERITANCE
+                               if (needboost == TRUE) {
+                                       if (ipc_port_importance_delta(port, 1) == FALSE)
+                                               ip_unlock(port);
+                               } else
+#endif /* IMPORTANCE_INHERITANCE */
+                                       ip_unlock(port);
+
                                break;
                        }
                        /* entry may have changed to dead-name by ipc_right_check() */
@@ -487,7 +514,6 @@ ipc_right_check(
        ip_lock(port);
        if (ip_active(port))
                return FALSE;
-       ip_unlock(port);
 
        /* this was either a pure send right or a send-once right */
 
@@ -495,25 +521,34 @@ ipc_right_check(
        assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
        assert(IE_BITS_UREFS(bits) > 0);
 
-       if (bits & MACH_PORT_TYPE_SEND) {   
+       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.
+        */
 
-       /* convert entry to dead name */
-
-       if ((bits & MACH_PORT_TYPE_SEND) && !(bits & MACH_PORT_TYPE_RECEIVE))
+       if ((bits & MACH_PORT_TYPE_SEND) != 0) {
                ipc_hash_delete(space, (ipc_object_t)port, name, entry);
+       }
 
+       /* convert entry to dead name */
        bits = (bits &~ IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME;
        
        /*
         * If there was a notification request outstanding on this
         * name, and the port went dead, that notification
-        * must already be on its way up from the port layer. 
+        * must already be on its way up from the port layer.
         *
         * Add the reference that the notification carries. It
         * is done here, and not in the notification delivery,
@@ -624,20 +659,11 @@ ipc_right_terminate(
                }
 
                if (type & MACH_PORT_TYPE_RECEIVE) {
-                       wait_queue_link_t wql;
-                       queue_head_t links_data;
-                       queue_t links = &links_data;
-
                        assert(port->ip_receiver_name == name);
                        assert(port->ip_receiver == space);
 
-                       queue_init(links);
-                       ipc_port_clear_receiver(port, links);
+                       ipc_port_clear_receiver(port);
                        ipc_port_destroy(port); /* consumes our ref, unlocks */
-                       while(!queue_empty(links)) {
-                               wql = (wait_queue_link_t) dequeue(links);
-                               wait_queue_link_free(wql);
-                       }
 
                } else if (type & MACH_PORT_TYPE_SEND_ONCE) {
                        assert(port->ip_sorights > 0);
@@ -679,7 +705,9 @@ kern_return_t
 ipc_right_destroy(
        ipc_space_t             space,
        mach_port_name_t        name,
-       ipc_entry_t             entry)
+       ipc_entry_t             entry,
+       boolean_t               check_guard,
+       uint64_t                guard)
 {
        ipc_entry_bits_t bits;
        mach_port_type_t type;
@@ -744,6 +772,20 @@ ipc_right_destroy(
                        break;
                }
 
+               /* 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;
@@ -762,20 +804,11 @@ ipc_right_destroy(
                }
 
                if (type & MACH_PORT_TYPE_RECEIVE) {
-                       queue_head_t links_data;
-                       queue_t links = &links_data;
-                       wait_queue_link_t wql;
-
                        assert(ip_active(port));
                        assert(port->ip_receiver == space);
 
-                       queue_init(links);
-                       ipc_port_clear_receiver(port, links);
+                       ipc_port_clear_receiver(port);
                        ipc_port_destroy(port); /* consumes our ref, unlocks */
-                       while(!queue_empty(links)) {
-                               wql = (wait_queue_link_t) dequeue(links);
-                               wait_queue_link_free(wql);
-                       }
 
                } else if (type & MACH_PORT_TYPE_SEND_ONCE) {
                        assert(port->ip_sorights > 0);
@@ -794,6 +827,8 @@ ipc_right_destroy(
 
                if (request != IP_NULL)
                        ipc_notify_port_deleted(request, name);
+
+
                break;
            }
 
@@ -1017,7 +1052,7 @@ ipc_right_delta(
 {
        ipc_port_t port = IP_NULL;
        ipc_entry_bits_t bits;
-       
+
        bits = entry->ie_bits;
 
 
@@ -1067,9 +1102,6 @@ ipc_right_delta(
 
            case MACH_PORT_RIGHT_RECEIVE: {
                ipc_port_t request = IP_NULL;
-               queue_head_t links_data;
-               queue_t links = &links_data;
-               wait_queue_link_t wql;
 
                if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
                        goto invalid_right;
@@ -1093,7 +1125,17 @@ ipc_right_delta(
                assert(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);
@@ -1146,13 +1188,8 @@ ipc_right_delta(
                }
                is_write_unlock(space);
 
-               queue_init(links);
-               ipc_port_clear_receiver(port, links);
+               ipc_port_clear_receiver(port);
                ipc_port_destroy(port); /* consumes ref, unlocks */
-               while(!queue_empty(links)) {
-                       wql = (wait_queue_link_t) dequeue(links);
-                       wait_queue_link_free(wql);
-               }
 
                if (request != IP_NULL)
                        ipc_notify_port_deleted(request, name);
@@ -1205,6 +1242,7 @@ ipc_right_delta(
            }
 
            case MACH_PORT_RIGHT_DEAD_NAME: {
+               ipc_port_t relport = IP_NULL;
                mach_port_urefs_t urefs;
 
                if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
@@ -1219,6 +1257,8 @@ ipc_right_delta(
                                goto invalid_right;
                        }
                        bits = entry->ie_bits;
+                       relport = port;
+                       port = IP_NULL;
                } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0)
                        goto invalid_right;
 
@@ -1241,6 +1281,9 @@ ipc_right_delta(
                }
                is_write_unlock(space);
 
+               if (relport != IP_NULL)
+                       ip_release(relport);
+
                break;
            }
 
@@ -1349,8 +1392,181 @@ ipc_right_delta(
     urefs_overflow:
        is_write_unlock(space);
        return KERN_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) ||
+           (srdelta && ((bits & MACH_PORT_TYPE_SEND) == 0))) {
+               is_write_unlock(space);
+               return KERN_INVALID_RIGHT;
+       }
+
+       if (srdelta > 0)
+               goto invalid_value;
+
+       port = (ipc_port_t) 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);
+
+       /* 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 + 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;
+                       ipc_hash_insert(space, (ipc_object_t) 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;
+                       if (entry->ie_request) {
+                               entry->ie_request = IE_REQ_NONE;
+                               bits++;
+                       }
+                       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_clear_receiver(port);
+       ipc_port_destroy(port); /* consumes ref, unlocks */
+
+       if (request != IP_NULL)
+               ipc_notify_port_deleted(request, name);
+       
+       return KERN_SUCCESS;
+       
+    invalid_value:
+       is_write_unlock(space);
+       return KERN_INVALID_VALUE;
+
+}
+
+
 /*
  *     Routine:        ipc_right_info
  *     Purpose:
@@ -1435,10 +1651,6 @@ ipc_right_copyin_check(
 {
        ipc_entry_bits_t bits;
        ipc_port_t port;
-#if CONFIG_MACF_MACH
-       task_t self = current_task();
-       int rc = 0;
-#endif
 
        bits= entry->ie_bits;
        assert(is_active(space));
@@ -1447,54 +1659,21 @@ ipc_right_copyin_check(
            case MACH_MSG_TYPE_MAKE_SEND:
                if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
                        return FALSE;
-
-#if CONFIG_MACF_MACH
-               port = (ipc_port_t) entry->ie_object;
-               ip_lock(port);
-               tasklabel_lock(self);
-               rc = mac_port_check_make_send(&self->maclabel, &port->ip_label);                tasklabel_unlock(self);
-               ip_unlock(port);
-               if (rc)
-                       return FALSE;
-#endif
                break;
 
            case MACH_MSG_TYPE_MAKE_SEND_ONCE:
                if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
                        return FALSE;
-
-#if CONFIG_MACF_MACH
-               port = (ipc_port_t) entry->ie_object;
-               ip_lock(port);
-               tasklabel_lock(self);
-               rc = mac_port_check_make_send_once(&self->maclabel, &port->ip_label);
-               tasklabel_unlock(self);
-               ip_unlock(port);
-               if (rc)
-                       return FALSE;
-#endif
                break;
 
            case MACH_MSG_TYPE_MOVE_RECEIVE:
                if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
                        return FALSE;
-
-#if CONFIG_MACF_MACH
-               port = (ipc_port_t) entry->ie_object;
-               ip_lock(port);
-               tasklabel_lock(self);
-               rc = mac_port_check_move_receive(&self->maclabel, &port->ip_label);
-               tasklabel_unlock(self);
-               ip_unlock(port);
-               if (rc)
-                        return FALSE;
-#endif
                break;
 
            case MACH_MSG_TYPE_COPY_SEND:
            case MACH_MSG_TYPE_MOVE_SEND:
            case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
-               boolean_t active;
 
                if (bits & MACH_PORT_TYPE_DEAD_NAME)
                        break;
@@ -1505,35 +1684,12 @@ ipc_right_copyin_check(
                port = (ipc_port_t) entry->ie_object;
                assert(port != IP_NULL);
 
-               ip_lock(port);
-               active = ip_active(port);
-#if CONFIG_MACF_MACH
-               tasklabel_lock(self);
-               switch (msgt_name) {
-               case MACH_MSG_TYPE_COPY_SEND:
-                       rc = mac_port_check_copy_send(&self->maclabel,
-                           &port->ip_label);
-                       break;
-               case MACH_MSG_TYPE_MOVE_SEND:
-                       rc = mac_port_check_move_send(&self->maclabel,
-                           &port->ip_label);
-                       break;
-               case MACH_MSG_TYPE_MOVE_SEND_ONCE:
-                       rc = mac_port_check_move_send_once(&self->maclabel,
-                           &port->ip_label);
-                       break;
-               default:
-                       panic("ipc_right_copyin_check: strange rights");
-               }
-               tasklabel_unlock(self);
-               if (rc) {
-                       ip_unlock(port);
-                       return FALSE;
-               }
-#endif
-               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(port)) {
                        break;
                }
 
@@ -1588,16 +1744,13 @@ ipc_right_copyin(
        ipc_object_t            *objectp,
        ipc_port_t              *sorightp,
        ipc_port_t              *releasep,
-       queue_t                 links)
+       int                     *assertcntp)
 {
        ipc_entry_bits_t bits;
        ipc_port_t port;
-#if CONFIG_MACF_MACH
-       task_t self = current_task();
-       int    rc;
-#endif
-       
+
        *releasep = IP_NULL;
+       *assertcntp = 0;
 
        bits = entry->ie_bits;
 
@@ -1617,16 +1770,6 @@ ipc_right_copyin(
                assert(port->ip_receiver_name == name);
                assert(port->ip_receiver == space);
 
-#if CONFIG_MACF_MACH
-               tasklabel_lock(self);
-               rc = mac_port_check_make_send(&self->maclabel, &port->ip_label);
-               tasklabel_unlock(self);
-               if (rc) {
-                       ip_unlock(port);
-                       return KERN_NO_ACCESS;
-               }
-#endif
-
                port->ip_mscount++;
                port->ip_srights++;
                ip_reference(port);
@@ -1650,16 +1793,6 @@ ipc_right_copyin(
                assert(port->ip_receiver_name == name);
                assert(port->ip_receiver == space);
 
-#if CONFIG_MACF_MACH
-               tasklabel_lock(self);
-               rc = mac_port_check_make_send_once(&self->maclabel, &port->ip_label);
-               tasklabel_unlock(self);
-               if (rc) {
-                       ip_unlock(port);
-                       return KERN_NO_ACCESS;
-               }
-#endif
-
                port->ip_sorights++;
                ip_reference(port);
                ip_unlock(port);
@@ -1683,17 +1816,6 @@ ipc_right_copyin(
                assert(port->ip_receiver_name == name);
                assert(port->ip_receiver == space);
 
-#if CONFIG_MACF_MACH
-               tasklabel_lock(self);
-               rc = mac_port_check_move_receive(&self->maclabel,
-                                                &port->ip_label);
-               tasklabel_unlock(self);
-               if (rc) {
-                       ip_unlock(port);
-                       return KERN_NO_ACCESS;
-               }
-#endif
-
                if (bits & MACH_PORT_TYPE_SEND) {
                        assert(IE_BITS_TYPE(bits) ==
                                        MACH_PORT_TYPE_SEND_RECEIVE);
@@ -1714,9 +1836,29 @@ ipc_right_copyin(
                entry->ie_bits = bits &~ MACH_PORT_TYPE_RECEIVE;
                ipc_entry_modified(space, name, entry);
 
-               ipc_port_clear_receiver(port, links);
+               ipc_port_clear_receiver(port);
                port->ip_receiver_name = MACH_PORT_NULL;
                port->ip_destination = IP_NULL;
+
+#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);
+
+                       /* ports in limbo have to be tempowner */
+                       port->ip_tempowner = 1;
+                       *assertcntp = port->ip_impcount;
+               }
+#endif /* IMPORTANCE_INHERITANCE */
+
                ip_unlock(port);
 
                *objectp = (ipc_object_t) port;
@@ -1746,16 +1888,6 @@ ipc_right_copyin(
                }
                /* port is locked and active */
 
-#if CONFIG_MACF_MACH
-               tasklabel_lock(self);
-               rc = mac_port_check_copy_send(&self->maclabel, &port->ip_label);
-               tasklabel_unlock(self);
-               if (rc) {
-                       ip_unlock(port);
-                       return KERN_NO_ACCESS;
-               }
-#endif
-
                if ((bits & MACH_PORT_TYPE_SEND) == 0) {
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
                        assert(port->ip_sorights > 0);
@@ -1798,17 +1930,6 @@ ipc_right_copyin(
                }
                /* port is locked and active */
 
-#if CONFIG_MACF_MACH
-               tasklabel_lock (self);
-               rc = mac_port_check_copy_send (&self->maclabel, &port->ip_label);
-               tasklabel_unlock (self);
-               if (rc)
-                 {
-                   ip_unlock (port);
-                   return KERN_NO_ACCESS;
-                 }
-#endif
-
                if ((bits & MACH_PORT_TYPE_SEND) == 0) {
                        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
                        assert(port->ip_sorights > 0);
@@ -1870,21 +1991,11 @@ ipc_right_copyin(
 
                if (ipc_right_check(space, port, name, entry)) {
                        bits = entry->ie_bits;
+                       *releasep = port;
                        goto move_dead;
                }
                /* port is locked and active */
 
-#if CONFIG_MACF_MACH
-               tasklabel_lock (self);
-               rc = mac_port_check_copy_send (&self->maclabel, &port->ip_label);
-               tasklabel_unlock (self);
-               if (rc)
-                 {
-                   ip_unlock (port);
-                   return KERN_NO_ACCESS;
-                 }
-#endif
-
                if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
                        assert(bits & MACH_PORT_TYPE_SEND);
                        assert(port->ip_srights > 0);
@@ -2031,7 +2142,7 @@ ipc_right_copyin_undo(
 }
 
 /*
- *     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
@@ -2043,9 +2154,9 @@ 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_right_copyin_two_move_sends(
        ipc_space_t             space,
        mach_port_name_t        name,
        ipc_entry_t             entry,
@@ -2057,10 +2168,6 @@ ipc_right_copyin_two(
        mach_port_urefs_t urefs;
        ipc_port_t port;
        ipc_port_t request = IP_NULL;
-#if CONFIG_MACF_MACH
-       task_t self = current_task();
-       int    rc;
-#endif
 
        *releasep = IP_NULL;
 
@@ -2084,16 +2191,6 @@ ipc_right_copyin_two(
        }
        /* port is locked and active */
 
-#if CONFIG_MACF_MACH
-       tasklabel_lock(self);
-       rc = mac_port_check_copy_send(&self->maclabel, &port->ip_label);
-       tasklabel_unlock(self);
-       if (rc) {
-               ip_unlock(port);
-               return KERN_NO_ACCESS;
-       }
-#endif
-
        assert(port->ip_srights > 0);
 
        if (urefs == 2) {
@@ -2137,6 +2234,158 @@ ipc_right_copyin_two(
        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.
+ *     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));
+       
+
+       /*
+        * Pre-validate the second disposition is possible all by itself.
+        */
+       if (!ipc_right_copyin_check(space, name, entry, msgt_two)) {
+               return KERN_INVALID_CAPABILITY;
+       }
+
+       /*
+        *      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, FALSE,
+                                     objectp, sorightp, releasep,
+                                     &assertcnt);
+               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, FALSE,
+                                     &object_two, sorightp, releasep,
+                                     &assertcnt);
+               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, FALSE,
+                                     objectp, sorightp, releasep,
+                                     &assertcnt);
+               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((ipc_port_t)*objectp);
+       }
+
+       return KERN_SUCCESS;
+}
+
+
 /*
  *     Routine:        ipc_right_copyout
  *     Purpose:
@@ -2172,9 +2421,6 @@ ipc_right_copyout(
 {
        ipc_entry_bits_t bits;
        ipc_port_t port;
-#if CONFIG_MACF_MACH
-       int rc;
-#endif
 
        bits = entry->ie_bits;
 
@@ -2191,19 +2437,6 @@ ipc_right_copyout(
                assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
                assert(port->ip_sorights > 0);
 
-#if CONFIG_MACF_MACH
-               if (space->is_task) {
-                       tasklabel_lock(space->is_task);
-                       rc = mac_port_check_hold_send_once(&space->is_task->maclabel,
-                                                          &port->ip_label);
-                       tasklabel_unlock(space->is_task);
-
-                       if (rc) {
-                               ip_unlock(port);
-                               return KERN_NO_ACCESS;
-                       }
-               }
-#endif
                /* transfer send-once right and ref to entry */
                ip_unlock(port);
 
@@ -2214,20 +2447,6 @@ ipc_right_copyout(
            case MACH_MSG_TYPE_PORT_SEND:
                assert(port->ip_srights > 0);
 
-#if CONFIG_MACF_MACH
-               if (space->is_task) {
-                       tasklabel_lock(space->is_task);
-                       rc = mac_port_check_hold_send(&space->is_task->maclabel,
-                                                     &port->ip_label);
-                       tasklabel_unlock(space->is_task);
-
-                       if (rc) {
-                               ip_unlock(port);
-                               return KERN_NO_ACCESS;
-                       }
-               }
-#endif
-
                if (bits & MACH_PORT_TYPE_SEND) {
                        mach_port_urefs_t urefs = IE_BITS_UREFS(bits);
 
@@ -2280,24 +2499,14 @@ ipc_right_copyout(
            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);
                dest = port->ip_destination;
 
-#if CONFIG_MACF_MACH
-               if (space->is_task) {
-                       tasklabel_lock(space->is_task);
-                       rc = mac_port_check_hold_receive(&space->is_task->maclabel,
-                                                        &port->ip_label);
-                       tasklabel_unlock(space->is_task);
-
-                       if (rc) {
-                               ip_unlock(port);
-                               return KERN_NO_ACCESS;
-                       }
-               }
-#endif
-
                port->ip_receiver_name = name;
                port->ip_receiver = space;
 
@@ -2325,15 +2534,26 @@ ipc_right_copyout(
                entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE;
                ipc_entry_modified(space, name, entry);
 
-               if (dest != IP_NULL)
+               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 */
                        ip_release(dest);
+               }
                break;
            }
 
            default:
                panic("ipc_right_copyout: strange rights");
        }
-
        return KERN_SUCCESS;
 }