+/*
+ * Routine: ipc_thread_reset
+ * Purpose:
+ * Reset the IPC state for a given Mach thread when
+ * its task enters an elevated security context.
+ * Both the thread port and its exception ports have
+ * to be reset. Its RPC reply port cannot have any
+ * rights outstanding, so it should be fine.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_thread_reset(
+ thread_t thread)
+{
+ ipc_port_t old_kport, new_kport;
+ ipc_port_t old_sself;
+ ipc_port_t old_exc_actions[EXC_TYPES_COUNT];
+ boolean_t has_old_exc_actions = FALSE;
+ int i;
+
+#if CONFIG_MACF
+ struct label *new_label = mac_exc_create_label();
+#endif
+
+ new_kport = ipc_kobject_alloc_port((ipc_kobject_t)thread, IKOT_THREAD,
+ IPC_KOBJECT_ALLOC_MAKE_SEND);
+
+ thread_mtx_lock(thread);
+
+ old_kport = thread->ith_self;
+ old_sself = thread->ith_sself;
+
+ if (old_kport == IP_NULL && thread->inspection == FALSE) {
+ /* the is already terminated (can this happen?) */
+ thread_mtx_unlock(thread);
+ ipc_port_release_send(new_kport);
+ ipc_port_dealloc_kernel(new_kport);
+#if CONFIG_MACF
+ mac_exc_free_label(new_label);
+#endif
+ return;
+ }
+
+ thread->ith_sself = thread->ith_self = new_kport;
+ if (old_kport != IP_NULL) {
+ ipc_kobject_set(old_kport, IKO_NULL, IKOT_NONE);
+ }
+
+ /*
+ * Only ports that were set by root-owned processes
+ * (privileged ports) should survive
+ */
+ if (thread->exc_actions != NULL) {
+ has_old_exc_actions = TRUE;
+ for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
+ if (thread->exc_actions[i].privileged) {
+ old_exc_actions[i] = IP_NULL;
+ } else {
+#if CONFIG_MACF
+ mac_exc_update_action_label(thread->exc_actions + i, new_label);
+#endif
+ old_exc_actions[i] = thread->exc_actions[i].port;
+ thread->exc_actions[i].port = IP_NULL;
+ }
+ }
+ }
+
+ thread_mtx_unlock(thread);
+
+#if CONFIG_MACF
+ mac_exc_free_label(new_label);
+#endif
+
+ /* release the naked send rights */
+
+ if (IP_VALID(old_sself)) {
+ ipc_port_release_send(old_sself);
+ }
+
+ if (has_old_exc_actions) {
+ for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
+ ipc_port_release_send(old_exc_actions[i]);
+ }
+ }
+
+ /* destroy the kernel port */
+ if (old_kport != IP_NULL) {
+ ipc_port_dealloc_kernel(old_kport);
+ }
+
+ /* unbind the thread special reply port */
+ if (IP_VALID(thread->ith_special_reply_port)) {
+ ipc_port_unbind_special_reply_port(thread, TRUE);
+ }
+}
+