1 #include <darwintest.h>
2 #include <darwintest_multiprocess.h>
3 #include <mach-o/dyld.h>
4 #include <spawn.h>
5 #include <unistd.h>
6 #include <sys/wait.h>
7 #include <mach/exception_types.h>
8 #include <mach/mach_port.h>
9 #include <mach/mach.h>
10 #include <mach/mach_interface.h>
11 #include <unistd.h>
12 #include "excserver_protect_state.h"
13 #include <TargetConditionals.h>
14
15 T_GLOBAL_META(
16 T_META_NAMESPACE("xnu.ipc"),
17 T_META_RADAR_COMPONENT_NAME("xnu"),
18 T_META_RADAR_COMPONENT_VERSION("IPC"),
19 T_META_TAG_VM_PREFERRED);
20
21 T_HELPER_DECL(child_exit, "Call exit() which will call sys_perf_notify()")
22 {
23 T_LOG("Child exiting...");
24 exit(0);
25 }
26
27 static mach_port_t exc_port;
28 static bool caught_exceptiion = false;
29
30 kern_return_t
catch_mach_exception_raise_state(mach_port_t exception_port,exception_type_t exception,const mach_exception_data_t code,mach_msg_type_number_t code_count,int * flavor,const thread_state_t old_state,mach_msg_type_number_t old_state_count,thread_state_t new_state,mach_msg_type_number_t * new_state_count)31 catch_mach_exception_raise_state(mach_port_t exception_port,
32 exception_type_t exception,
33 const mach_exception_data_t code,
34 mach_msg_type_number_t code_count,
35 int * flavor,
36 const thread_state_t old_state,
37 mach_msg_type_number_t old_state_count,
38 thread_state_t new_state,
39 mach_msg_type_number_t * new_state_count)
40 {
41 #pragma unused(exception_port, exception, code, code_count, flavor, old_state, old_state_count, new_state, new_state_count)
42 T_FAIL("Unsupported catch_mach_exception_raise_state");
43 return KERN_NOT_SUPPORTED;
44 }
45
46 kern_return_t
catch_mach_exception_raise_state_identity(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t code_count,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_state_count,thread_state_t new_state,mach_msg_type_number_t * new_state_count)47 catch_mach_exception_raise_state_identity(mach_port_t exception_port,
48 mach_port_t thread,
49 mach_port_t task,
50 exception_type_t exception,
51 mach_exception_data_t code,
52 mach_msg_type_number_t code_count,
53 int * flavor,
54 thread_state_t old_state,
55 mach_msg_type_number_t old_state_count,
56 thread_state_t new_state,
57 mach_msg_type_number_t * new_state_count)
58 {
59 #pragma unused(exception_port, thread, task, exception, code, code_count, flavor, old_state, old_state_count, new_state, new_state_count)
60 T_FAIL("Unsupported catch_mach_exception_raise_state_identity");
61 return KERN_NOT_SUPPORTED;
62 }
63
64 kern_return_t
catch_mach_exception_raise_state_identity_protected(mach_port_t exception_port,uint64_t thread_id,mach_port_t task_id_token,exception_type_t exception,mach_exception_data_t codes,mach_msg_type_number_t codeCnt,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_state_count,thread_state_t new_state,mach_msg_type_number_t * new_state_count)65 catch_mach_exception_raise_state_identity_protected(
66 mach_port_t exception_port,
67 uint64_t thread_id,
68 mach_port_t task_id_token,
69 exception_type_t exception,
70 mach_exception_data_t codes,
71 mach_msg_type_number_t codeCnt,
72 int * flavor,
73 thread_state_t old_state,
74 mach_msg_type_number_t old_state_count,
75 thread_state_t new_state,
76 mach_msg_type_number_t * new_state_count)
77 {
78 #pragma unused(exception_port, thread_id, tatask_id_tokensk, exception, codes, codeCnt, flavor, old_state, old_state_count, new_state, new_state_count)
79 T_FAIL("Unsupported catch_mach_exception_raise_state_identity");
80 return KERN_NOT_SUPPORTED;
81 }
82
83 kern_return_t
catch_mach_exception_raise_identity_protected(mach_port_t exception_port,uint64_t thread_id,mach_port_t task_id_token,exception_type_t exception,mach_exception_data_t codes,mach_msg_type_number_t codeCnt)84 catch_mach_exception_raise_identity_protected(
85 mach_port_t exception_port,
86 uint64_t thread_id,
87 mach_port_t task_id_token,
88 exception_type_t exception,
89 mach_exception_data_t codes,
90 mach_msg_type_number_t codeCnt)
91 {
92 #pragma unused(thread_id, task_id_token)
93 caught_exceptiion = true;
94 T_QUIET; T_ASSERT_EQ(exception_port, exc_port, "correct exception port");
95 T_QUIET; T_ASSERT_EQ(exception, EXC_RPC_ALERT, "exception type is EXC_RPC_ALERT");
96 T_QUIET; T_ASSERT_EQ(codeCnt, 2, "codeCnt is 2");
97 T_QUIET; T_ASSERT_EQ(codes[0], 0xFF000001, "codes[0] is 0xFF000001");
98 return KERN_SUCCESS;
99 }
100
101 kern_return_t
catch_mach_exception_raise(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t code_count)102 catch_mach_exception_raise(mach_port_t exception_port,
103 mach_port_t thread,
104 mach_port_t task,
105 exception_type_t exception,
106 mach_exception_data_t code,
107 mach_msg_type_number_t code_count)
108 {
109 #pragma unused(exception_port, thread, task, exception, code, code_count)
110 T_FAIL("Unsupported catch_mach_exception_raise_state_identity");
111 return KERN_NOT_SUPPORTED;
112 }
113
114 static void
run_test(void)115 run_test(void)
116 {
117 int ret, child_pid;
118 task_t task;
119 mach_port_options_t opts = {
120 .flags = MPO_INSERT_SEND_RIGHT,
121 };
122 char path[1024];
123 uint32_t size = sizeof(path);
124 exception_mask_t masks[EXC_TYPES_COUNT];
125 mach_msg_type_number_t nmasks = 0;
126 exception_port_t old_ports[EXC_TYPES_COUNT];
127 exception_behavior_t old_behaviors[EXC_TYPES_COUNT];
128 thread_state_flavor_t old_flavors[EXC_TYPES_COUNT];
129
130 /* Save the current host exception port for EXC_MASK_RPC_ALERT */
131 ret = host_get_exception_ports(mach_host_self(), EXC_MASK_RPC_ALERT,
132 masks, &nmasks, old_ports, old_behaviors, old_flavors);
133 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "host_get_exception_ports");
134
135 /* Allocate a new port to catch exception */
136 task = mach_task_self();
137 T_QUIET; T_ASSERT_NE(task, MACH_PORT_NULL, "mach_task_self");
138
139 ret = mach_port_construct(task, &opts, 0ull, &exc_port);
140 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "mach_port_construct");
141
142 /* Set the new port as the host exception port */
143 ret = host_set_exception_ports(mach_host_self(), EXC_MASK_RPC_ALERT, exc_port, EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, old_flavors[0]);
144 T_ASSERT_MACH_SUCCESS(ret, "Set up host exception port for EXC_MASK_RPC_ALERT");
145
146 /* Spawn child to call exit() which calls into sys_perf_notify() */
147 T_QUIET; T_ASSERT_POSIX_ZERO(_NSGetExecutablePath(path, &size), "_NSGetExecutablePath");
148 char *args[] = { path, "-n", "child_exit", NULL };
149 T_ASSERT_POSIX_ZERO(posix_spawn(&child_pid, args[0], NULL, NULL, args, NULL), "Spawn child to call exit()");
150
151 /* We should receive an exception */
152 ret = mach_msg_server_once(mach_exc_server, 4096, exc_port, 0);
153 T_ASSERT_MACH_SUCCESS(ret, "Host exception port should receive an exception after child exited");
154 T_ASSERT_EQ(caught_exceptiion, true, "catch_mach_exception_raise_identity_protected() triggered");
155
156 /* Reset host exception port */
157 ret = host_set_exception_ports(mach_host_self(), EXC_MASK_RPC_ALERT, old_ports[0], old_behaviors[0], old_flavors[0]);
158 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "resetting host exception port");
159
160 /* Deallocate exception port */
161 ret = mach_port_deallocate(task, exc_port);
162 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "mach_port_deallocate");
163 }
164
165 T_DECL(sys_perf_notify_test, "test sys_perf_notify delivery exception successfully when process exits", T_META_ASROOT(true))
166 {
167 run_test();
168 }
169