]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/ipc/ipc_right.c
xnu-3789.70.16.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_right.c
index f4e102b47db6e64c8a3e715fa1139ca0dc03cc38..7a937f5bef66d0d1666847f89703dfe977b89aa5 100644 (file)
@@ -75,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>
@@ -86,6 +87,7 @@
 #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 */
@@ -260,8 +262,6 @@ 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.
  */
 
@@ -377,7 +377,7 @@ ipc_right_request_alloc(
 
 #if IMPORTANCE_INHERITANCE
                                if (needboost == TRUE) {
-                                       if (ipc_port_importance_delta(port, 1) == FALSE)
+                                       if (ipc_port_importance_delta(port, IPID_OPTION_SENDPOSSIBLE, 1) == FALSE)
                                                ip_unlock(port);
                                } else
 #endif /* IMPORTANCE_INHERITANCE */
@@ -396,15 +396,12 @@ ipc_right_request_alloc(
 
                        assert(urefs > 0);
 
-                       if (MACH_PORT_UREFS_OVERFLOW(urefs, 1)) {
-                               is_write_unlock(space);
-                               if (port != IP_NULL)
-                                       ip_release(port);
-                               return KERN_UREFS_OVERFLOW;
-                       }
+                       /* leave urefs pegged to maximum if it overflowed */
+                       if (urefs < MACH_PORT_UREFS_MAX)
+                               (entry->ie_bits)++; /* increment urefs */
 
-                       (entry->ie_bits)++; /* increment urefs */
                        ipc_entry_modified(space, name, entry);
+
                        is_write_unlock(space);
 
                        if (port != IP_NULL)
@@ -415,15 +412,15 @@ ipc_right_request_alloc(
                        break;
                }
 
+               kr = (entry->ie_bits & MACH_PORT_TYPE_PORT_OR_DEAD) ?
+                   KERN_INVALID_ARGUMENT : KERN_INVALID_RIGHT;
+
                is_write_unlock(space);
 
                if (port != IP_NULL)
                        ip_release(port);
 
-               if (entry->ie_bits & MACH_PORT_TYPE_PORT_OR_DEAD)
-                       return KERN_INVALID_ARGUMENT;
-               else
-                       return KERN_INVALID_RIGHT;
+               return kr;
        }
 
        *previousp = previous;
@@ -562,8 +559,9 @@ ipc_right_check(
         */
        if (entry->ie_request != IE_REQ_NONE) {
                if (ipc_port_request_type(port, name, entry->ie_request) != 0) {
-                       assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
-                       bits++; 
+                       /* 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; 
        }
@@ -658,20 +656,10 @@ 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_destroy(port); /* consumes our ref, unlocks */
-                       while(!queue_empty(links)) {
-                               wql = (wait_queue_link_t) dequeue(links);
-                               wait_queue_link_free(wql);
-                       }
+                       ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */
 
                } else if (type & MACH_PORT_TYPE_SEND_ONCE) {
                        assert(port->ip_sorights > 0);
@@ -812,22 +800,10 @@ 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_destroy(port); /* consumes our ref, unlocks */
-
-                       while(!queue_empty(links)) {
-                               wql = (wait_queue_link_t) dequeue(links);
-                               wait_queue_link_free(wql);
-                       }
+                       ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */
 
                } else if (type & MACH_PORT_TYPE_SEND_ONCE) {
                        assert(port->ip_sorights > 0);
@@ -899,7 +875,9 @@ ipc_right_dealloc(
                if (IE_BITS_UREFS(bits) == 1) {
                        ipc_entry_dealloc(space, name, entry);
                } else {
-                       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);
                }
                is_write_unlock(space);
@@ -984,12 +962,13 @@ ipc_right_dealloc(
                        ip_release(port);
 
                } else {
-                       ip_unlock(port);                        
-                       entry->ie_bits = bits-1; /* decrement urefs */
+                       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);
                }
-               
 
                if (nsrequest != IP_NULL)
                        ipc_notify_no_senders(nsrequest, mscount);
@@ -1025,9 +1004,12 @@ ipc_right_dealloc(
 
                        entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK |
                                                  MACH_PORT_TYPE_SEND);
-               } else
-                       entry->ie_bits = bits-1; /* decrement urefs */
-
+               } 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);
@@ -1058,7 +1040,6 @@ 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
@@ -1121,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;
@@ -1162,7 +1140,6 @@ ipc_right_delta(
                        assert(IE_BITS_TYPE(bits) ==
                                        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);
 
                        if (port->ip_pdrequest != NULL) {
@@ -1193,7 +1170,9 @@ ipc_right_delta(
                                bits |= MACH_PORT_TYPE_DEAD_NAME;
                                if (entry->ie_request) {
                                        entry->ie_request = IE_REQ_NONE;
-                                       bits++;
+                                       /* 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;
@@ -1210,14 +1189,7 @@ ipc_right_delta(
                }
                is_write_unlock(space);
 
-               queue_init(links);
-               ipc_port_clear_receiver(port, links);
-               ipc_port_destroy(port); /* consumes ref, unlocks */
-
-               while(!queue_empty(links)) {
-                       wql = (wait_queue_link_t) dequeue(links);
-                       wait_queue_link_free(wql);
-               }
+               ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */
 
                if (request != IP_NULL)
                        ipc_notify_port_deleted(request, name);
@@ -1287,26 +1259,46 @@ ipc_right_delta(
                        bits = entry->ie_bits;
                        relport = port;
                        port = IP_NULL;
-               } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0)
+               } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0) {
                        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 == 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)
@@ -1319,12 +1311,13 @@ ipc_right_delta(
                mach_port_urefs_t urefs;
                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)
                        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;
                assert(port != IP_NULL);
@@ -1337,14 +1330,31 @@ 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) {
@@ -1359,7 +1369,7 @@ ipc_right_delta(
                        if (bits & MACH_PORT_TYPE_RECEIVE) {
                                assert(port->ip_receiver_name == name);
                                assert(port->ip_receiver == space);
-                               ip_unlock(port);                                
+                               ip_unlock(port);
                                assert(IE_BITS_TYPE(bits) ==
                                                MACH_PORT_TYPE_SEND_RECEIVE);
 
@@ -1376,19 +1386,24 @@ ipc_right_delta(
                                                name, entry);
 
                                ip_unlock(port);
-                               ip_release(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);
                }
 
                is_write_unlock(space);
 
+               if (port_to_release != IP_NULL)
+                       ip_release(port_to_release);
+
                if (nsrequest != IP_NULL)
                        ipc_notify_no_senders(nsrequest, mscount);
 
@@ -1417,12 +1432,8 @@ ipc_right_delta(
        is_write_unlock(space);
        return KERN_INVALID_VALUE;
 
-    urefs_overflow:
-       is_write_unlock(space);
-       return KERN_UREFS_OVERFLOW;
-       
     guard_failure:
-       return KERN_INVALID_RIGHT;              
+       return KERN_INVALID_RIGHT;
 }
 
 /*
@@ -1450,10 +1461,6 @@ ipc_right_destruct(
        ipc_port_t port = IP_NULL;
        ipc_entry_bits_t bits;
 
-       queue_head_t links_data;
-       queue_t links = &links_data;
-       wait_queue_link_t wql;
-
        mach_port_urefs_t urefs;
        ipc_port_t request = IP_NULL;
        ipc_port_t nsrequest = IP_NULL;
@@ -1474,7 +1481,7 @@ ipc_right_destruct(
 
        port = (ipc_port_t) entry->ie_object;
        assert(port != IP_NULL);
-       
+
        ip_lock(port);
        assert(ip_active(port));
        assert(port->ip_receiver_name == name);
@@ -1497,10 +1504,11 @@ ipc_right_destruct(
         */
 
        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.
@@ -1509,6 +1517,16 @@ ipc_right_destruct(
                        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;
@@ -1533,7 +1551,7 @@ ipc_right_destruct(
        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);
+               assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX);
 
                if (port->ip_pdrequest != NULL) {
                        /*
@@ -1563,7 +1581,8 @@ ipc_right_destruct(
                        bits |= MACH_PORT_TYPE_DEAD_NAME;
                        if (entry->ie_request) {
                                entry->ie_request = IE_REQ_NONE;
-                               bits++;
+                               if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+                                       bits++; /* increment urefs */
                        }
                        entry->ie_bits = bits;
                        entry->ie_object = IO_NULL;
@@ -1584,14 +1603,7 @@ ipc_right_destruct(
        if (nsrequest != IP_NULL)
                ipc_notify_no_senders(nsrequest, mscount);
 
-       queue_init(links);
-       ipc_port_clear_receiver(port, links);
-       ipc_port_destroy(port); /* consumes ref, unlocks */
-
-       while(!queue_empty(links)) {
-               wql = (wait_queue_link_t) dequeue(links);
-               wait_queue_link_free(wql);
-       }
+       ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */
 
        if (request != IP_NULL)
                ipc_notify_port_deleted(request, name);
@@ -1689,10 +1701,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));
@@ -1701,54 +1709,23 @@ 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
+               if (io_kotype(entry->ie_object) != IKOT_NONE)
+                       return FALSE;
                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;
@@ -1759,35 +1736,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;
                }
 
