]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/kern/ipc_mig.c
xnu-6153.101.6.tar.gz
[apple/xnu.git] / osfmk / kern / ipc_mig.c
index c031c089d3d3183999d7bd3fae5543c5289236a6..6896e3793bf934969f94070948cce0d6bc702b48 100644 (file)
@@ -1,49 +1,55 @@
 /*
  * Copyright (c) 2000-2004 Apple Computer, 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_COPYRIGHT@
  */
-/* 
+/*
  * Mach Operating System
  * Copyright (c) 1991,1990 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.
  */
 #include <ipc/ipc_space.h>
 #include <ipc/ipc_port.h>
 #include <ipc/ipc_pset.h>
+#include <ipc/ipc_notify.h>
 #include <vm/vm_map.h>
 
+#include <libkern/OSAtomic.h>
+
+void
+mach_msg_receive_results_complete(ipc_object_t object);
+
 /*
  *     Routine:        mach_msg_send_from_kernel
  *     Purpose:
  *             Nothing locked.
  *     Returns:
  *             MACH_MSG_SUCCESS        Sent the message.
- *             MACH_MSG_SEND_NO_BUFFER Destination port had inuse fixed bufer
  *             MACH_SEND_INVALID_DEST  Bad destination port.
+ *             MACH_MSG_SEND_NO_BUFFER Destination port had inuse fixed bufer
+ *                                     or destination is above kernel limit
  */
 
+#if IKM_SUPPORT_LEGACY
+
+#undef mach_msg_send_from_kernel
+mach_msg_return_t mach_msg_send_from_kernel(
+       mach_msg_header_t       *msg,
+       mach_msg_size_t         send_size);
+
 mach_msg_return_t
 mach_msg_send_from_kernel(
-       mach_msg_header_t       *msg,
-       mach_msg_size_t         send_size)
+       mach_msg_header_t       *msg,
+       mach_msg_size_t         send_size)
 {
        ipc_kmsg_t kmsg;
        mach_msg_return_t mr;
 
-       if (!MACH_PORT_VALID((mach_port_name_t)msg->msgh_remote_port))
-               return MACH_SEND_INVALID_DEST;
+       KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);
 
        mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
-       if (mr != MACH_MSG_SUCCESS)
+       if (mr != MACH_MSG_SUCCESS) {
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
                return mr;
+       }
 
-       ipc_kmsg_copyin_from_kernel(kmsg);
-       ipc_kmsg_send_always(kmsg);
+       mr = ipc_kmsg_copyin_from_kernel_legacy(kmsg);
+       if (mr != MACH_MSG_SUCCESS) {
+               ipc_kmsg_free(kmsg);
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+               return mr;
+       }
 
-       return MACH_MSG_SUCCESS;
+       /*
+        * respect the thread's SEND_IMPORTANCE option to allow importance
+        * donation from the kernel-side of user threads
+        * (11938665 & 23925818)
+        */
+       mach_msg_option_t option = MACH_SEND_KERNEL_DEFAULT;
+       if (current_thread()->options & TH_OPT_SEND_IMPORTANCE) {
+               option &= ~MACH_SEND_NOIMPORTANCE;
+       }
+
+       mr = ipc_kmsg_send(kmsg, option, MACH_MSG_TIMEOUT_NONE);
+       if (mr != MACH_MSG_SUCCESS) {
+               ipc_kmsg_destroy(kmsg);
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+       }
+
+       return mr;
 }
 
