xref: /xnu-12377.41.6/tests/ipc/imm_pinned_control_port.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
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