+ mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits);
+ mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits);
+ mach_msg_type_name_t voucher_type = MACH_MSGH_BITS_VOUCHER(mbits);
+ ipc_object_t dest_port = IO_NULL;
+ ipc_object_t reply_port = IO_NULL;
+ ipc_port_t dest_soright = IP_NULL;
+ ipc_port_t reply_soright = IP_NULL;
+ ipc_port_t voucher_soright = IP_NULL;
+ ipc_port_t release_port = IP_NULL;
+ ipc_port_t voucher_port = IP_NULL;
+ ipc_port_t voucher_release_port = IP_NULL;
+ ipc_entry_t dest_entry = IE_NULL;
+ ipc_entry_t reply_entry = IE_NULL;
+ ipc_entry_t voucher_entry = IE_NULL;
+
+ int assertcnt = 0;
+#if IMPORTANCE_INHERITANCE
+ boolean_t needboost = FALSE;
+#endif /* IMPORTANCE_INHERITANCE */
+
+ if ((mbits != msg->msgh_bits) ||
+ (!MACH_MSG_TYPE_PORT_ANY_SEND(dest_type)) ||
+ ((reply_type == 0) ?
+ (reply_name != MACH_PORT_NULL) :
+ !MACH_MSG_TYPE_PORT_ANY_SEND(reply_type)))
+ return MACH_SEND_INVALID_HEADER;
+
+ if (!MACH_PORT_VALID(dest_name))
+ return MACH_SEND_INVALID_DEST;
+
+ is_write_lock(space);
+ if (!is_active(space)) {
+ is_write_unlock(space);
+ return MACH_SEND_INVALID_DEST;
+ }
+ /* space locked and active */
+
+ /*
+ * If there is a voucher specified, make sure the disposition is
+ * valid and the entry actually refers to a voucher port. Don't
+ * actually copy in until we validate destination and reply.
+ */
+ if (voucher_type != MACH_MSGH_BITS_ZERO) {
+
+ voucher_name = msg->msgh_voucher_port;
+
+ if (voucher_name == MACH_PORT_DEAD ||
+ (voucher_type != MACH_MSG_TYPE_MOVE_SEND &&
+ voucher_type != MACH_MSG_TYPE_COPY_SEND)) {
+ is_write_unlock(space);
+ if ((*optionp & MACH_SEND_KERNEL) == 0) {
+ mach_port_guard_exception(voucher_name, 0, 0, kGUARD_EXC_SEND_INVALID_VOUCHER);
+ }
+ return MACH_SEND_INVALID_VOUCHER;
+ }
+
+ if (voucher_name != MACH_PORT_NULL) {
+ voucher_entry = ipc_entry_lookup(space, voucher_name);
+ if (voucher_entry == IE_NULL ||
+ (voucher_entry->ie_bits & MACH_PORT_TYPE_SEND) == 0 ||
+ io_kotype(voucher_entry->ie_object) != IKOT_VOUCHER) {
+ is_write_unlock(space);
+ if ((*optionp & MACH_SEND_KERNEL) == 0) {
+ mach_port_guard_exception(voucher_name, 0, 0, kGUARD_EXC_SEND_INVALID_VOUCHER);
+ }
+ return MACH_SEND_INVALID_VOUCHER;
+ }
+ } else {
+ voucher_type = MACH_MSG_TYPE_MOVE_SEND;
+ }
+ }
+
+ /*
+ * Handle combinations of validating destination and reply; along
+ * with copying in destination, reply, and voucher in an atomic way.
+ */
+
+ if (dest_name == voucher_name) {
+
+ /*
+ * If the destination name is the same as the voucher name,
+ * the voucher_entry must already be known. Either that or
+ * the destination name is MACH_PORT_NULL (i.e. invalid).
+ */
+ dest_entry = voucher_entry;
+ if (dest_entry == IE_NULL) {
+ goto invalid_dest;
+ }
+
+ /*
+ * Make sure a future copyin of the reply port will succeed.
+ * Once we start copying in the dest/voucher pair, we can't
+ * back out.
+ */
+ if (MACH_PORT_VALID(reply_name)) {
+ assert(reply_type != 0); /* because reply_name not null */
+
+ /* It is just WRONG if dest, voucher, and reply are all the same. */
+ if (voucher_name == reply_name) {
+ goto invalid_reply;
+ }
+ reply_entry = ipc_entry_lookup(space, reply_name);
+ if (reply_entry == IE_NULL) {
+ goto invalid_reply;
+ }
+ assert(dest_entry != reply_entry); /* names are not equal */
+ if (!ipc_right_copyin_check(space, reply_name, reply_entry, reply_type)) {
+ goto invalid_reply;
+ }
+ }
+
+ /*
+ * Do the joint copyin of the dest disposition and
+ * voucher disposition from the one entry/port. We
+ * already validated that the voucher copyin would
+ * succeed (above). So, any failure in combining
+ * the copyins can be blamed on the destination.
+ */
+ kr = ipc_right_copyin_two(space, dest_name, dest_entry,
+ dest_type, voucher_type,
+ &dest_port, &dest_soright,
+ &release_port);
+ if (kr != KERN_SUCCESS) {
+ assert(kr != KERN_INVALID_CAPABILITY);
+ goto invalid_dest;
+ }
+ voucher_port = (ipc_port_t)dest_port;
+
+ /*
+ * could not have been one of these dispositions,
+ * validated the port was a true kernel voucher port above,
+ * AND was successfully able to copyin both dest and voucher.
+ */
+ assert(dest_type != MACH_MSG_TYPE_MAKE_SEND);
+ assert(dest_type != MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ assert(dest_type != MACH_MSG_TYPE_MOVE_SEND_ONCE);
+
+ /*
+ * Perform the delayed reply right copyin (guaranteed success).
+ */
+ if (reply_entry != IE_NULL) {
+ kr = ipc_right_copyin(space, reply_name, reply_entry,
+ reply_type, TRUE,
+ &reply_port, &reply_soright,
+ &release_port, &assertcnt);
+ assert(assertcnt == 0);
+ assert(kr == KERN_SUCCESS);
+ }
+
+ } else {
+ if (dest_name == reply_name) {
+ /*
+ * Destination and reply ports are the same!
+ * This is very similar to the case where the
+ * destination and voucher ports were the same
+ * (except the reply port disposition is not
+ * previously validated).
+ */
+ dest_entry = ipc_entry_lookup(space, dest_name);
+ if (dest_entry == IE_NULL) {
+ goto invalid_dest;
+ }
+ reply_entry = dest_entry;
+ assert(reply_type != 0); /* because name not null */
+
+ /*
+ * Do the joint copyin of the dest disposition and
+ * reply disposition from the one entry/port.
+ */
+ kr = ipc_right_copyin_two(space, dest_name, dest_entry,
+ dest_type, reply_type,
+ &dest_port, &dest_soright,
+ &release_port);
+ if (kr == KERN_INVALID_CAPABILITY) {
+ goto invalid_reply;
+ } else if (kr != KERN_SUCCESS) {
+ goto invalid_dest;
+ }
+ reply_port = dest_port;
+
+
+ } else {
+ /*
+ * Handle destination and reply independently, as
+ * they are independent entries (even if the entries
+ * refer to the same port).
+ *
+ * This can be the tough case to make atomic.
+ *
+ * The difficult problem is serializing with port death.
+ * The bad case is when dest_port dies after its copyin,
+ * reply_port dies before its copyin, and dest_port dies before
+ * reply_port. Then the copyins operated as if dest_port was
+ * alive and reply_port was dead, which shouldn't have happened
+ * because they died in the other order.
+ *
+ * Note that it is easy for a user task to tell if
+ * a copyin happened before or after a port died.
+ * If a port dies before copyin, a dead-name notification
+ * is generated and the dead name's urefs are incremented,
+ * and if the copyin happens first, a port-deleted
+ * notification is generated.
+ *
+ * Even so, avoiding that potentially detectable race is too
+ * expensive - and no known code cares about it. So, we just
+ * do the expedient thing and copy them in one after the other.
+ */
+
+ dest_entry = ipc_entry_lookup(space, dest_name);
+ if (dest_entry == IE_NULL) {
+ goto invalid_dest;
+ }
+ assert(dest_entry != voucher_entry);
+
+ /*
+ * Make sure reply port entry is valid before dest copyin.
+ */
+ if (MACH_PORT_VALID(reply_name)) {
+ if (reply_name == voucher_name) {
+ goto invalid_reply;
+ }
+ reply_entry = ipc_entry_lookup(space, reply_name);
+ if (reply_entry == IE_NULL) {
+ goto invalid_reply;
+ }
+ assert(dest_entry != reply_entry); /* names are not equal */
+ assert(reply_type != 0); /* because reply_name not null */
+
+ if (!ipc_right_copyin_check(space, reply_name, reply_entry, reply_type)) {
+ goto invalid_reply;
+ }
+ }
+
+ /*
+ * copyin the destination.
+ */
+ kr = ipc_right_copyin(space, dest_name, dest_entry,
+ dest_type, FALSE,
+ &dest_port, &dest_soright,
+ &release_port, &assertcnt);
+ assert(assertcnt == 0);
+ if (kr != KERN_SUCCESS) {
+ goto invalid_dest;
+ }
+ assert(IO_VALID(dest_port));
+ assert(!IP_VALID(release_port));
+
+ /*
+ * Copyin the pre-validated reply right.
+ * It's OK if the reply right has gone dead in the meantime.
+ */
+ if (MACH_PORT_VALID(reply_name)) {
+ kr = ipc_right_copyin(space, reply_name, reply_entry,
+ reply_type, TRUE,
+ &reply_port, &reply_soright,
+ &release_port, &assertcnt);
+ assert(assertcnt == 0);
+ assert(kr == KERN_SUCCESS);
+ } else {
+ /* convert invalid name to equivalent ipc_object type */
+ reply_port = (ipc_object_t)CAST_MACH_NAME_TO_PORT(reply_name);
+ }
+ }
+
+ /*
+ * Finally can copyin the voucher right now that dest and reply
+ * are fully copied in (guaranteed success).
+ */
+ if (IE_NULL != voucher_entry) {
+ kr = ipc_right_copyin(space, voucher_name, voucher_entry,
+ voucher_type, FALSE,
+ (ipc_object_t *)&voucher_port,
+ &voucher_soright,
+ &voucher_release_port,
+ &assertcnt);
+ assert(assertcnt == 0);
+ assert(KERN_SUCCESS == kr);
+ assert(IP_VALID(voucher_port));
+ assert(ip_active(voucher_port));
+ }
+ }
+
+ /*
+ * The entries might need to be deallocated.
+ *
+ * Each entry should be deallocated only once,
+ * even if it was specified in more than one slot in the header.
+ * Note that dest can be the same entry as reply or voucher,
+ * but reply and voucher must be distinct entries.
+ */
+ assert(IE_NULL != dest_entry);
+ if (IE_NULL != reply_entry)
+ assert(reply_entry != voucher_entry);
+
+ if (IE_BITS_TYPE(dest_entry->ie_bits) == MACH_PORT_TYPE_NONE) {
+ ipc_entry_dealloc(space, dest_name, dest_entry);