+#endif /* IKM_SUPPORT_LEGACY */
+
+mach_msg_return_t
+mach_msg_send_from_kernel_proper(
+       mach_msg_header_t       *msg,
+       mach_msg_size_t         send_size)
+{
+       ipc_kmsg_t kmsg;
+       mach_msg_return_t mr;
+
+       KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);
+
+       mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
+       if (mr != MACH_MSG_SUCCESS) {
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+               return mr;
+       }
+
+       mr = ipc_kmsg_copyin_from_kernel(kmsg);
+       if (mr != MACH_MSG_SUCCESS) {
+               ipc_kmsg_free(kmsg);
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+               return mr;
+       }
+
+       /*
+        * respect the thread's SEND_IMPORTANCE option to force importance
+        * donation from the kernel-side of user threads
+        * (11938665 & 23925818)
+        */
+       mach_msg_option_t option = MACH_SEND_KERNEL_DEFAULT;
+       if (current_thread()->options & TH_OPT_SEND_IMPORTANCE) {
+               option &= ~MACH_SEND_NOIMPORTANCE;
+       }
+
+       mr = ipc_kmsg_send(kmsg, option, MACH_MSG_TIMEOUT_NONE);
+       if (mr != MACH_MSG_SUCCESS) {
+               ipc_kmsg_destroy(kmsg);
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+       }
+
+       return mr;
+}
+
+mach_msg_return_t
+mach_msg_send_from_kernel_with_options(
+       mach_msg_header_t       *msg,
+       mach_msg_size_t         send_size,
+       mach_msg_option_t       option,
+       mach_msg_timeout_t      timeout_val)
+{
+       return kernel_mach_msg_send(msg, send_size, option, timeout_val, NULL);
+}
+
+mach_msg_return_t
+kernel_mach_msg_send(
+       mach_msg_header_t       *msg,
+       mach_msg_size_t         send_size,
+       mach_msg_option_t       option,
+       mach_msg_timeout_t      timeout_val,
+       boolean_t               *message_moved)
+{
+       ipc_kmsg_t kmsg;
+       mach_msg_return_t mr;
+
+       KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);
+
+       if (message_moved) {
+               *message_moved = FALSE;
+       }
+
+       mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
+       if (mr != MACH_MSG_SUCCESS) {
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+               return mr;
+       }
+
+       mr = ipc_kmsg_copyin_from_kernel(kmsg);
+       if (mr != MACH_MSG_SUCCESS) {
+               ipc_kmsg_free(kmsg);
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+               return mr;
+       }
+
+       if (message_moved) {
+               *message_moved = TRUE;
+       }
+
+       /*
+        * Until we are sure of its effects, we are disabling
+        * importance donation from the kernel-side of user
+        * threads in importance-donating tasks - unless the
+        * option to force importance donation is passed in,
+        * or the thread's SEND_IMPORTANCE option has been set.
+        * (11938665 & 23925818)
+        */
+       if (current_thread()->options & TH_OPT_SEND_IMPORTANCE) {
+               option &= ~MACH_SEND_NOIMPORTANCE;
+       } else if ((option & MACH_SEND_IMPORTANCE) == 0) {
+               option |= MACH_SEND_NOIMPORTANCE;
+       }
+
+       mr = ipc_kmsg_send(kmsg, option, timeout_val);
+
+       if (mr != MACH_MSG_SUCCESS) {
+               ipc_kmsg_destroy(kmsg);
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+       }
+
+       return mr;
+}
+
+
+#if IKM_SUPPORT_LEGACY
+
+mach_msg_return_t
+mach_msg_send_from_kernel_with_options_legacy(
+       mach_msg_header_t       *msg,
+       mach_msg_size_t         send_size,
+       mach_msg_option_t       option,
+       mach_msg_timeout_t      timeout_val)
+{
+       ipc_kmsg_t kmsg;
+       mach_msg_return_t mr;
+
+       KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);
+
+       mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
+       if (mr != MACH_MSG_SUCCESS) {
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+               return mr;
+       }
+
+       mr = ipc_kmsg_copyin_from_kernel_legacy(kmsg);
+       if (mr != MACH_MSG_SUCCESS) {
+               ipc_kmsg_free(kmsg);
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+               return mr;
+       }
+
+       /*
+        * Until we are sure of its effects, we are disabling
+        * importance donation from the kernel-side of user
+        * threads in importance-donating tasks.
+        * (11938665 & 23925818)
+        */
+       if (current_thread()->options & TH_OPT_SEND_IMPORTANCE) {
+               option &= ~MACH_SEND_NOIMPORTANCE;
+       } else {
+               option |= MACH_SEND_NOIMPORTANCE;
+       }
+
+       mr = ipc_kmsg_send(kmsg, option, timeout_val);
+
+       if (mr != MACH_MSG_SUCCESS) {
+               ipc_kmsg_destroy(kmsg);
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+       }
+
+       return mr;
+}
+
+#endif /* IKM_SUPPORT_LEGACY */
+
 /*
  *     Routine:        mach_msg_rpc_from_kernel
  *     Purpose:
@@ -127,11 +332,44 @@ mach_msg_send_from_kernel(
  *             MACH_RCV_PORT_DIED      The reply port was deallocated.
  */
 
+#if IKM_SUPPORT_LEGACY
+
+#undef mach_msg_rpc_from_kernel
+mach_msg_return_t
+mach_msg_rpc_from_kernel(
+       mach_msg_header_t       *msg,
+       mach_msg_size_t         send_size,
+       mach_msg_size_t         rcv_size);
+
 mach_msg_return_t
 mach_msg_rpc_from_kernel(
-       mach_msg_header_t       *msg,
-       mach_msg_size_t         send_size,
-       mach_msg_size_t         rcv_size)
+       mach_msg_header_t       *msg,
+       mach_msg_size_t         send_size,
+       mach_msg_size_t         rcv_size)
+{
+       return kernel_mach_msg_rpc(msg, send_size, rcv_size, TRUE, NULL);
+}
+#endif /* IKM_SUPPORT_LEGACY */
+
+mach_msg_return_t
+mach_msg_rpc_from_kernel_proper(
+       mach_msg_header_t       *msg,
+       mach_msg_size_t         send_size,
+       mach_msg_size_t         rcv_size)
+{
+       return kernel_mach_msg_rpc(msg, send_size, rcv_size, FALSE, NULL);
+}
+
+mach_msg_return_t
+kernel_mach_msg_rpc(
+       mach_msg_header_t       *msg,
+       mach_msg_size_t         send_size,
+       mach_msg_size_t         rcv_size,
+#if !IKM_SUPPORT_LEGACY
+       __unused
+#endif
+       boolean_t           legacy,
+       boolean_t           *message_moved)
 {
        thread_t self = current_thread();
        ipc_port_t reply;
@@ -139,106 +377,249 @@ mach_msg_rpc_from_kernel(
        mach_port_seqno_t seqno;
        mach_msg_return_t mr;
 
-       assert(MACH_PORT_VALID((mach_port_name_t)msg->msgh_remote_port));
        assert(msg->msgh_local_port == MACH_PORT_NULL);
 
+       KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);
+
+       if (message_moved) {
+               *message_moved = FALSE;
+       }
+
        mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
-       if (mr != MACH_MSG_SUCCESS)
+       if (mr != MACH_MSG_SUCCESS) {
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
                return mr;
+       }
 
        reply = self->ith_rpc_reply;
        if (reply == IP_NULL) {
                reply = ipc_port_alloc_reply();
                if ((reply == IP_NULL) ||
-                   (self->ith_rpc_reply != IP_NULL))
+                   (self->ith_rpc_reply != IP_NULL)) {
                        panic("mach_msg_rpc_from_kernel");
+               }
                self->ith_rpc_reply = reply;
        }
 
        /* insert send-once right for the reply port */
        kmsg->ikm_header->msgh_local_port = reply;
        kmsg->ikm_header->msgh_bits |=
