xref: /xnu-12377.41.6/tests/reply_port_defense_client.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <pthread.h>
4 #include <pthread/private.h>
5 #include <unistd.h>
6 #include <mach/kern_return.h>
7 #include <mach/mach.h>
8 #include <mach/task.h>
9 #include <mach/thread_status.h>
10 #include <os/tsd.h>
11 #include <assert.h>
12 #include <sys/codesign.h>
13 #include <stdbool.h>
14 #include <darwintest.h>
15 #include <mach/mk_timer.h>
16 #include "cs_helpers.h"
17 
18 #define MAX_TEST_NUM 22
19 
20 #if __arm64__
21 #define machine_thread_state_t          arm_thread_state64_t
22 #define EXCEPTION_THREAD_STATE          ARM_THREAD_STATE64
23 #define EXCEPTION_THREAD_STATE_COUNT    ARM_THREAD_STATE64_COUNT
24 #elif __x86_64__
25 #define machine_thread_state_t          x86_thread_state_t
26 #define EXCEPTION_THREAD_STATE          x86_THREAD_STATE
27 #define EXCEPTION_THREAD_STATE_COUNT    x86_THREAD_STATE_COUNT
28 #else
29 #error Unsupported architecture
30 #endif
31 
32 /* in xpc/launch_private.h */
33 #define XPC_DOMAIN_SYSTEM 1
34 #define XPC_DOMAIN_PORT 7
35 
36 static mach_port_t
alloc_server_port(void)37 alloc_server_port(void)
38 {
39 	mach_port_t server_port = MACH_PORT_NULL;
40 	kern_return_t kr;
41 
42 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &server_port);
43 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "alloc_server_port");
44 
45 	kr = mach_port_insert_right(mach_task_self(), server_port, server_port, MACH_MSG_TYPE_MAKE_SEND);
46 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "alloc_server_port mach_port_insert_right");
47 
48 	return server_port;
49 }
50 
51 static mach_port_t
alloc_reply_port()52 alloc_reply_port()
53 {
54 	kern_return_t kr;
55 	mach_port_t reply_port = MACH_PORT_NULL;
56 
57 	mach_port_options_t opts = {
58 		.flags = MPO_REPLY_PORT | MPO_INSERT_SEND_RIGHT,
59 	};
60 
61 	kr = mach_port_construct(mach_task_self(), &opts, 0, &reply_port);
62 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "alloc_reply_port");
63 	T_QUIET; T_ASSERT_NE(reply_port, (mach_port_t)MACH_PORT_NULL, "reply_port_create: %s", mach_error_string(kr));
64 
65 	return reply_port;
66 }
67 
68 static mach_port_t
alloc_weak_reply_port()69 alloc_weak_reply_port()
70 {
71 	kern_return_t kr;
72 	mach_port_t reply_port = MACH_PORT_NULL;
73 
74 	mach_port_options_t opts = {
75 		.flags = MPO_PROVISIONAL_REPLY_PORT,
76 	};
77 
78 	kr = mach_port_construct(mach_task_self(), &opts, 0, &reply_port);
79 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "alloc_reply_port");
80 	T_QUIET; T_ASSERT_NE(reply_port, (mach_port_t)MACH_PORT_NULL, "weak_reply_port_create: %s", mach_error_string(kr));
81 
82 	return reply_port;
83 }
84 
85 static mach_port_t
alloc_service_port(void)86 alloc_service_port(void)
87 {
88 	kern_return_t kr;
89 	mach_port_t service_port = MACH_PORT_NULL;
90 
91 	struct mach_service_port_info sp_info = {
92 		.mspi_string_name = "com.apple.testservice",
93 		.mspi_domain_type = XPC_DOMAIN_SYSTEM,
94 	};
95 
96 	mach_port_options_t opts = {
97 		.flags = MPO_STRICT_SERVICE_PORT | MPO_INSERT_SEND_RIGHT,
98 		.service_port_info = &sp_info,
99 	};
100 
101 	kr = mach_port_construct(mach_task_self(), &opts, 0, &service_port);
102 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "alloc_service_port");
103 
104 	return service_port;
105 }
106 
107 static mach_port_t
alloc_bootstrap_port(void)108 alloc_bootstrap_port(void)
109 {
110 	kern_return_t kr;
111 	mach_port_t bootstrap_port = MACH_PORT_NULL;
112 
113 	struct mach_service_port_info sp_info = {
114 		.mspi_string_name = "com.apple.testservice",
115 		.mspi_domain_type = XPC_DOMAIN_PORT,
116 	};
117 
118 	mach_port_options_t opts = {
119 		.flags = MPO_STRICT_SERVICE_PORT | MPO_INSERT_SEND_RIGHT,
120 		.service_port_info = &sp_info,
121 	};
122 
123 	kr = mach_port_construct(mach_task_self(), &opts, 0, &bootstrap_port);
124 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "alloc_bootstrap_port");
125 
126 	return bootstrap_port;
127 }
128 
129 static mach_port_t
alloc_weak_service_port(void)130 alloc_weak_service_port(void)
131 {
132 	kern_return_t kr;
133 	mach_port_t weak_service_port = MACH_PORT_NULL;
134 
135 	struct mach_service_port_info sp_info = {
136 		.mspi_string_name = "com.apple.testservice",
137 		.mspi_domain_type = XPC_DOMAIN_SYSTEM,
138 	};
139 
140 	mach_port_options_t opts = {
141 		.flags = MPO_SERVICE_PORT | MPO_INSERT_SEND_RIGHT,
142 		.service_port_info = &sp_info,
143 	};
144 
145 	kr = mach_port_construct(mach_task_self(), &opts, 0, &weak_service_port);
146 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "alloc_weak_service_port");
147 
148 	return weak_service_port;
149 }
150 
151 /* The rcv right of the port would be marked immovable. */
152 static void
test_immovable_receive_right(void)153 test_immovable_receive_right(void)
154 {
155 	kern_return_t kr;
156 	mach_port_t server_port = MACH_PORT_NULL, reply_port = MACH_PORT_NULL;
157 	struct {
158 		mach_msg_header_t header;
159 		mach_msg_body_t body;
160 		mach_msg_port_descriptor_t desc;
161 	} msg;
162 
163 	server_port = alloc_server_port();
164 	reply_port = alloc_reply_port();
165 
166 	msg.header.msgh_remote_port = server_port;
167 	msg.header.msgh_local_port = MACH_PORT_NULL;
168 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
169 	msg.header.msgh_size = sizeof msg;
170 
171 	msg.body.msgh_descriptor_count = 1;
172 
173 	msg.desc.name = reply_port;
174 	msg.desc.disposition = MACH_MSG_TYPE_MOVE_RECEIVE;
175 	msg.desc.type = MACH_MSG_PORT_DESCRIPTOR;
176 	kr = mach_msg_send(&msg.header);
177 
178 	printf("[reply_port_defense_client test_immovable_receive_right]: mach_msg2() returned %d\n", kr);
179 }
180 
181 /* The only way you could create a send once right is when you send the port in local port of a mach msg with MAKE_SEND_ONCE disposition. */
182 static void
test_make_send_once_right(void)183 test_make_send_once_right(void)
184 {
185 	kern_return_t kr;
186 	mach_port_t reply_port = alloc_reply_port();
187 	kr = mach_port_insert_right(mach_task_self(), reply_port, reply_port, MACH_MSG_TYPE_MAKE_SEND_ONCE);
188 	printf("[reply_port_defense_client test_make_send_once_right]: mach_port_insert_right() returned %d\n", kr);
189 }
190 
191 static void
test_alloc_weak_reply_port(void)192 test_alloc_weak_reply_port(void)
193 {
194 	mach_port_t reply_port = alloc_weak_reply_port();
195 	printf("[reply_port_defense_client test_alloc_weak_reply_port]: did not crash with port=%d\n", reply_port);
196 }
197 
198 /* The send right of the port would only used for guarding a name in ipc space, it would not allow to send a message. */
199 static void
test_using_send_right(void)200 test_using_send_right(void)
201 {
202 	kern_return_t kr;
203 	mach_port_t reply_port = alloc_reply_port();
204 	struct {
205 		mach_msg_header_t header;
206 		mach_msg_body_t body;
207 	} msg;
208 
209 	msg.header.msgh_remote_port = reply_port;
210 	msg.header.msgh_local_port = MACH_PORT_NULL;
211 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
212 	msg.header.msgh_size = sizeof msg;
213 
214 	kr = mach_msg_send(&msg.header);
215 	printf("[reply_port_defense_client test_using_send_right]: mach_msg2() returned %d\n", kr);
216 }
217 
218 /* The send right of the port would only used for guarding a name in ipc space, it would not allowed to get moved. */
219 static void
test_move_send_right(void)220 test_move_send_right(void)
221 {
222 	kern_return_t kr;
223 	mach_port_t server_port = MACH_PORT_NULL, reply_port = MACH_PORT_NULL;
224 	struct {
225 		mach_msg_header_t header;
226 		mach_msg_body_t body;
227 		mach_msg_port_descriptor_t desc;
228 	} msg;
229 
230 	server_port = alloc_server_port();
231 	reply_port = alloc_reply_port();
232 
233 	msg.header.msgh_remote_port = server_port;
234 	msg.header.msgh_local_port = MACH_PORT_NULL;
235 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
236 	msg.header.msgh_size = sizeof msg;
237 
238 	msg.body.msgh_descriptor_count = 1;
239 
240 	msg.desc.name = reply_port;
241 	msg.desc.disposition = MACH_MSG_TYPE_MOVE_SEND;
242 	msg.desc.type = MACH_MSG_PORT_DESCRIPTOR;
243 
244 	kr = mach_msg_send(&msg.header);
245 	printf("[reply_port_defense_client test_move_send_right]: mach_msg2() returned %d\n", kr);
246 }
247 
248 static void
test_unentitled_thread_set_state(void)249 test_unentitled_thread_set_state(void)
250 {
251 	machine_thread_state_t ts;
252 	mach_msg_type_number_t count = MACHINE_THREAD_STATE_COUNT;
253 
254 	/* thread_set_state as a platform restrictions binary should fail */
255 	kern_return_t kr = thread_get_state(mach_thread_self(), MACHINE_THREAD_STATE, (thread_state_t)&ts, &count);
256 
257 	kr = thread_set_state(mach_thread_self(), MACHINE_THREAD_STATE, (thread_state_t)&ts, count);
258 	assert(kr != KERN_SUCCESS);
259 	exit(-1); /* Should have crashed before here! */
260 }
261 
262 static void
test_unentitled_thread_set_exception_ports(void)263 test_unentitled_thread_set_exception_ports(void)
264 {
265 	mach_port_t exc_port = alloc_server_port();
266 
267 	/* thread_set_exception_ports as a platform restrictions binary should fail without identity protected options */
268 	kern_return_t kr = thread_set_exception_ports(
269 		mach_thread_self(),
270 		EXC_MASK_ALL,
271 		exc_port,
272 		(exception_behavior_t)((unsigned int)EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES),
273 		EXCEPTION_THREAD_STATE);
274 
275 	/* thread_set_exception_ports is supposed to crash, unless the policy is turned off.
276 	 * Things that disable the policy: AMFI boot-args in use, SIP disabled,
277 	 * third party plugins in a process. The caller of this client will check
278 	 * whether the test crashed and correctly adhered to these policies.
279 	 */
280 	printf("thread_set_exception_ports did not crash\n");
281 }
282 
283 static void
unentitled_set_exception_ports_pass(void)284 unentitled_set_exception_ports_pass(void)
285 {
286 	mach_port_t exc_port = alloc_server_port();
287 
288 	/* thread_set_exception_ports with state *IDENTITY_PROTECTED should not fail */
289 	kern_return_t kr = thread_set_exception_ports(
290 		mach_thread_self(),
291 		EXC_MASK_ALL,
292 		exc_port,
293 		(exception_behavior_t)((unsigned int)EXCEPTION_STATE_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES),
294 		EXCEPTION_THREAD_STATE);
295 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_set_exception_ports EXCEPTION_STATE_IDENTITY_PROTECTED");
296 
297 	kr = thread_set_exception_ports(
298 		mach_thread_self(),
299 		EXC_MASK_ALL,
300 		exc_port,
301 		(exception_behavior_t)((unsigned int)EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES),
302 		EXCEPTION_THREAD_STATE);
303 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_set_exception_ports EXCEPTION_IDENTITY_PROTECTED");
304 
305 	return;
306 }
307 
308 static void
exception_ports_crash(void)309 exception_ports_crash(void)
310 {
311 	kern_return_t kr;
312 	mach_port_t exc_port;
313 	mach_port_options_t opts = {
314 		.flags = MPO_INSERT_SEND_RIGHT | MPO_EXCEPTION_PORT,
315 	};
316 
317 	kr = mach_port_construct(mach_task_self(), &opts, 0ull, &exc_port);
318 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "exception_ports_crash mach_port_construct");
319 
320 	kr = task_register_hardened_exception_handler(current_task(),
321 	    0, EXC_MASK_BAD_ACCESS,
322 	    EXCEPTION_STATE_IDENTITY_PROTECTED, EXCEPTION_THREAD_STATE, exc_port);
323 
324 	kr = thread_set_exception_ports(
325 		mach_thread_self(),
326 		EXC_MASK_BAD_ACCESS,
327 		exc_port,
328 		(exception_behavior_t)((unsigned int)EXCEPTION_STATE_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES),
329 		EXCEPTION_THREAD_STATE);
330 
331 	printf("thread_set_exception_ports did not crash: %d\n", kr);
332 }
333 
334 static void
kobject_reply_port_defense(void)335 kobject_reply_port_defense(void)
336 {
337 	machine_thread_state_t ts;
338 	mach_msg_type_number_t count = MACHINE_THREAD_STATE_COUNT;
339 	mach_port_t port = MACH_PORT_NULL;
340 
341 	kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
342 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "kobject_reply_port_defense mach_port_allocate");
343 
344 	// make a kobject call
345 	kr = thread_get_state(mach_thread_self(), MACHINE_THREAD_STATE, (thread_state_t)&ts, &count);
346 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "kobject_reply_port_defense thread_get_state");
347 
348 	// set the MIG reply port to a "normal" port
349 	_os_tsd_set_direct(__TSD_MIG_REPLY, (void *)(uintptr_t)port);
350 
351 	kr = thread_get_state(mach_thread_self(), MACHINE_THREAD_STATE, (thread_state_t)&ts, &count);
352 
353 	T_FAIL("kobject call did not crash: %d\n", kr);
354 }
355 
356 static void
test_move_service_port(void)357 test_move_service_port(void)
358 {
359 	kern_return_t kr;
360 	mach_port_t server_port = MACH_PORT_NULL, service_port = MACH_PORT_NULL;
361 	struct {
362 		mach_msg_header_t header;
363 		mach_msg_body_t body;
364 		mach_msg_port_descriptor_t desc;
365 	} msg;
366 
367 	server_port = alloc_server_port();
368 	service_port = alloc_service_port();
369 
370 	msg.header.msgh_remote_port = server_port;
371 	msg.header.msgh_local_port = MACH_PORT_NULL;
372 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
373 	msg.header.msgh_size = sizeof msg;
374 
375 	msg.body.msgh_descriptor_count = 1;
376 
377 	msg.desc.name = service_port;
378 	msg.desc.disposition = MACH_MSG_TYPE_MOVE_RECEIVE;
379 	msg.desc.type = MACH_MSG_PORT_DESCRIPTOR;
380 
381 	kr = mach_msg_send(&msg.header);
382 	T_FAIL("move service port did not crash: %d\n", kr);
383 }
384 
385 static void
test_mktimer_notification_policy(void)386 test_mktimer_notification_policy(void)
387 {
388 	mach_port_t timer_port = MACH_PORT_NULL;
389 	mach_port_t notify_port = MACH_PORT_NULL;
390 	mach_port_t previous = MACH_PORT_NULL;
391 
392 	kern_return_t kr = KERN_SUCCESS;
393 
394 	timer_port = mk_timer_create();
395 	T_ASSERT_NE(timer_port, (mach_port_t)MACH_PORT_NULL, "mk_timer_create: %s", mach_error_string(kr));
396 
397 	/* notification port for the mk_timer port to come back on */
398 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &notify_port);
399 	T_ASSERT_EQ(kr, KERN_SUCCESS, "mach_port_allocate(notify_port): %s", mach_error_string(kr));
400 
401 	T_LOG("timer: 0x%x, notify: 0x%x", timer_port, notify_port);
402 
403 	/* request a port-destroyed notification on the timer port, which should crash */
404 	kr = mach_port_request_notification(mach_task_self(), timer_port, MACH_NOTIFY_PORT_DESTROYED,
405 	    0, notify_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
406 
407 	T_FAIL("mktimer did not crash with exc_guard kr=%d", kr);
408 }
409 
410 static void
test_reply_port_port_destroyed_notification_policy(void)411 test_reply_port_port_destroyed_notification_policy(void)
412 {
413 	mach_port_t reply_port = MACH_PORT_NULL;
414 	mach_port_t previous = MACH_PORT_NULL;
415 	mach_port_t notify_port = MACH_PORT_NULL;
416 
417 	kern_return_t kr = KERN_SUCCESS;
418 	mach_port_options_t opts = {};
419 
420 	reply_port = alloc_reply_port();
421 
422 	kr = mach_port_construct(mach_task_self(), &opts, 0, &notify_port);
423 	T_ASSERT_EQ(kr, KERN_SUCCESS, "mach_port_allocate(notify_port): %s", mach_error_string(kr));
424 
425 	/* request a port-destroyed notification on the reply port */
426 	kr = mach_port_request_notification(mach_task_self(), reply_port, MACH_NOTIFY_PORT_DESTROYED,
427 	    0, notify_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
428 
429 	printf("reply port did not crash kr=%d\n", kr);
430 }
431 
432 static void
test_reply_port_no_senders_notification_policy(void)433 test_reply_port_no_senders_notification_policy(void)
434 {
435 	mach_port_t reply_port = MACH_PORT_NULL;
436 	mach_port_t previous = MACH_PORT_NULL;
437 	mach_port_t notify_port = MACH_PORT_NULL;
438 
439 	kern_return_t kr = KERN_SUCCESS;
440 
441 	reply_port = alloc_reply_port();
442 	mach_port_options_t opts = {};
443 
444 	kr = mach_port_construct(mach_task_self(), &opts, 0, &notify_port);
445 	T_ASSERT_EQ(kr, KERN_SUCCESS, "mach_port_allocate(notify_port): %s", mach_error_string(kr));
446 
447 	/* request a no-senders notification on the reply port */
448 	kr = mach_port_request_notification(mach_task_self(), reply_port, MACH_NOTIFY_NO_SENDERS,
449 	    0, notify_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
450 
451 	T_FAIL("reply port did not crash kr=%d", kr);
452 }
453 
454 static void
test_reply_port_insert_right_disallowed(void)455 test_reply_port_insert_right_disallowed(void)
456 {
457 	mach_port_t reply_port = MACH_PORT_NULL;
458 	mach_port_t send_reply_port = MACH_PORT_NULL;
459 	mach_msg_type_name_t right = 0;
460 
461 	kern_return_t kr = KERN_SUCCESS;
462 	reply_port = alloc_reply_port();
463 	kr = mach_port_extract_right(mach_task_self(), reply_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &send_reply_port, &right);
464 	T_ASSERT_EQ(kr, KERN_SUCCESS, "mach_port_extract_right(reply_port, make_send_once): %s", mach_error_string(kr));
465 
466 	T_FAIL("reply port make send once outside of kmsg did not crash kr=%d", kr);
467 }
468 
469 static kern_return_t
move_port(mach_port_t immovable_port)470 move_port(mach_port_t immovable_port)
471 {
472 	mach_port_t server_port = MACH_PORT_NULL;
473 	struct {
474 		mach_msg_header_t header;
475 		mach_msg_body_t body;
476 		mach_msg_port_descriptor_t desc;
477 	} msg;
478 
479 	server_port = alloc_server_port();
480 
481 	msg.header.msgh_remote_port = server_port;
482 	msg.header.msgh_local_port = MACH_PORT_NULL;
483 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
484 	msg.header.msgh_size = sizeof msg;
485 
486 	msg.body.msgh_descriptor_count = 1;
487 
488 	msg.desc.name = immovable_port;
489 	msg.desc.disposition = MACH_MSG_TYPE_MOVE_SEND;
490 	msg.desc.type = MACH_MSG_PORT_DESCRIPTOR;
491 	return mach_msg_send(&msg.header);
492 }
493 
494 /* attempt to move mach_task_self */
495 static void
test_mach_task_self_send_movability(void)496 test_mach_task_self_send_movability(void)
497 {
498 	kern_return_t kr = move_port(mach_task_self());
499 	printf("[reply_port_defense_client test_task_self_immovable]: mach_msg2() returned %d\n", kr);
500 }
501 
502 /* mach_task_self() is movable before and after calling task_get_special_port, when entitled */
503 static void
test_task_self_movable_send(void)504 test_task_self_movable_send(void)
505 {
506 	kern_return_t kr;
507 	mach_port_t task_self = MACH_PORT_NULL;
508 
509 	kr = move_port(mach_task_self());
510 	T_EXPECT_MACH_SUCCESS(kr, "move mach_task_self");
511 
512 	kr = task_get_special_port(mach_task_self(), TASK_KERNEL_PORT, &task_self);
513 	T_EXPECT_MACH_SUCCESS(kr, "task_get_special_port");
514 
515 	kr = move_port(mach_task_self());
516 	T_EXPECT_MACH_SUCCESS(kr, "move mach_task_self again");
517 
518 	mach_port_t thread_port = pthread_mach_thread_np(pthread_main_thread_np());
519 	kr = move_port(thread_port);
520 	T_EXPECT_MACH_SUCCESS(kr, "move main_thread_port");
521 }
522 
523 static void
test_move_newly_constructed_port_immovable_send(void)524 test_move_newly_constructed_port_immovable_send(void)
525 {
526 	kern_return_t kr;
527 	mach_port_t port = MACH_PORT_NULL;
528 
529 	mach_port_options_t opts = {
530 		.flags = MPO_INSERT_SEND_RIGHT | MPO_CONNECTION_PORT,
531 		.service_port_name = MPO_ANONYMOUS_SERVICE,
532 	};
533 
534 	kr = mach_port_construct(mach_task_self(), &opts, 0, &port);
535 
536 	kr = move_port(port);
537 	printf("kr=%d\n", kr);
538 	T_EXPECT_MACH_ERROR(kr, KERN_DENIED, "move port with immovable send rights");
539 }
540 
541 static void
test_move_special_reply_port(void)542 test_move_special_reply_port(void)
543 {
544 	kern_return_t kr;
545 	mach_port_t special_reply_port = thread_get_special_reply_port();
546 
547 	kr = move_port(special_reply_port);
548 	T_EXPECT_MACH_ERROR(kr, KERN_DENIED, "move special reply port");
549 }
550 
551 static void
test_reply_port_header_disposition(void)552 test_reply_port_header_disposition(void)
553 {
554 	kern_return_t kr;
555 	mach_port_t server_port = MACH_PORT_NULL;
556 	mach_port_t reply_port1 = MACH_PORT_NULL, reply_port2 = MACH_PORT_NULL;
557 	struct {
558 		mach_msg_header_t header;
559 	} msg;
560 
561 	server_port = alloc_server_port();
562 	reply_port1 = alloc_reply_port();
563 	reply_port2 = alloc_reply_port();
564 
565 	msg.header.msgh_remote_port = server_port;
566 	msg.header.msgh_size = sizeof msg;
567 
568 	/* sending with make_send_once should succeed */
569 	msg.header.msgh_local_port = reply_port1;
570 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
571 	    MACH_MSG_TYPE_MAKE_SEND_ONCE);
572 	kr = mach_msg_send(&msg.header);
573 	T_EXPECT_MACH_SUCCESS(kr, "reply_port_disposition make_send_once");
574 
575 	/* sending with make_send should fail */
576 	msg.header.msgh_local_port = reply_port2;
577 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
578 	    MACH_MSG_TYPE_MAKE_SEND);
579 	kr = mach_msg_send(&msg.header);
580 	T_ASSERT_MACH_ERROR(kr, MACH_SEND_INVALID_REPLY, "reply_port_disposition make_send");
581 }
582 
583 static void
test_service_port_as_exception_port(void)584 test_service_port_as_exception_port(void)
585 {
586 	kern_return_t kr;
587 	mach_port_t service_port = alloc_service_port();
588 	mach_port_t bootstrap_port = alloc_bootstrap_port();
589 	mach_port_t weak_service_port = alloc_weak_service_port();
590 
591 	/* Should succeed */
592 	kr = thread_set_exception_ports(
593 		mach_thread_self(),
594 		EXC_MASK_ALL,
595 		service_port,
596 		(exception_behavior_t)((unsigned int)EXCEPTION_STATE_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES),
597 		EXCEPTION_THREAD_STATE);
598 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "test_service_port_as_exception_port IOT_SERVICE_PORT");
599 
600 	/* Should succeed */
601 	kr = thread_set_exception_ports(
602 		mach_thread_self(),
603 		EXC_MASK_ALL,
604 		weak_service_port,
605 		(exception_behavior_t)((unsigned int)EXCEPTION_STATE_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES),
606 		EXCEPTION_THREAD_STATE);
607 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "test_service_port_as_exception_port IOT_WEAK_SERVICE_PORT");
608 
609 	/* Should fail, but no guard exception */
610 	kr = thread_set_exception_ports(
611 		mach_thread_self(),
612 		EXC_MASK_ALL,
613 		bootstrap_port,
614 		(exception_behavior_t)((unsigned int)EXCEPTION_STATE_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES),
615 		EXCEPTION_THREAD_STATE);
616 	T_QUIET; T_ASSERT_MACH_ERROR(kr, KERN_INVALID_RIGHT, "test_service_port_as_exception_port IOT_BOOTSTRAP_PORT");
617 }
618 
619 int
main(int argc,char * argv[])620 main(int argc, char *argv[])
621 {
622 	uint32_t my_csflags = 0;
623 	bool thirdparty_hardened = !strcmp(argv[0], "./reply_port_defense_client_3P_hardened");
624 	T_ASSERT_POSIX_ZERO(csops(getpid(), CS_OPS_STATUS, &my_csflags, sizeof(my_csflags)), NULL);
625 
626 	/* TODO add some sysctl which disabled platform binary bit here */
627 	if ((my_csflags & CS_PLATFORM_BINARY) == thirdparty_hardened) {
628 		printf("platform binary does not match expected\n");
629 		return -1;
630 	}
631 
632 
633 	void (*tests[MAX_TEST_NUM])(void) = {
634 		test_immovable_receive_right, /* 0 */
635 		test_using_send_right,        /* 1 */
636 		test_move_send_right,         /* 2 */
637 		test_make_send_once_right,    /* 3 */
638 		NULL, /* 4 */
639 		test_unentitled_thread_set_exception_ports, /* 5 */
640 		test_unentitled_thread_set_state, /* 6 */
641 		unentitled_set_exception_ports_pass,
642 		exception_ports_crash, /* 8 */
643 		kobject_reply_port_defense, /* 9 */
644 		test_alloc_weak_reply_port, /* 10 */
645 		test_move_service_port, /* 11 */
646 		test_mktimer_notification_policy, /* 12 */
647 		test_reply_port_port_destroyed_notification_policy, /* 13 */
648 		test_reply_port_no_senders_notification_policy, /* 14 */
649 		test_reply_port_insert_right_disallowed, /* 15 */
650 		test_mach_task_self_send_movability, /* 16 */
651 		test_task_self_movable_send, /* 17 */
652 		test_move_newly_constructed_port_immovable_send, /* 18 */
653 		test_move_special_reply_port, /* 19 */
654 		test_reply_port_header_disposition, /* 20 */
655 		test_service_port_as_exception_port, /* 21 */
656 	};
657 
658 	if (argc < 2) {
659 		printf("[reply_port_defense_client]: Specify a test to run.");
660 		exit(-1);
661 	}
662 
663 	int test_num = atoi(argv[1]);
664 	printf("[reply_port_defense_client]: My Pid: %d Test num: %d third_party_hardened: %s\n",
665 	    getpid(), test_num, thirdparty_hardened ? "yes" : "no");
666 	fflush(stdout);
667 	if (test_num >= 0 && test_num < MAX_TEST_NUM) {
668 		(*tests[test_num])();
669 	} else {
670 		printf("[reply_port_defense_client]: Invalid test num. Exiting...\n");
671 		exit(-1);
672 	}
673 	printf("Child exiting cleanly!!\n");
674 	fflush(stdout);
675 	// return 0;
676 	exit(0);
677 }
678