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