-               MACH_MSGH_BITS(0, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+           MACH_MSGH_BITS(0, MACH_MSG_TYPE_MAKE_SEND_ONCE);
 
-       ipc_port_reference(reply);
+#if IKM_SUPPORT_LEGACY
+       if (legacy) {
+               mr = ipc_kmsg_copyin_from_kernel_legacy(kmsg);
+       } else {
+               mr = ipc_kmsg_copyin_from_kernel(kmsg);
+       }
+#else
+       mr = ipc_kmsg_copyin_from_kernel(kmsg);
+#endif
+       if (mr != MACH_MSG_SUCCESS) {
+               ipc_kmsg_free(kmsg);
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+               return mr;
+       }
 
-       ipc_kmsg_copyin_from_kernel(kmsg);
+       if (message_moved) {
+               *message_moved = TRUE;
+       }
+
+       /*
+        * respect the thread's SEND_IMPORTANCE option to force importance
+        * donation from the kernel-side of user threads
+        * (11938665 & 23925818)
+        */
+       mach_msg_option_t option = MACH_SEND_KERNEL_DEFAULT;
+       if (current_thread()->options & TH_OPT_SEND_IMPORTANCE) {
+               option &= ~MACH_SEND_NOIMPORTANCE;
+       }
 
-       ipc_kmsg_send_always(kmsg);
+       mr = ipc_kmsg_send(kmsg, option, MACH_MSG_TIMEOUT_NONE);
+       if (mr != MACH_MSG_SUCCESS) {
+               ipc_kmsg_destroy(kmsg);
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
+               return mr;
+       }
 
        for (;;) {
                ipc_mqueue_t mqueue;
 
-               ip_lock(reply);
-               if ( !ip_active(reply)) {
-                       ip_unlock(reply);
-                       ipc_port_release(reply);
-                       return MACH_RCV_PORT_DIED;
-               }
-               if (!self->active) {
-                       ip_unlock(reply);
-                       ipc_port_release(reply);
+               assert(reply->ip_in_pset == 0);
+               require_ip_active(reply);
+
+               /* JMM - why this check? */
+               if (!self->active && !self->inspection) {
+                       ipc_port_dealloc_reply(reply);
+                       self->ith_rpc_reply = IP_NULL;
                        return MACH_RCV_INTERRUPTED;
                }
 
-               assert(reply->ip_pset_count == 0);
-               mqueue = &reply->ip_messages;
-               ip_unlock(reply);
-
                self->ith_continuation = (void (*)(mach_msg_return_t))0;
 
+               mqueue = &reply->ip_messages;
                ipc_mqueue_receive(mqueue,
-                                  MACH_MSG_OPTION_NONE,
-                                  MACH_MSG_SIZE_MAX,
-                                  MACH_MSG_TIMEOUT_NONE,
-                                  THREAD_INTERRUPTIBLE);
+                   MACH_MSG_OPTION_NONE,
+                   MACH_MSG_SIZE_MAX,
+                   MACH_MSG_TIMEOUT_NONE,
+                   THREAD_INTERRUPTIBLE);
 
                mr = self->ith_state;
                kmsg = self->ith_kmsg;
                seqno = self->ith_seqno;
 
-               if (mr == MACH_MSG_SUCCESS)
-                 {
+               mach_msg_receive_results_complete(ip_to_object(reply));
+
+               if (mr == MACH_MSG_SUCCESS) {
                        break;
-                 }
+               }
 
                assert(mr == MACH_RCV_INTERRUPTED);
 
-               if (self->handlers) {
-                       ipc_port_release(reply);
-                       return(mr);
+               assert(reply == self->ith_rpc_reply);
+
+               if (self->ast & AST_APC) {
+                       ipc_port_dealloc_reply(reply);
+                       self->ith_rpc_reply = IP_NULL;
+                       return mr;
                }
        }
-       ipc_port_release(reply);
 
        /*
-        * XXXXX  Set manually for now ...
-        *      No, why even bother, since the effort is wasted?
-        *
-       { mach_msg_format_0_trailer_t *trailer = (mach_msg_format_0_trailer_t *)
-               ((vm_offset_t)&kmsg->ikm_header + kmsg->ikm_header.msgh_size);
-       trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
-       trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE;
-       }
-        *****/
+        * Check to see how much of the message/trailer can be received.
+        * We chose the maximum trailer that will fit, since we don't
+        * have options telling us which trailer elements the caller needed.
+        */
+       if (rcv_size >= kmsg->ikm_header->msgh_size) {
+               mach_msg_format_0_trailer_t *trailer =  (mach_msg_format_0_trailer_t *)
+                   ((vm_offset_t)kmsg->ikm_header + kmsg->ikm_header->msgh_size);
 
-       if (rcv_size < kmsg->ikm_header->msgh_size) {
-               ipc_kmsg_copyout_dest(kmsg, ipc_space_reply);
-               ipc_kmsg_put_to_kernel(msg, kmsg, kmsg->ikm_header->msgh_size);
-               return MACH_RCV_TOO_LARGE;
+               if (rcv_size >= kmsg->ikm_header->msgh_size + MAX_TRAILER_SIZE) {
+                       /* Enough room for a maximum trailer */
+                       trailer->msgh_trailer_size = MAX_TRAILER_SIZE;
+               } else if (rcv_size < kmsg->ikm_header->msgh_size +
+                   trailer->msgh_trailer_size) {
+                       /* no room for even the basic (default) trailer */
+                       trailer->msgh_trailer_size = 0;
+               }
+               assert(trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0);
+               rcv_size = kmsg->ikm_header->msgh_size + trailer->msgh_trailer_size;
+               mr = MACH_MSG_SUCCESS;
+       } else {
+               mr = MACH_RCV_TOO_LARGE;
        }
 
+
        /*
         *      We want to preserve rights and memory in reply!
         *      We don't have to put them anywhere; just leave them
         *      as they are.
         */
-
+#if IKM_SUPPORT_LEGACY
+       if (legacy) {
+               ipc_kmsg_copyout_to_kernel_legacy(kmsg, ipc_space_reply);
+       } else {
+               ipc_kmsg_copyout_to_kernel(kmsg, ipc_space_reply);
+       }
+#else
        ipc_kmsg_copyout_to_kernel(kmsg, ipc_space_reply);
-       ipc_kmsg_put_to_kernel(msg, kmsg, kmsg->ikm_header->msgh_size);
-       return MACH_MSG_SUCCESS;
+#endif
+       ipc_kmsg_put_to_kernel(msg, kmsg, rcv_size);
+       return mr;
 }
 
+/*
+ *     Routine:        mach_msg_destroy_from_kernel_proper
+ *     Purpose:
+ *             mach_msg_destroy_from_kernel_proper is used to destroy
+ *             an unwanted/unexpected reply message from a MIG
+ *             kernel-specific user-side stub. It is like ipc_kmsg_destroy(),
+ *             except we no longer have the kmsg - just the contents.
+ */
+void
+mach_msg_destroy_from_kernel_proper(mach_msg_header_t *msg)
+{
+       mach_msg_bits_t mbits = msg->msgh_bits;
+       ipc_object_t object;
+
+       object = (ipc_object_t) msg->msgh_remote_port;
+       if (IO_VALID(object)) {
+               ipc_object_destroy(object, MACH_MSGH_BITS_REMOTE(mbits));
+       }
+
+       /*
+        * The destination (now in msg->msgh_local_port via
+        * ipc_kmsg_copyout_to_kernel) has been consumed with
+        * ipc_object_copyout_dest.
+        */
+
+       /* MIG kernel users don't receive vouchers */
+       assert(!MACH_MSGH_BITS_VOUCHER(mbits));
+
+       /* For simple messages, we're done */
+       if ((mbits & MACH_MSGH_BITS_COMPLEX) == 0) {
+               return;
+       }
+
+       /* Discard descriptor contents */
+       mach_msg_body_t *body = (mach_msg_body_t *)(msg + 1);
+       mach_msg_descriptor_t *daddr = (mach_msg_descriptor_t *)(body + 1);
+       mach_msg_size_t i;
+
+       for (i = 0; i < body->msgh_descriptor_count; i++, daddr++) {
+               switch (daddr->type.type) {
+               case MACH_MSG_PORT_DESCRIPTOR: {
+                       mach_msg_port_descriptor_t *dsc = &daddr->port;
+                       if (IO_VALID((ipc_object_t) dsc->name)) {
+                               ipc_object_destroy((ipc_object_t) dsc->name, dsc->disposition);
+                       }
+                       break;
+               }
+               case MACH_MSG_OOL_VOLATILE_DESCRIPTOR:
+               case MACH_MSG_OOL_DESCRIPTOR: {
+                       mach_msg_ool_descriptor_t *dsc =
+                           (mach_msg_ool_descriptor_t *)&daddr->out_of_line;
+
+                       if (dsc->size > 0) {
+                               vm_map_copy_discard((vm_map_copy_t) dsc->address);
+                       } else {
+                               assert(dsc->address == (void *) 0);
+                       }
+                       break;
+               }
+               case MACH_MSG_OOL_PORTS_DESCRIPTOR: {
+                       ipc_object_t                    *objects;
+                       mach_msg_type_number_t          j;
+                       mach_msg_ool_ports_descriptor_t *dsc;
+
+                       dsc = (mach_msg_ool_ports_descriptor_t  *)&daddr->ool_ports;
+                       objects = (ipc_object_t *) dsc->address;
+
+                       if (dsc->count == 0) {
+                               break;
+                       }
+                       assert(objects != 0);
+                       for (j = 0; j < dsc->count; j++) {
+                               object = objects[j];
+                               if (IO_VALID(object)) {
+                                       ipc_object_destroy(object, dsc->disposition);
+                               }
+                       }
+                       kfree(dsc->address, (vm_size_t) dsc->count * sizeof(mach_port_t));
+                       break;
+               }
+               case MACH_MSG_GUARDED_PORT_DESCRIPTOR: {
+                       mach_msg_guarded_port_descriptor_t *dsc = (mach_msg_guarded_port_descriptor_t *)&daddr->guarded_port;
+                       if (IO_VALID((ipc_object_t) dsc->name)) {
+                               ipc_object_destroy((ipc_object_t) dsc->name, dsc->disposition);
+                       }
+                       break;
+               }
+               default:
+                       break;
+               }
+       }
+}
 
 /************** These Calls are set up for kernel-loaded tasks/threads **************/
 
@@ -258,42 +639,57 @@ mach_msg_rpc_from_kernel(
 
 mach_msg_return_t
 mach_msg_overwrite(
-       mach_msg_header_t               *msg,
-       mach_msg_option_t               option,
-       mach_msg_size_t         send_size,
-       mach_msg_size_t         rcv_size,
-       mach_port_name_t                rcv_name,
-       __unused mach_msg_timeout_t     msg_timeout,
-       __unused mach_port_name_t       notify,
-       __unused mach_msg_header_t      *rcv_msg,
-       __unused mach_msg_size_t        rcv_msg_size)
+       mach_msg_header_t               *msg,
+       mach_msg_option_t               option,
+       mach_msg_size_t         send_size,
+       mach_msg_size_t         rcv_size,
+       mach_port_name_t                rcv_name,
+       __unused mach_msg_timeout_t     msg_timeout,
+       mach_msg_priority_t     override,
+       __unused mach_msg_header_t      *rcv_msg,
+       __unused mach_msg_size_t rcv_msg_size)
 {
        ipc_space_t space = current_space();
        vm_map_t map = current_map();
        ipc_kmsg_t kmsg;
        mach_port_seqno_t seqno;
        mach_msg_return_t mr;
-       mach_msg_format_0_trailer_t *trailer;
+       mach_msg_trailer_size_t trailer_size;
 
        if (option & MACH_SEND_MSG) {
-               mach_msg_size_t msg_and_trailer_size;
-               mach_msg_max_trailer_t  *max_trailer;
+               mach_msg_size_t msg_and_trailer_size;
+               mach_msg_max_trailer_t  *max_trailer;
 
-               if ((send_size < sizeof(mach_msg_header_t)) || (send_size & 3))
+               if ((send_size & 3) ||
+                   send_size < sizeof(mach_msg_header_t) ||
+                   (send_size < sizeof(mach_msg_base_t) && (msg->msgh_bits & MACH_MSGH_BITS_COMPLEX))) {
                        return MACH_SEND_MSG_TOO_SMALL;
+               }
 
-               msg_and_trailer_size = send_size + MAX_TRAILER_SIZE;
+               if (send_size > MACH_MSG_SIZE_MAX - MAX_TRAILER_SIZE) {
+                       return MACH_SEND_TOO_LARGE;
+               }
+
+               KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);
 
+               msg_and_trailer_size = send_size + MAX_TRAILER_SIZE;
                kmsg = ipc_kmsg_alloc(msg_and_trailer_size);
 
-               if (kmsg == IKM_NULL)
+               if (kmsg == IKM_NULL) {
+                       KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, MACH_SEND_NO_BUFFER);
                        return MACH_SEND_NO_BUFFER;
+               }
 
+               KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_LINK) | DBG_FUNC_NONE,
+                   (uintptr_t)0,                   /* this should only be called from the kernel! */
+                   VM_KERNEL_ADDRPERM((uintptr_t)kmsg),
+                   0, 0,
+                   0);
                (void) memcpy((void *) kmsg->ikm_header, (const void *) msg, send_size);
 
                kmsg->ikm_header->msgh_size = send_size;
 