@@ -1842,23 +1796,13 @@ ipc_right_copyin(
        ipc_object_t            *objectp,
        ipc_port_t              *sorightp,
        ipc_port_t              *releasep,
-#if IMPORTANCE_INHERITANCE
-       int                     *assertcntp,
-#endif /* IMPORTANCE_INHERITANCE */
-       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;
 
-#if IMPORTANCE_INHERITANCE
+       *releasep = IP_NULL;
        *assertcntp = 0;
-#endif
 
        bits = entry->ie_bits;
 
@@ -1878,16 +1822,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);
@@ -1911,16 +1845,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);
@@ -1936,6 +1860,23 @@ ipc_right_copyin(
                if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
                        goto invalid_right;
 
+               /*
+                * 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."
+                        */
+                       return KERN_INVALID_CAPABILITY;
+               }
+
                port = (ipc_port_t) entry->ie_object;
                assert(port != IP_NULL);
 
@@ -1944,17 +1885,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);
@@ -1975,7 +1905,7 @@ ipc_right_copyin(
                entry->ie_bits = bits &~ MACH_PORT_TYPE_RECEIVE;
                ipc_entry_modified(space, name, entry);
 
-               ipc_port_clear_receiver(port, links);
+               (void)ipc_port_clear_receiver(port, FALSE); /* don't destroy the port/mqueue */
                port->ip_receiver_name = MACH_PORT_NULL;
                port->ip_destination = IP_NULL;
 
@@ -1990,7 +1920,7 @@ ipc_right_copyin(
                 * destination port (see ipc_port_check_circularity()).
                 */
                if (port->ip_tempowner == 0) {
-                       assert(port->ip_taskptr == 0);
+                       assert(IIT_NULL == port->ip_imp_task);
 
                        /* ports in limbo have to be tempowner */
                        port->ip_tempowner = 1;
@@ -2027,16 +1957,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);
@@ -2079,17 +1999,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);
@@ -2117,14 +2026,18 @@ ipc_right_copyin(
                                ipc_hash_delete(space, (ipc_object_t) 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);
                } else {
                        port->ip_srights++;
                        ip_reference(port);
-                       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);
                ip_unlock(port);
 
@@ -2151,21 +2064,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);
@@ -2222,7 +2125,10 @@ ipc_right_copyin(
        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;
@@ -2279,8 +2185,10 @@ ipc_right_copyin_undo(
                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 */
+                       assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX);
+                       /* if urefs are pegged due to overflow, leave them pegged */
+                       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+                               entry->ie_bits = bits+1; /* increment urefs */
                }
        } else {
                assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
@@ -2291,8 +2199,10 @@ ipc_right_copyin_undo(
                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 */
+                       assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX);
+                       /* if urefs are pegged due to overflow, leave them pegged */
+                       if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+                               entry->ie_bits = bits+1; /* increment urefs */
                }
 
                /*
@@ -2312,7 +2222,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
@@ -2324,9 +2234,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,
@@ -2338,10 +2248,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;
 
@@ -2365,16 +2271,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) {
@@ -2404,7 +2300,9 @@ ipc_right_copyin_two(
                port->ip_srights += 2;
                ip_reference(port);
                ip_reference(port);
-               entry->ie_bits = bits-2; /* 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-2; /* decrement urefs */
        }
        ipc_entry_modified(space, name, entry);
 
