1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <darwintest.h>
6 #include <mach/mach.h>
7 #include <mach/mach_vm.h>
8 #include <sys/sysctl.h>
9 #include <spawn.h>
10 #include <signal.h>
11 #include <TargetConditionals.h>
12 #include "excserver_protect.h"
13
14 #define MAX_ARGV 3
15 #define EXC_CODE_SHIFT 32
16 #define EXC_GUARD_TYPE_SHIFT 29
17 #define MAX_TEST_NUM 21
18
19 #define TASK_EXC_GUARD_MP_DELIVER 0x10
20
21 extern char **environ;
22 static uint64_t exception_code = 0;
23 static exception_type_t exception_taken = 0;
24
25 #ifndef kGUARD_EXC_INVALID_OPTIONS
26 #define kGUARD_EXC_INVALID_OPTIONS 3
27 #endif
28
29 /*
30 * This test verifies behaviors of immovable/pinned task/thread ports.
31 *
32 * 1. Compare and verifies port names of mach_{task, thread}_self(),
33 * {TASK, THREAD}_KERNEL_PORT, and ports returned from task_threads()
34 * and processor_set_tasks().
35 * 2. Make sure correct exceptions are raised resulting from moving immovable
36 * task/thread control, read and inspect ports.
37 * 3. Make sure correct exceptions are raised resulting from deallocating pinned
38 * task/thread control ports.
39 * 4. Make sure immovable ports cannot be stashed:
40 * rdar://70585367 (Disallow immovable port stashing with *_set_special_port() and mach_port_register())
41 */
42 T_GLOBAL_META(
43 T_META_NAMESPACE("xnu.ipc"),
44 T_META_RADAR_COMPONENT_NAME("xnu"),
45 T_META_RADAR_COMPONENT_VERSION("IPC"),
46 T_META_RUN_CONCURRENTLY(TRUE),
47 T_META_TAG_VM_PREFERRED
48 );
49
50 static uint64_t soft_exception_code[] = {
51 EXC_GUARD, // Soft crash delivered as EXC_CORPSE_NOTIFY
52 EXC_GUARD,
53 EXC_GUARD,
54 EXC_GUARD,
55 EXC_GUARD,
56 EXC_GUARD,
57 EXC_GUARD,
58
59 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
60 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
61 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
62 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
63 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
64 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
65 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
66 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
67 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
68 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
69
70 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_INVALID_OPTIONS,
71 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_INVALID_OPTIONS,
72 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_INVALID_OPTIONS,
73 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_INVALID_OPTIONS,
74 };
75
76 static uint64_t hard_exception_code[] = {
77 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_MOD_REFS,
78 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_MOD_REFS,
79 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_MOD_REFS,
80 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_MOD_REFS,
81 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_MOD_REFS,
82 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_MOD_REFS,
83 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_MOD_REFS,
84
85 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
86 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
87 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
88 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
89 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
90 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
91 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
92 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
93 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
94 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_IMMOVABLE,
95
96 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_INVALID_OPTIONS,
97 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_INVALID_OPTIONS,
98 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_INVALID_OPTIONS,
99 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_INVALID_OPTIONS,
100 };
101
102 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)103 catch_mach_exception_raise_state(mach_port_t exception_port,
104 exception_type_t exception,
105 const mach_exception_data_t code,
106 mach_msg_type_number_t code_count,
107 int * flavor,
108 const thread_state_t old_state,
109 mach_msg_type_number_t old_state_count,
110 thread_state_t new_state,
111 mach_msg_type_number_t * new_state_count)
112 {
113 #pragma unused(exception_port, exception, code, code_count, flavor, old_state, old_state_count, new_state, new_state_count)
114 T_FAIL("Unsupported catch_mach_exception_raise_state");
115 return KERN_NOT_SUPPORTED;
116 }
117
118 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)119 catch_mach_exception_raise_identity_protected(
120 mach_port_t exception_port,
121 uint64_t thread_id,
122 mach_port_t task_id_token,
123 exception_type_t exception,
124 mach_exception_data_t codes,
125 mach_msg_type_number_t codeCnt)
126 {
127 #pragma unused(exception_port, codeCnt)
128 task_t task;
129 pid_t pid;
130 kern_return_t kr = task_identity_token_get_task_port(task_id_token, TASK_FLAVOR_READ, &task);
131 T_ASSERT_MACH_SUCCESS(kr, "task_identity_token_get_task_port");
132 kr = pid_for_task(task, &pid);
133 T_ASSERT_MACH_SUCCESS(kr, "pid_for_task");
134 T_LOG("Crashing child pid: %d, continuing...\n", pid);
135
136 kr = mach_port_deallocate(mach_task_self(), task);
137 T_QUIET; T_EXPECT_MACH_SUCCESS(kr, "mach_port_deallocate");
138
139 T_ASSERT_GT_UINT(codeCnt, 0, "CodeCnt");
140 T_LOG("Caught exception type: %d code: 0x%llx", exception, (uint64_t)codes[0]);
141 if (exception == EXC_GUARD || exception == EXC_CORPSE_NOTIFY) {
142 exception_taken = exception;
143 exception_code = (uint64_t)codes[0];
144 } else {
145 T_FAIL("Unexpected exception");
146 }
147 return KERN_SUCCESS;
148 }
149
150 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)151 catch_mach_exception_raise_state_identity(mach_port_t exception_port,
152 mach_port_t thread,
153 mach_port_t task,
154 exception_type_t exception,
155 mach_exception_data_t code,
156 mach_msg_type_number_t code_count,
157 int * flavor,
158 thread_state_t old_state,
159 mach_msg_type_number_t old_state_count,
160 thread_state_t new_state,
161 mach_msg_type_number_t * new_state_count)
162 {
163 #pragma unused(exception_port, thread, task, exception, code, code_count, flavor, old_state, old_state_count, new_state, new_state_count)
164 T_FAIL("Unsupported catch_mach_exception_raise_state_identity");
165 return KERN_NOT_SUPPORTED;
166 }
167
168 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)169 catch_mach_exception_raise(mach_port_t exception_port,
170 mach_port_t thread,
171 mach_port_t task,
172 exception_type_t exception,
173 mach_exception_data_t code,
174 mach_msg_type_number_t code_count)
175 {
176 #pragma unused(exception_port, thread, task, exception, code, code_count)
177 T_FAIL("Unsupported catch_mach_exception_raise_state_identity");
178 return KERN_NOT_SUPPORTED;
179 }
180
181 static void *
exception_server_thread(void * arg)182 exception_server_thread(void *arg)
183 {
184 kern_return_t kr;
185 mach_port_t exc_port = *(mach_port_t *)arg;
186
187 /* Handle exceptions on exc_port */
188 kr = mach_msg_server_once(mach_exc_server, 4096, exc_port, 0);
189 T_QUIET; T_EXPECT_MACH_SUCCESS(kr, "mach_msg_server_once");
190
191 return NULL;
192 }
193
194 static mach_port_t
alloc_exception_port(void)195 alloc_exception_port(void)
196 {
197 kern_return_t kret;
198 mach_port_t exc_port = MACH_PORT_NULL;
199 mach_port_t task = mach_task_self();
200
201 kret = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &exc_port);
202 T_QUIET; T_EXPECT_MACH_SUCCESS(kret, "mach_port_allocate exc_port");
203
204 kret = mach_port_insert_right(task, exc_port, exc_port, MACH_MSG_TYPE_MAKE_SEND);
205 T_QUIET; T_EXPECT_MACH_SUCCESS(kret, "mach_port_insert_right exc_port");
206
207 return exc_port;
208 }
209
210 static void
test_immovable_port_stashing(void)211 test_immovable_port_stashing(void)
212 {
213 kern_return_t kr;
214 mach_port_t port;
215
216 kr = task_set_special_port(mach_task_self(), TASK_BOOTSTRAP_PORT, mach_task_self());
217 T_EXPECT_EQ(kr, KERN_INVALID_RIGHT, "should disallow task_set_special_port() with immovable port");
218
219 kr = thread_set_special_port(mach_thread_self(), THREAD_KERNEL_PORT, mach_thread_self());
220 T_EXPECT_EQ(kr, KERN_INVALID_RIGHT, "should disallow task_set_special_port() with immovable port");
221
222 mach_port_t stash[1] = {mach_task_self()};
223 kr = mach_ports_register(mach_task_self(), stash, 1);
224 T_EXPECT_EQ(kr, KERN_INVALID_RIGHT, "should disallow mach_ports_register() with immovable port");
225
226 T_QUIET; T_ASSERT_MACH_SUCCESS(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port), "mach_port_allocate");
227 T_QUIET; T_ASSERT_MACH_SUCCESS(mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND), "mach_port_insert_right");
228
229 stash[0] = port;
230 kr = mach_ports_register(mach_task_self(), stash, 1);
231 T_EXPECT_MACH_SUCCESS(kr, "mach_ports_register() should succeed with movable port");
232 }
233
234 int
read_ipc_control_port_options(void)235 read_ipc_control_port_options(void)
236 {
237 uint32_t opts = 0;
238 size_t size = sizeof(&opts);
239 int sysctl_ret = sysctlbyname("kern.ipc_control_port_options", &opts, &size, NULL, 0);
240
241 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctl_ret, "kern.ipc_control_port_options");
242
243 return opts;
244 }
245
246 bool
is_hard_immovable_control_port_enabled(void)247 is_hard_immovable_control_port_enabled(void)
248 {
249 T_LOG("Check if immovable control port is enabled");
250 int opts = read_ipc_control_port_options();
251 return (opts & 0x8) == 0x8;
252 }
253
254 bool
is_hard_pinning_enforcement_enabled(void)255 is_hard_pinning_enforcement_enabled(void)
256 {
257 T_LOG("Check if hard pinning enforcement is enabled");
258 int opts = read_ipc_control_port_options();
259 return opts & 0x2;
260 }
261
262 static bool
is_test_possible_in_current_system()263 is_test_possible_in_current_system()
264 {
265 uint32_t task_exc_guard = 0;
266 size_t te_size = sizeof(&task_exc_guard);
267 int sysctl_ret;
268
269 /*
270 * This test tries to check assumptions that are only valid if
271 * certain boot args are missing, because in the presence of those boot args
272 * we relax the security policy that this test is trying to enforce.
273 */
274 size_t bootargs_size = 1024;
275 char bootargs[bootargs_size];
276 sysctl_ret = sysctlbyname("kern.bootargs", bootargs, &bootargs_size, NULL, 0);
277 T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctl_ret, "kern.bootargs");
278 if (strstr(bootargs, "amfi_get_out_of_my_way=1") != NULL ||
279 strstr(bootargs, "amfi_unrestrict_task_for_pid=1") != NULL) {
280 T_SKIP("This test can only verify behavior when AMFI boot args are unset");
281 return false;
282 }
283
284 T_LOG("Check if task_exc_guard exception has been enabled\n");
285 sysctl_ret = sysctlbyname("kern.task_exc_guard_default", &task_exc_guard, &te_size, NULL, 0);
286 T_ASSERT_EQ(sysctl_ret, 0, "sysctl to check exc_guard config");
287
288 if (!(task_exc_guard & TASK_EXC_GUARD_MP_DELIVER)) {
289 T_SKIP("task_exc_guard exception is not enabled");
290 return false;
291 }
292
293 if (!is_hard_immovable_control_port_enabled()) {
294 T_SKIP("hard immovable control port isn't enabled");
295 return false;
296 }
297
298 return true;
299 }
300
301 static void
test_imm_control_port_exc_behavior(const char * test_prog_name)302 test_imm_control_port_exc_behavior(const char* test_prog_name)
303 {
304 uint64_t *test_exception_code;
305 posix_spawnattr_t attrs;
306 pid_t client_pid = 0;
307 mach_port_t exc_port;
308 pthread_t s_exc_thread;
309 uint64_t exc_id;
310 char *child_args[MAX_ARGV];
311 int ret = 0;
312
313 T_SETUPBEGIN;
314 bool is_test_possible = is_test_possible_in_current_system();
315 T_SETUPEND;
316 if (!is_test_possible) {
317 return;
318 }
319
320 if (is_hard_pinning_enforcement_enabled()) {
321 T_LOG("Hard pinning enforcement is on.");
322 test_exception_code = hard_exception_code;
323 } else {
324 T_LOG("Hard pinning enforcement is off.");
325 test_exception_code = soft_exception_code;
326 }
327
328 /* spawn a child and see if EXC_GUARD are correctly generated */
329 for (int i = 0; i < MAX_TEST_NUM; i++) {
330 /* Create the exception port for the child */
331 exc_port = alloc_exception_port();
332 T_QUIET; T_ASSERT_NE(exc_port, MACH_PORT_NULL, "Create a new exception port");
333
334 /* Create exception serving thread */
335 ret = pthread_create(&s_exc_thread, NULL, exception_server_thread, &exc_port);
336 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_create exception_server_thread");
337
338 /* Initialize posix_spawn attributes */
339 posix_spawnattr_init(&attrs);
340
341 int err = posix_spawnattr_setexceptionports_np(&attrs, EXC_MASK_GUARD | EXC_MASK_CORPSE_NOTIFY, exc_port,
342 (exception_behavior_t) (EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES), 0);
343 T_QUIET; T_ASSERT_POSIX_SUCCESS(err, "posix_spawnattr_setflags");
344
345 child_args[0] = (char*)test_prog_name;
346 char test_num[10];
347 sprintf(test_num, "%d", i);
348 child_args[1] = test_num;
349 child_args[2] = NULL;
350
351 T_LOG("========== Spawning new child ==========");
352 err = posix_spawn(&client_pid, child_args[0], NULL, &attrs, &child_args[0], environ);
353 T_ASSERT_POSIX_SUCCESS(err, "posix_spawn control_port_options_client = %d test_num = %d", client_pid, i);
354
355 int child_status;
356 /* Wait for child and check for exception */
357 if (-1 == waitpid(-1, &child_status, 0)) {
358 T_FAIL("waitpid: child mia");
359 }
360
361 if (WIFEXITED(child_status)) {
362 if (WEXITSTATUS(child_status)) {
363 T_FAIL("Child exited with status = %x", child_status); T_END;
364 } else {
365 /* Skipping test because CS_OPS_CLEARPLATFORM is not supported in the current BATS container */
366 T_SKIP("Failed to remove platform binary");
367 }
368 }
369
370 sleep(1);
371 kill(1, SIGKILL);
372
373 ret = pthread_join(s_exc_thread, NULL);
374 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_join");
375
376 if (exception_taken == EXC_GUARD) {
377 exc_id = exception_code >> EXC_CODE_SHIFT;
378 } else {
379 exc_id = exception_code;
380 }
381
382 T_LOG("Exception code: Received code = 0x%llx Expected code = 0x%llx", exc_id, test_exception_code[i]);
383 T_EXPECT_EQ(exc_id, test_exception_code[i], "Exception code: Received == Expected");
384 }
385 }
386
387 static void
test_imm_pinned_control_port_stashing(void)388 test_imm_pinned_control_port_stashing(void)
389 {
390 T_SETUPBEGIN;
391 bool is_test_possible = is_test_possible_in_current_system();
392 T_SETUPEND;
393 if (!is_test_possible) {
394 return;
395 }
396
397 /* try stashing immovable ports: rdar://70585367 */
398 test_immovable_port_stashing();
399 }
400
401 T_DECL(imm_pinned_control_port_stashing, "Validate API behavior when presented with an immovable port",
402 T_META_CHECK_LEAKS(false))
403 {
404 test_imm_pinned_control_port_stashing();
405 }
406
407 /*
408 * rdar://150644433: IPC policy changed substantially since these tests were written,
409 * and these tests weren't updated to reflect changes in policy.
410 * The flow these tests exercise is no longer throwing exceptions, and someone needs to dig into the
411 * policy changes to understand how the tests needs to change.
412 * This is a bit too far to go at the precise moment this comment was written, where
413 * I'm just trying to re-enable other parts of this test program, so I've marked the tests as may-fail
414 * for the future day where the coverage will be useful to recover.
415 */
416 T_DECL(imm_pinned_control_port_hardened, "Test pinned & immovable task and thread control ports for platform restrictions binary",
417 T_META_IGNORECRASHES(".*pinned_rights_child.*"),
418 /* rdar://150644433 this test has fallen behind contemporary IPC policy, see comment above */
419 T_META_ENABLED(false),
420 T_META_CHECK_LEAKS(false))
421 {
422 test_imm_control_port_exc_behavior("imm_pinned_control_port_crasher_3P_hardened");
423 }
424
425 T_DECL(imm_pinned_control_port, "Test pinned & immovable task and thread control ports for first party binary",
426 T_META_IGNORECRASHES(".*pinned_rights_child.*"),
427 T_META_CHECK_LEAKS(false),
428 /* rdar://150644433 this test has fallen behind contemporary IPC policy, see comment above */
429 T_META_ENABLED(false),
430 T_META_TAG_VM_PREFERRED)
431 {
432 test_imm_control_port_exc_behavior("imm_pinned_control_port_crasher");
433 }
434