-               /* 
+               /*
                 * Reserve for the trailer the largest space (MAX_TRAILER_SIZE)
                 * However, the internal size field of the trailer (msgh_trailer_size)
                 * is initialized to the minimum (sizeof(mach_msg_trailer_t)), to optimize
@@ -304,17 +700,19 @@ mach_msg_overwrite(
                max_trailer->msgh_audit = current_thread()->task->audit_token;
                max_trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
                max_trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE;
-       
-               mr = ipc_kmsg_copyin(kmsg, space, map, MACH_PORT_NULL);
+
+               mr = ipc_kmsg_copyin(kmsg, space, map, override, &option);
+
                if (mr != MACH_MSG_SUCCESS) {
                        ipc_kmsg_free(kmsg);
+                       KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
                        return mr;
                }
 
-               do
-                       mr = ipc_kmsg_send(kmsg, MACH_MSG_OPTION_NONE,
-                                            MACH_MSG_TIMEOUT_NONE);
-               while (mr == MACH_SEND_INTERRUPTED);
+               do {
+                       mr = ipc_kmsg_send(kmsg, MACH_MSG_OPTION_NONE, MACH_MSG_TIMEOUT_NONE);
+               } while (mr == MACH_SEND_INTERRUPTED);
+
                assert(mr == MACH_MSG_SUCCESS);
        }
 
@@ -326,47 +724,46 @@ mach_msg_overwrite(
                        ipc_mqueue_t mqueue;
 
                        mr = ipc_mqueue_copyin(space, rcv_name,
-                                              &mqueue, &object);
-                       if (mr != MACH_MSG_SUCCESS)
+                           &mqueue, &object);
+                       if (mr != MACH_MSG_SUCCESS) {
                                return mr;
+                       }
+
                        /* hold ref for object */
 
                        self->ith_continuation = (void (*)(mach_msg_return_t))0;
                        ipc_mqueue_receive(mqueue,
-                                          MACH_MSG_OPTION_NONE,
-                                          MACH_MSG_SIZE_MAX,
-                                          MACH_MSG_TIMEOUT_NONE,
-                                          THREAD_ABORTSAFE);
+                           MACH_MSG_OPTION_NONE,
+                           MACH_MSG_SIZE_MAX,
+                           MACH_MSG_TIMEOUT_NONE,
+                           THREAD_ABORTSAFE);
                        mr = self->ith_state;
                        kmsg = self->ith_kmsg;
                        seqno = self->ith_seqno;
 