@@ -2418,6 +2316,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:
@@ -2437,9 +2487,6 @@ ipc_right_copyin_two(
  *             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
@@ -2448,14 +2495,11 @@ ipc_right_copyout(
        mach_port_name_t        name,
        ipc_entry_t             entry,
        mach_msg_type_name_t    msgt_name,
-       boolean_t               overflow,
+       __unused boolean_t      overflow,
        ipc_object_t            object)
 {
        ipc_entry_bits_t bits;
        ipc_port_t port;
-#if CONFIG_MACF_MACH
-       int rc;
-#endif
 
        bits = entry->ie_bits;
 
@@ -2468,79 +2512,53 @@ ipc_right_copyout(
 
        switch (msgt_name) {
            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);
 
-#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);
 
-               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:
                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);
 
                        assert(port->ip_srights > 1);
                        assert(urefs > 0);
-                       assert(urefs < MACH_PORT_UREFS_MAX);
-
-                       if (urefs+1 == MACH_PORT_UREFS_MAX) {
-                               if (overflow) {
-                                       /* leave urefs pegged to maximum */
+                       assert(urefs <= MACH_PORT_UREFS_MAX);
 
-                                       port->ip_srights--;
-                                       ip_unlock(port);
-                                       ip_release(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_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 */
+                       /* 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);
@@ -2554,7 +2572,7 @@ ipc_right_copyout(
                                        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;
 
@@ -2569,20 +2587,6 @@ ipc_right_copyout(
                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;
 
@@ -2619,8 +2623,7 @@ ipc_right_copyout(
                         * getting enqueued.
                         */
                        ip_lock(dest);
-                       assert(dest->ip_impcount >= assertcnt);
-                       dest->ip_impcount -= assertcnt;
+                       ipc_port_impcount_delta(dest, 0 - assertcnt, IP_NULL);
                        ip_unlock(dest);
 #endif /* IMPORTANCE_INHERITANCE */
                        ip_release(dest);
@@ -2740,9 +2743,7 @@ ipc_right_rename(
 
                ips_lock(pset);
                assert(ips_active(pset));
-               assert(pset->ips_local_name == oname);
 
-               pset->ips_local_name = nname;
                ips_unlock(pset);
                break;
            }