+ kern_return_t __assert_only kr;
+ uint32_t persona_id = 0;
+ ipc_voucher_t voucher;
+
+ ip_lock_held(reply_port);
+
+ if (!ip_active(reply_port)) {
+ return;
+ }
+
+ voucher = convert_port_to_voucher(voucher_port);
+
+ kr = bank_get_bank_ledger_thread_group_and_persona(voucher, NULL, NULL, &persona_id);
+ assert(kr == KERN_SUCCESS);
+ ipc_voucher_release(voucher);
+
+ if (persona_id == 0 || persona_id == PERSONA_ID_NONE) {
+ /* there was no persona context to record */
+ return;
+ }
+
+ /*
+ * Set the persona_id as the context on the reply port.
+ * This will force the thread that replies to have adopted a voucher
+ * with a matching persona.
+ */
+ reply_port->ip_reply_context = persona_id;
+
+ return;
+}
+
+static kern_return_t
+ipc_kmsg_validate_reply_port_locked(ipc_port_t reply_port, mach_msg_option_t options)
+{
+ ip_lock_held(reply_port);
+
+ if (!ip_active(reply_port)) {
+ /*
+ * Ideally, we would enforce that the reply receive right is
+ * active, but asynchronous XPC cancellation destroys the
+ * receive right, so we just have to return success here.
+ */
+ return KERN_SUCCESS;
+ }
+
+ if (options & MACH_SEND_MSG) {
+ /*
+ * If the rely port is active, then it should not be
+ * in-transit, and the receive right should be in the caller's
+ * IPC space.
+ */
+ if (!reply_port->ip_receiver_name || reply_port->ip_receiver != current_task()->itk_space) {
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ /*
+ * A port used as a reply port in an RPC should have exactly 1
+ * extant send-once right which we either just made or are
+ * moving as part of the IPC.
+ */
+ if (reply_port->ip_sorights != 1) {
+ return KERN_INVALID_CAPABILITY;
+ }
+ /*
+ * XPC uses an extra send-right to keep the name of the reply
+ * right around through cancellation. That makes it harder to
+ * enforce a particular semantic kere, so for now, we say that
+ * you can have a maximum of 1 send right (in addition to your
+ * send once right). In the future, it would be great to lock
+ * this down even further.
+ */
+ if (reply_port->ip_srights > 1) {
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ /*
+ * The sender can also specify that the receive right should
+ * be immovable. Note that this check only applies to
+ * send-only operations. Combined send/receive or rcv-only
+ * operations can specify an immovable receive right by
+ * opt-ing into guarded descriptors (MACH_RCV_GUARDED_DESC)
+ * and using the MACH_MSG_STRICT_REPLY options flag.
+ */
+ if (MACH_SEND_REPLY_IS_IMMOVABLE(options)) {
+ if (!reply_port->ip_immovable_receive) {
+ return KERN_INVALID_CAPABILITY;
+ }
+ }
+ }
+
+ /*
+ * don't enforce this yet: need a better way of indicating the
+ * receiver wants this...
+ */
+#if 0
+ if (MACH_RCV_WITH_IMMOVABLE_REPLY(options)) {
+ if (!reply_port->ip_immovable_receive) {
+ return KERN_INVALID_CAPABILITY;
+ }
+ }
+#endif /* 0 */
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_kmsg_validate_reply_context_locked
+ * Purpose:
+ * Validate that the current thread is running in the context
+ * required by the destination port.
+ * Conditions:
+ * dest_port is locked
+ * Returns:
+ * MACH_MSG_SUCCESS on success.
+ * On error, an EXC_GUARD exception is also raised.
+ * This function *always* resets the port reply context.
+ */
+static mach_msg_return_t
+ipc_kmsg_validate_reply_context_locked(
+ mach_msg_option_t option,
+ ipc_port_t dest_port,
+ ipc_voucher_t voucher,
+ mach_port_name_t voucher_name)
+{
+ uint32_t dest_ctx = dest_port->ip_reply_context;
+ dest_port->ip_reply_context = 0;
+
+ if (!ip_active(dest_port)) {
+ return MACH_MSG_SUCCESS;
+ }
+
+ if (voucher == IPC_VOUCHER_NULL || !MACH_PORT_VALID(voucher_name)) {
+ if ((option & MACH_SEND_KERNEL) == 0) {
+ mach_port_guard_exception(voucher_name, 0,
+ (MPG_FLAGS_STRICT_REPLY_INVALID_VOUCHER | dest_ctx),
+ kGUARD_EXC_STRICT_REPLY);
+ }
+ return MACH_SEND_INVALID_CONTEXT;
+ }
+
+ kern_return_t __assert_only kr;
+ uint32_t persona_id = 0;
+ kr = bank_get_bank_ledger_thread_group_and_persona(voucher, NULL, NULL, &persona_id);
+ assert(kr == KERN_SUCCESS);
+
+ if (dest_ctx != persona_id) {
+ if ((option & MACH_SEND_KERNEL) == 0) {
+ mach_port_guard_exception(voucher_name, 0,
+ (MPG_FLAGS_STRICT_REPLY_MISMATCHED_PERSONA | ((((uint64_t)persona_id << 32) & MPG_FLAGS_STRICT_REPLY_MASK) | dest_ctx)),
+ kGUARD_EXC_STRICT_REPLY);
+ }
+ return MACH_SEND_INVALID_CONTEXT;
+ }
+
+ return MACH_MSG_SUCCESS;
+}
+
+/*
+ * Routine: ipc_kmsg_copyin_header
+ * Purpose:
+ * "Copy-in" port rights in the header of a message.
+ * Operates atomically; if it doesn't succeed the
+ * message header and the space are left untouched.
+ * If it does succeed the remote/local port fields
+ * contain object pointers instead of port names,
+ * and the bits field is updated. The destination port
+ * will be a valid port pointer.
+ *
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Successful copyin.
+ * MACH_SEND_INVALID_HEADER
+ * Illegal value in the message header bits.
+ * MACH_SEND_INVALID_DEST The space is dead.
+ * MACH_SEND_INVALID_DEST Can't copyin destination port.
+ * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.)
+ * MACH_SEND_INVALID_REPLY Can't copyin reply port.
+ * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.)
+ */
+
+mach_msg_return_t
+ipc_kmsg_copyin_header(
+ ipc_kmsg_t kmsg,
+ ipc_space_t space,
+ mach_msg_priority_t override,
+ mach_msg_option_t *optionp)
+{
+ mach_msg_header_t *msg = kmsg->ikm_header;