-                       ipc_object_release(object);
-
+                       mach_msg_receive_results_complete(object);
+                       io_release(object);
                } while (mr == MACH_RCV_INTERRUPTED);
-               if (mr != MACH_MSG_SUCCESS)
-                       return mr;
 
-               trailer = (mach_msg_format_0_trailer_t *) 
-                   ((vm_offset_t)kmsg->ikm_header + kmsg->ikm_header->msgh_size);
-               if (option & MACH_RCV_TRAILER_MASK) {
-                       trailer->msgh_seqno = seqno;
-                       trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option);
+               if (mr != MACH_MSG_SUCCESS) {
+                       return mr;
                }
 
-               if (rcv_size < (kmsg->ikm_header->msgh_size + trailer->msgh_trailer_size)) {
+               trailer_size = ipc_kmsg_add_trailer(kmsg, space, option, current_thread(), seqno, TRUE,
+                   kmsg->ikm_header->msgh_remote_port->ip_context);
+
+               if (rcv_size < (kmsg->ikm_header->msgh_size + trailer_size)) {
                        ipc_kmsg_copyout_dest(kmsg, space);
                        (void) memcpy((void *) msg, (const void *) kmsg->ikm_header, sizeof *msg);
                        ipc_kmsg_free(kmsg);
                        return MACH_RCV_TOO_LARGE;
                }
 
-               mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL,
-                                     MACH_MSG_BODY_NULL);
+               mr = ipc_kmsg_copyout(kmsg, space, map, MACH_MSG_BODY_NULL, option);
                if (mr != MACH_MSG_SUCCESS) {
-                       if ((mr &MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
+                       if ((mr & ~MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
                                ipc_kmsg_put_to_kernel(msg, kmsg,
-                                               kmsg->ikm_header->msgh_size + trailer->msgh_trailer_size);
+                                   kmsg->ikm_header->msgh_size + trailer_size);
                        } else {
                                ipc_kmsg_copyout_dest(kmsg, space);
                                (void) memcpy((void *) msg, (const void *) kmsg->ikm_header, sizeof *msg);
@@ -377,7 +774,7 @@ mach_msg_overwrite(
                }
 
                (void) memcpy((void *) msg, (const void *) kmsg->ikm_header,
-                             kmsg->ikm_header->msgh_size + trailer->msgh_trailer_size);
+                   kmsg->ikm_header->msgh_size + trailer_size);
                ipc_kmsg_free(kmsg);
        }
 
@@ -393,7 +790,7 @@ mach_msg_overwrite(
 mach_port_t
 mig_get_reply_port(void)
 {
-       return (MACH_PORT_NULL);
+       return MACH_PORT_NULL;
 }
 
 /*
@@ -406,13 +803,12 @@ void
 mig_dealloc_reply_port(
        __unused mach_port_t reply_port)
 {
-       panic("mig_dealloc_reply_port");
 }
 
 /*
  *     Routine:        mig_put_reply_port
  *     Purpose:
- *             Called by client side interfaces after each RPC to 
+ *             Called by client side interfaces after each RPC to
  *             let the client recycle the reply port if it wishes.
  */
 void
@@ -425,48 +821,105 @@ mig_put_reply_port(
  * mig_strncpy.c - by Joshua Block
  *
  * mig_strncp -- Bounded string copy.  Does what the library routine strncpy
- * OUGHT to do:  Copies the (null terminated) string in src into dest, a 
+ * OUGHT to do:  Copies the (null terminated) string in src into dest, a
  * buffer of length len.  Assures that the copy is still null terminated
  * and doesn't overflow the buffer, truncating the copy if necessary.
  *
  * Parameters:
- * 
+ *
  *     dest - Pointer to destination buffer.
- * 
+ *
  *     src - Pointer to source string.
- * 
+ *
  *     len - Length of destination buffer.
  */
-int 
+int
 mig_strncpy(
-       char            *dest,
-       const char      *src,
-       int             len)
+       char            *dest,
+       const char      *src,
+       int             len)
+{
+       int i = 0;
+
+       if (len > 0) {
+               if (dest != NULL) {
+                       if (src != NULL) {
+                               for (i = 1; i < len; i++) {
+                                       if (!(*dest++ = *src++)) {
+                                               return i;
+                                       }
+                               }
+                       }
+                       *dest = '\0';
+               }
+       }
+       return i;
+}
+
+/*
+ * mig_strncpy_zerofill -- Bounded string copy.  Does what the
+ * library routine strncpy OUGHT to do:  Copies the (null terminated)
+ * string in src into dest, a buffer of length len.  Assures that
+ * the copy is still null terminated and doesn't overflow the buffer,
+ * truncating the copy if necessary. If the string in src is smaller
+ * than given length len, it will zero fill the remaining bytes in dest.
+ *
+ * Parameters:
+ *
+ *     dest - Pointer to destination buffer.
+ *
+ *     src - Pointer to source string.
+ *
+ *     len - Length of destination buffer.
+ */
+int
+mig_strncpy_zerofill(
+       char            *dest,
+       const char      *src,
+       int             len)
 {
-    int i = 0;
+       int i = 0;
+       boolean_t terminated = FALSE;
+       int retval = 0;
+
+       if (len <= 0 || dest == NULL) {
+               return 0;
+       }
 
-    if (len > 0)
-       if (dest != NULL) {
-           if (src != NULL)
-                  for (i=1; i<len; i++)
-                       if (! (*dest++ = *src++))
-                           return i;
-               *dest = '\0';
+       if (src == NULL) {
+               terminated = TRUE;
        }
-    return i;
+
+       for (i = 1; i < len; i++) {
+               if (!terminated) {
+                       if (!(*dest++ = *src++)) {
+                               retval = i;
+                               terminated = TRUE;
+                       }
+               } else {
+                       *dest++ = '\0';
+               }
+       }
+
+       *dest = '\0';
+       if (!terminated) {
+               retval = i;
+       }
+
+       return retval;
 }
 
-char *
+void *
 mig_user_allocate(
-       vm_size_t       size)
+       vm_size_t       size)
 {
        return (char *)kalloc(size);
 }
 
 void
 mig_user_deallocate(
-       char            *data,
-       vm_size_t       size)
+       char            *data,
+       vm_size_t       size)
 {
        kfree(data, size);
 }
@@ -479,11 +932,12 @@ mig_user_deallocate(
  */
 kern_return_t
 mig_object_init(
-       mig_object_t            mig_object,
-       const IMIGObject        *interface)
+       mig_object_t            mig_object,
+       const IMIGObject        *interface)
 {
-       if (mig_object == MIG_OBJECT_NULL)
+       if (mig_object == MIG_OBJECT_NULL) {
                return KERN_INVALID_ARGUMENT;
+       }
        mig_object->pVtbl = (const IMIGObjectVtbl *)interface;
        mig_object->port = MACH_PORT_NULL;
        return KERN_SUCCESS;
@@ -502,7 +956,7 @@ mig_object_init(
  */
 void
 mig_object_destroy(
-       __assert_only mig_object_t      mig_object)
+       __assert_only mig_object_t      mig_object)
 {
        assert(mig_object->port == MACH_PORT_NULL);
        return;
@@ -518,7 +972,7 @@ mig_object_destroy(
  */
 void
 mig_object_reference(
-       mig_object_t    mig_object)
+       mig_object_t    mig_object)
 {
        assert(mig_object != MIG_OBJECT_NULL);
        mig_object->pVtbl->AddRef((IMIGObject *)mig_object);
@@ -534,10 +988,16 @@ mig_object_reference(
  */
 void
 mig_object_deallocate(
-       mig_object_t    mig_object)
+       mig_object_t    mig_object)
 {
        assert(mig_object != MIG_OBJECT_NULL);
-       mig_object->pVtbl->Release((IMIGObject *)mig_object);
+       ipc_port_t port = mig_object->port;
+       if (mig_object->pVtbl->Release((IMIGObject *)mig_object) == 0) {
+               if (IP_VALID(port)) {
+                       assert(!port->ip_srights);
+                       ipc_port_dealloc_kernel(port);
+               }
+       }
 }
 
 /*
@@ -554,56 +1014,22 @@ mig_object_deallocate(
  */
 ipc_port_t
 convert_mig_object_to_port(
-       mig_object_t    mig_object)
+       mig_object_t    mig_object)
 {
-       ipc_port_t      port;
-       boolean_t       deallocate = TRUE;
-
-       if (mig_object == MIG_OBJECT_NULL)
+       if (mig_object == MIG_OBJECT_NULL) {
                return IP_NULL;
-
-       port = mig_object->port;
-       while ((port == IP_NULL) ||
-              ((port = ipc_port_make_send(port)) == IP_NULL)) {
-               ipc_port_t      previous;
-
-               /*
-                * Either the port was never set up, or it was just
-                * deallocated out from under us by the no-senders
-                * processing.  In either case, we must:
-                *      Attempt to make one
-                *      Arrange for no senders
-                *      Try to atomically register it with the object
-                *              Destroy it if we are raced.
-                */
-               port = ipc_port_alloc_kernel();
-               ip_lock(port);
-               ipc_kobject_set_atomically(port,
-                                          (ipc_kobject_t) mig_object,
-                                          IKOT_MIG);
-
-               /* make a sonce right for the notification */
-               port->ip_sorights++;
-               ip_reference(port);
-
-               ipc_port_nsrequest(port, 1, port, &previous);
-               /* port unlocked */
-
-               assert(previous == IP_NULL);
-
-               if (hw_compare_and_store((uint32_t)IP_NULL, (uint32_t)port,
-                                                                                       (uint32_t *)&mig_object->port)) {
-                       deallocate = FALSE;
-               } else {
-                       ipc_port_dealloc_kernel(port);
-                       port = mig_object->port;
-               }
        }
 
-       if (deallocate)
-               mig_object->pVtbl->Release((IMIGObject *)mig_object);
+       /*
+        * make a send right and donate our reference for mig_object_no_senders
+        * if this is the first send right
+        */
+       if (!ipc_kobject_make_send_lazy_alloc_port(&mig_object->port,
+           (ipc_kobject_t) mig_object, IKOT_MIG)) {
+               mig_object_deallocate(mig_object);
+       }
 
-       return (port);
+       return mig_object->port;
 }
 
 
@@ -622,14 +1048,15 @@ convert_mig_object_to_port(
  */
 mig_object_t
 convert_port_to_mig_object(
-       ipc_port_t      port,
-       const MIGIID    *iid)
+       ipc_port_t      port,
+       const MIGIID    *iid)
 {
-       mig_object_t    mig_object;
-       void            *ppv;
+       mig_object_t    mig_object;
+       void            *ppv;
 
-       if (!IP_VALID(port))
+       if (!IP_VALID(port)) {
                return NULL;
+       }
 
        ip_lock(port);
        if (!ip_active(port) || (ip_kotype(port) != IKOT_MIG)) {
@@ -642,7 +1069,7 @@ convert_port_to_mig_object(
         * query it to get a reference to the desired interface.
         */
        ppv = NULL;
-       mig_object = (mig_object_t)port->ip_kobject;
+       mig_object = (mig_object_t) ip_get_kobject(port);
        mig_object->pVtbl->QueryInterface((IMIGObject *)mig_object, iid, &ppv);
        ip_unlock(port);
        return (mig_object_t)ppv;
@@ -654,60 +1081,19 @@ convert_port_to_mig_object(
  *             Base implementation of a no-senders notification handler
  *             for MIG objects. If there truly are no more senders, must
  *             destroy the port and drop its reference on the object.
- *     Returns:
- *             TRUE  - port deallocate and reference dropped
- *             FALSE - more senders arrived, re-registered for notification
  *     Conditions:
  *             Nothing locked.
  */
-
-boolean_t
+void
 mig_object_no_senders(
-       ipc_port_t              port,
-       mach_port_mscount_t     mscount)
+       ipc_port_t              port)
 {
-       mig_object_t            mig_object;
-
-       ip_lock(port);
-       if (port->ip_mscount > mscount) {
-               ipc_port_t      previous;
-
-               /*
-                * Somebody created new send rights while the
-                * notification was in-flight.  Just create a
-                * new send-once right and re-register with 
-                * the new (higher) mscount threshold.
-                */
-               /* make a sonce right for the notification */
-               port->ip_sorights++;
-               ip_reference(port);
-               ipc_port_nsrequest(port, mscount, port, &previous);
-               /* port unlocked */
-
-               assert(previous == IP_NULL);
-               return (FALSE);
-       }
+       require_ip_active(port);
+       assert(IKOT_MIG == ip_kotype(port));
 
-       /*
-        * Clear the port pointer while we have it locked.
-        */
-       mig_object = (mig_object_t)port->ip_kobject;
-       mig_object->port = IP_NULL;
-
-       /*
-        * Bring the sequence number and mscount in
-        * line with ipc_port_destroy assertion.
-        */
-       port->ip_mscount = 0;
-       port->ip_messages.imq_seqno = 0;
-       ipc_port_destroy(port); /* releases lock */
-       
-       /*
-        * Release the port's reference on the object.
-        */
-       mig_object->pVtbl->Release((IMIGObject *)mig_object);
-       return (TRUE);
-}      
+       /* consume the reference donated by convert_mig_object_to_port */
+       mig_object_deallocate((mig_object_t) ip_get_kobject(port));
+}
 
 /*
  * Kernel implementation of the notification chain for MIG object