xref: /xnu-12377.81.4/tests/ipc/port_type_policy.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 /*
2  * Copyright (c) 2025 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <darwintest.h>
30 #include <mach/mach.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <mach/mk_timer.h>
34 #include <sys/sysctl.h>
35 #include <sys/code_signing.h>
36 #include "ipc_utils.h"
37 
38 T_GLOBAL_META(
39 	T_META_NAMESPACE("xnu.ipc"),
40 	T_META_RADAR_COMPONENT_NAME("xnu"),
41 	T_META_RADAR_COMPONENT_VERSION("IPC"),
42 	T_META_TIMEOUT(10),
43 	T_META_IGNORECRASHES(".*port_type_policy.*"),
44 	T_META_RUN_CONCURRENTLY(TRUE));
45 
46 /* in xpc/launch_private.h */
47 #define XPC_DOMAIN_SYSTEM 1
48 #define XPC_DOMAIN_PORT 7
49 
50 #define countof(arr) (sizeof(arr) / sizeof((arr)[0]))
51 
52 static void
53 expect_sigkill(
54 	void (^fn)(void),
55 	const char *format_description, ...)
56 {
57 	char description[0x100];
58 
59 	va_list args;
60 	va_start(args, format_description);
61 	vsnprintf(description, sizeof(description), format_description, args);
62 	va_end(args);
63 
64 	pid_t pid = fork();
65 	T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "fork");
66 
67 	if (pid == 0) {
68 		fn();
69 		T_ASSERT_FAIL("%s: did not receive SIGKILL", description);
70 	} else {
71 		int status = 0;
72 		T_QUIET; T_ASSERT_POSIX_SUCCESS(waitpid(pid, &status, 0), "waitpid");
73 		T_EXPECT_EQ(WTERMSIG(status), SIGKILL,
74 		    "%s exited with %d, expect SIGKILL", description, WTERMSIG(status));
75 	}
76 }
77 
78 struct msg_complex_port {
79 	mach_msg_base_t         base;
80 	mach_msg_port_descriptor_t dsc;
81 	mach_msg_max_trailer_t  trailer;
82 };
83 
84 #define OOL_PORT_COUNTS 2
85 
86 struct msg_complex_port_array {
87 	mach_msg_base_t         base;
88 	mach_msg_ool_ports_descriptor_t dsc;
89 	mach_msg_max_trailer_t  trailer;
90 	mach_port_name_t        array[OOL_PORT_COUNTS];
91 };
92 
93 struct msg_complex_port_two_arrays {
94 	mach_msg_header_t header;
95 	mach_msg_base_t         base;
96 	mach_msg_ool_ports_descriptor_t dsc1;
97 	mach_msg_ool_ports_descriptor_t dsc2;
98 	mach_msg_max_trailer_t  trailer;
99 	mach_port_name_t        array[OOL_PORT_COUNTS];
100 };
101 
102 static kern_return_t
send_msg(mach_port_t dest_port,mach_msg_header_t * msg,mach_msg_size_t size)103 send_msg(
104 	mach_port_t       dest_port,
105 	mach_msg_header_t *msg,
106 	mach_msg_size_t   size)
107 {
108 	mach_msg_option64_t     opts;
109 
110 	opts = MACH64_SEND_MSG | MACH64_SEND_MQ_CALL | MACH64_SEND_TIMEOUT;
111 
112 	msg->msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MAKE_SEND, 0, 0,
113 	    MACH_MSGH_BITS_COMPLEX);
114 	msg->msgh_size = size;
115 	msg->msgh_remote_port = dest_port;
116 	msg->msgh_local_port = MACH_PORT_NULL;
117 	msg->msgh_voucher_port = MACH_PORT_NULL;
118 	msg->msgh_id = 42;
119 	return mach_msg2(msg, opts, *msg, size, 0, 0, 0, 0);
120 }
121 
122 static kern_return_t
send_port_descriptor(mach_port_t dest_port,mach_port_t dsc_port,int disp)123 send_port_descriptor(
124 	mach_port_t             dest_port,
125 	mach_port_t             dsc_port,
126 	int                     disp)
127 {
128 	struct msg_complex_port complex_msg;
129 	mach_msg_header_t *msg;
130 	mach_msg_size_t size;
131 
132 	complex_msg = (struct msg_complex_port){
133 		.base.body.msgh_descriptor_count = 1,
134 		.dsc = {
135 			.type        = MACH_MSG_PORT_DESCRIPTOR,
136 			.disposition = disp,
137 			.name        = dsc_port,
138 		},
139 	};
140 
141 	msg = &complex_msg.base.header;
142 	size = (mach_msg_size_t)((char *)&complex_msg.trailer - (char *)&complex_msg.base);
143 	return send_msg(dest_port, msg, size);
144 }
145 
146 static mach_port_t
recv_port_descriptor(mach_port_t dst_port)147 recv_port_descriptor(mach_port_t dst_port)
148 {
149 	struct msg_complex_port msg;
150 
151 	kern_return_t kr = mach_msg2(&msg, MACH64_RCV_MSG, MACH_MSG_HEADER_EMPTY,
152 	    0, sizeof(msg), dst_port, 0, 0);
153 	T_ASSERT_MACH_SUCCESS(kr, "mach_msg2 receive port descriptor");
154 
155 	/* extract and return the received port name */
156 	return msg.dsc.name;
157 }
158 
159 static mach_port_t
get_send_receive_right(void)160 get_send_receive_right(void)
161 {
162 	kern_return_t kr;
163 	mach_port_t port;
164 
165 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
166 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_allocate");
167 
168 	kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
169 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_insert_right");
170 
171 	return port;
172 }
173 
174 static kern_return_t
send_ool_port_array(mach_port_t dest_port,mach_msg_type_name_t disp)175 send_ool_port_array(
176 	mach_port_t dest_port,
177 	mach_msg_type_name_t disp)
178 {
179 	struct msg_complex_port_array complex_msg;
180 	mach_msg_header_t *msg;
181 	mach_msg_size_t size;
182 
183 	complex_msg = (struct msg_complex_port_array){
184 		.base.body.msgh_descriptor_count = 1,
185 		.dsc = {
186 			.type        = MACH_MSG_OOL_PORTS_DESCRIPTOR,
187 			.disposition = disp,
188 			.address     = &complex_msg.array,
189 			.count       = OOL_PORT_COUNTS,
190 			.deallocate  = false,
191 		},
192 	};
193 
194 	for (size_t i = 0; i < OOL_PORT_COUNTS; ++i) {
195 		complex_msg.array[i] = get_send_receive_right();
196 	}
197 
198 	msg = &complex_msg.base.header;
199 	size = (mach_msg_size_t)((char *)&complex_msg.trailer - (char *)&complex_msg.base);
200 	return send_msg(dest_port, msg, size);
201 }
202 
203 static kern_return_t
send_ool_port_multiple_arrays(mach_port_t dest_port,mach_msg_type_name_t disp)204 send_ool_port_multiple_arrays(
205 	mach_port_t dest_port,
206 	mach_msg_type_name_t disp)
207 {
208 	struct msg_complex_port_two_arrays complex_msg;
209 	mach_msg_header_t *msg;
210 	mach_msg_size_t size;
211 
212 	complex_msg = (struct msg_complex_port_two_arrays){
213 		.base.body.msgh_descriptor_count = 2,
214 		.dsc1 = {
215 			.type        = MACH_MSG_OOL_PORTS_DESCRIPTOR,
216 			.disposition = disp,
217 			.address     = &complex_msg.array,
218 			.count       = OOL_PORT_COUNTS,
219 			.deallocate  = false,
220 		},
221 		.dsc2 = {
222 			.type        = MACH_MSG_OOL_PORTS_DESCRIPTOR,
223 			.disposition = disp,
224 			.address     = &complex_msg.array,
225 			.count       = OOL_PORT_COUNTS,
226 			.deallocate  = false,
227 		},
228 	};
229 
230 	for (size_t i = 0; i < OOL_PORT_COUNTS; ++i) {
231 		complex_msg.array[i] = get_send_receive_right();
232 	}
233 
234 	msg = &complex_msg.base.header;
235 	size = (mach_msg_size_t)((char *)&complex_msg.trailer - (char *)&complex_msg.base);
236 	return send_msg(dest_port, msg, size);
237 }
238 
239 /*
240  * Helper constructor functions to create different types of ports.
241  */
242 static mach_port_t
create_conn_with_port_array_port(void)243 create_conn_with_port_array_port(void)
244 {
245 	kern_return_t kr;
246 	mach_port_t port;
247 
248 	mach_port_options_t opts = {.flags = MPO_CONNECTION_PORT_WITH_PORT_ARRAY, };
249 
250 	kr = mach_port_construct(mach_task_self(), &opts, 0x0, &port);
251 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
252 
253 	return port;
254 }
255 
256 static mach_port_t
create_exception_port(void)257 create_exception_port(void)
258 {
259 	kern_return_t kr;
260 	mach_port_t port;
261 
262 	mach_port_options_t opts = {.flags = MPO_EXCEPTION_PORT, };
263 
264 	kr = mach_port_construct(mach_task_self(), &opts, 0x0, &port);
265 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
266 
267 	return port;
268 }
269 
270 static mach_port_t
create_connection_port(void)271 create_connection_port(void)
272 {
273 	kern_return_t kr;
274 	mach_port_t conn_port;
275 
276 	mach_port_options_t opts = {
277 		.flags = MPO_CONNECTION_PORT | MPO_INSERT_SEND_RIGHT,
278 		.service_port_name = MPO_ANONYMOUS_SERVICE,
279 	};
280 
281 	kr = mach_port_construct(mach_task_self(), &opts, 0x0, &conn_port);
282 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
283 
284 	return conn_port;
285 }
286 
287 static mach_port_t
create_reply_port(void)288 create_reply_port(void)
289 {
290 	kern_return_t kr;
291 	mach_port_t port;
292 
293 	mach_port_options_t opts = {
294 		.flags = MPO_REPLY_PORT,
295 	};
296 
297 	kr = mach_port_construct(mach_task_self(), &opts, 0x0, &port);
298 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
299 
300 	return port;
301 }
302 
303 static mach_port_t
create_provisional_reply_port(void)304 create_provisional_reply_port(void)
305 {
306 	kern_return_t kr;
307 	mach_port_t port;
308 
309 	mach_port_options_t opts = {
310 		.flags = MPO_PROVISIONAL_REPLY_PORT,
311 	};
312 
313 	kr = mach_port_construct(mach_task_self(), &opts, 0x0, &port);
314 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
315 
316 	return port;
317 }
318 
319 static mach_port_t
create_service_port(void)320 create_service_port(void)
321 {
322 	kern_return_t kr;
323 	mach_port_t port;
324 
325 	struct mach_service_port_info sp_info = {
326 		.mspi_string_name = "com.apple.testservice",
327 		.mspi_domain_type = XPC_DOMAIN_SYSTEM,
328 	};
329 
330 	mach_port_options_t opts = {
331 		.flags = MPO_STRICT_SERVICE_PORT,
332 		.service_port_info = &sp_info,
333 	};
334 
335 	kr = mach_port_construct(mach_task_self(), &opts, 0x0, &port);
336 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
337 
338 	return port;
339 }
340 
341 static mach_port_t
create_bootstrap_port(void)342 create_bootstrap_port(void)
343 {
344 	kern_return_t kr;
345 	mach_port_t port;
346 
347 	struct mach_service_port_info sp_info = {
348 		.mspi_string_name = "com.apple.testservice",
349 		.mspi_domain_type = XPC_DOMAIN_PORT,
350 	};
351 
352 	mach_port_options_t opts = {
353 		.flags = MPO_STRICT_SERVICE_PORT,
354 		.service_port_info = &sp_info,
355 	};
356 
357 	kr = mach_port_construct(mach_task_self(), &opts, 0x0, &port);
358 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
359 
360 	return port;
361 }
362 
363 static void
destruct_generic_port(mach_port_t port)364 destruct_generic_port(mach_port_t port)
365 {
366 	kern_return_t kr;
367 	mach_port_type_t type = 0;
368 
369 	kr = mach_port_type(mach_task_self(), port, &type);
370 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_type");
371 
372 	kr = mach_port_destruct(mach_task_self(),
373 	    port,
374 	    (type & MACH_PORT_TYPE_SEND) ? -1 : 0,
375 	    0);
376 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_destruct");
377 }
378 /*
379  * Helper functions and types to help making test output nice and readable.
380  */
381 static const char*
get_disp_name(mach_msg_type_name_t disp)382 get_disp_name(mach_msg_type_name_t disp)
383 {
384 	switch (disp) {
385 	case MACH_MSG_TYPE_MOVE_SEND:
386 		return "MOVE_SEND";
387 	case MACH_MSG_TYPE_MAKE_SEND:
388 		return "MAKE_SEND";
389 	case MACH_MSG_TYPE_MOVE_SEND_ONCE:
390 		return "MOVE_SEND_ONCE";
391 	case MACH_MSG_TYPE_MAKE_SEND_ONCE:
392 		return "MAKE_SEND_ONCE";
393 	case MACH_MSG_TYPE_COPY_SEND:
394 		return "COPY_SEND";
395 	case MACH_MSG_TYPE_MOVE_RECEIVE:
396 		return "MOVE_RECEIVE";
397 	default:
398 		T_ASSERT_FAIL("Invalid disp");
399 	}
400 }
401 
402 static const char*
get_notification_name(mach_msg_id_t notification_id)403 get_notification_name(mach_msg_id_t notification_id)
404 {
405 	switch (notification_id) {
406 	case MACH_NOTIFY_PORT_DESTROYED:
407 		return "PORT_DESTROY";
408 		break;
409 	case MACH_NOTIFY_NO_SENDERS:
410 		return "NO_MORE_SENDERS";
411 		break;
412 	case MACH_NOTIFY_SEND_POSSIBLE:
413 		return "SEND_POSSIBLE";
414 		break;
415 	default:
416 		T_ASSERT_FAIL("Invalid notification id");
417 	}
418 }
419 
420 typedef struct {
421 	mach_port_t (*port_ctor)(void);
422 	char *port_type_name;
423 	bool is_reply_port;
424 } port_type_desc;
425 
426 const port_type_desc IOT_PORT_DESC = {
427 	.port_ctor = get_send_receive_right,
428 	.port_type_name = "IOT_PORT",
429 	.is_reply_port = false,
430 };
431 const port_type_desc REPLY_PORT_DESC = {
432 	.port_ctor = create_reply_port,
433 	.port_type_name = "IOT_REPLY_PORT",
434 	.is_reply_port = true,
435 };
436 const port_type_desc CONNECTION_PORT_DESC = {
437 	.port_ctor = create_connection_port,
438 	.port_type_name = "IOT_CONNECTION_PORT",
439 	.is_reply_port = false,
440 };
441 const port_type_desc EXCEPTION_PORT_DESC = {
442 	.port_ctor = create_exception_port,
443 	.port_type_name = "IOT_EXCEPTION_PORT",
444 	.is_reply_port = false,
445 };
446 const port_type_desc PROVISIONAL_REPLY_PORT_DESC = {
447 	.port_ctor = create_provisional_reply_port,
448 	.port_type_name = "IOT_PROVISIONAL_REPLY_PORT",
449 	.is_reply_port = false,
450 };
451 const port_type_desc CONNECTION_PORT_WITH_PORT_ARRAY_DESC = {
452 	.port_ctor = create_conn_with_port_array_port,
453 	.port_type_name = "IOT_CONNECTION_PORT_WITH_PORT_ARRAY",
454 	.is_reply_port = false,
455 };
456 const port_type_desc TIMER_PORT_DESC = {
457 	.port_ctor = mk_timer_create,
458 	.port_type_name = "IOT_TIMER_PORT",
459 	.is_reply_port = false,
460 };
461 const port_type_desc SPECIAL_REPLY_PORT_DESC = {
462 	.port_ctor = thread_get_special_reply_port,
463 	.port_type_name = "IOT_SPECIAL_REPLY_PORT",
464 	.is_reply_port = true,
465 };
466 const port_type_desc SERVICE_PORT_DESC = {
467 	.port_ctor = create_service_port,
468 	.port_type_name = "IOT_SERVICE_PORT",
469 	.is_reply_port = false,
470 };
471 const port_type_desc BOOTSTRAP_PORT_DESC = {
472 	.port_ctor = create_bootstrap_port,
473 	.port_type_name = "IOT_BOOTSTRAP_PORT",
474 	.is_reply_port = false,
475 };
476 
477 const port_type_desc PORT_TYPE_DESC_ARRAY[] = {
478 	IOT_PORT_DESC,
479 	REPLY_PORT_DESC,
480 	CONNECTION_PORT_DESC,
481 	EXCEPTION_PORT_DESC,
482 	PROVISIONAL_REPLY_PORT_DESC,
483 	CONNECTION_PORT_WITH_PORT_ARRAY_DESC,
484 	TIMER_PORT_DESC,
485 	SPECIAL_REPLY_PORT_DESC,
486 	SERVICE_PORT_DESC,
487 	BOOTSTRAP_PORT_DESC
488 };
489 
490 /*
491  * Helper functions to test MachIPC functionalities.
492  */
493 static void
test_disallowed_register_mach_notification(const port_type_desc * port_desc,mach_msg_id_t notify_id)494 test_disallowed_register_mach_notification(
495 	const port_type_desc *port_desc,
496 	mach_msg_id_t notify_id)
497 {
498 	expect_sigkill(^{
499 		mach_port_t port, notify_port, previous;
500 
501 		/* construct a receive right to send the port as descriptor to */
502 		notify_port = get_send_receive_right();
503 
504 		port = port_desc->port_ctor();
505 		(void)mach_port_request_notification(mach_task_self(),
506 		port,
507 		notify_id,
508 		0,
509 		notify_port,
510 		MACH_MSG_TYPE_MAKE_SEND_ONCE,
511 		&previous);
512 
513 		/* Unreachable; ports will be destructed when IPC space is destroyed */
514 	}, "%s failed with mach notification %s", port_desc->port_type_name, get_notification_name(notify_id));
515 }
516 
517 /*
518  * In this helper function we cover two properties:
519  *     - we make sure these ports are immovable-receive by trying to
520  *       send them in a message with MACH_MSG_PORT_DESCRIPTOR descriptor;
521  *     - we attempt to register them for a PD notification.
522  *
523  * This seems redundent since it is not possible to register immovable-receive
524  * ports to PD notification by construction. However, we want our tests
525  * to cover everything, and this link between immovable-receive and
526  * PD notifications, no matter how trivial, should be question as well.
527  *
528  * Note: this intentionally does NOT use get status trap
529  *       and test for MACH_PORT_STATUS_FLAG_GUARD_IMMOVABLE_RECEIVE,
530  *       because the purpose of these tests is to ensure the overall security
531  *       properties are respected (immovability, Guard, fatal exception, etc.).
532  */
533 static void
test_receive_immovability(const port_type_desc * port_desc)534 test_receive_immovability(const port_type_desc *port_desc)
535 {
536 	expect_sigkill(^{
537 		mach_port_t dst_port, port;
538 
539 		/* construct a receive right to send the port as descriptor to */
540 		dst_port = get_send_receive_right();
541 
542 		/*
543 		 * construct the port to test immovability, and send it as port
544 		 * descriptor with RECEIVE right.
545 		 */
546 		port = port_desc->port_ctor();
547 		(void)send_port_descriptor(dst_port, port, MACH_MSG_TYPE_MOVE_RECEIVE);
548 
549 		/* Unreachable; ports will be destructed when IPC space is destroyed */
550 	}, "%s failed immovable-receive", port_desc->port_type_name);
551 
552 	test_disallowed_register_mach_notification(port_desc,
553 	    MACH_NOTIFY_PORT_DESTROYED);
554 }
555 
556 /*
557  * We have port types which their receive right is allowed to be move
558  * ONCE, and then they become immovable-receive for the rest of their
559  * lifetime.
560  *
561  * This helper function tests that property.
562  */
563 static void
test_receive_immovability_move_once(const port_type_desc * port_desc)564 test_receive_immovability_move_once(const port_type_desc *port_desc)
565 {
566 	expect_sigkill(^{
567 		kern_return_t kr;
568 		mach_port_t dst_port, port;
569 
570 		/* construct a receive right to send the port as descriptor to */
571 		dst_port = get_send_receive_right();
572 
573 		/* construct the port for our test, and send it as port descriptor */
574 		port = port_desc->port_ctor();
575 		kr = send_port_descriptor(dst_port, port, MACH_MSG_TYPE_MOVE_RECEIVE);
576 		T_ASSERT_MACH_SUCCESS(kr, "send_port_descriptor");
577 
578 		/* we moved the receive right out of our IPC space */
579 		port = MACH_PORT_NULL;
580 
581 		/*
582 		 * receive the port we sent to ourselves.
583 		 *
584 		 * From now on, this port is expected to be immovable-receive
585 		 * for the rest of its lifetime.
586 		 */
587 		port = recv_port_descriptor(dst_port);
588 
589 		/*
590 		 * this should raise a fatal Guard exception
591 		 * on immovability violation
592 		 */
593 		(void)send_port_descriptor(dst_port, port, MACH_MSG_TYPE_MOVE_RECEIVE);
594 
595 		/* Unreachable; ports will be destructed when IPC space is destroyed */
596 	}, "%s is allowed to be move ONCE", port_desc->port_type_name);
597 }
598 
599 static void
test_send_immovability_move_so(const port_type_desc * port_desc)600 test_send_immovability_move_so(const port_type_desc *port_desc)
601 {
602 	expect_sigkill(^{
603 		mach_port_t dst_port, port, so_right;
604 		mach_msg_type_name_t disp;
605 		kern_return_t kr;
606 
607 		dst_port = get_send_receive_right();
608 		port = port_desc->port_ctor();
609 
610 		/* create a send-once right for the port */
611 		kr = mach_port_extract_right(mach_task_self(), port,
612 		MACH_MSG_TYPE_MAKE_SEND_ONCE, &so_right, &disp);
613 
614 		T_ASSERT_MACH_SUCCESS(kr, "mach_port_extract_right with %s", port_desc->port_type_name);
615 
616 		(void)send_port_descriptor(dst_port, so_right, MACH_MSG_TYPE_MOVE_SEND_ONCE);
617 
618 		/* Unreachable; ports will be destructed when IPC space is destroyed */
619 	}, "%s immovable-send failed with MOVE_SEND_ONCE", port_desc->port_type_name);
620 }
621 
622 static void
test_send_immovability(const port_type_desc * port_desc)623 test_send_immovability(const port_type_desc *port_desc)
624 {
625 	expect_sigkill(^{
626 		mach_msg_type_name_t disp;
627 		mach_port_name_t name;
628 
629 		mach_port_t port = port_desc->port_ctor();
630 		(void)mach_port_extract_right(mach_task_self(), port,
631 		MACH_MSG_TYPE_MOVE_SEND, &name, &disp);
632 
633 		/* Unreachable; ports will be destructed when IPC space is destroyed */
634 	}, "%s immovable-send failed extract_right MOVE_SEND", port_desc->port_type_name);
635 
636 	expect_sigkill(^{
637 		mach_port_t dst_port, port;
638 
639 		/* construct a receive right to send the port as descriptor to */
640 		dst_port = get_send_receive_right();
641 
642 		port = port_desc->port_ctor();
643 		(void)send_port_descriptor(dst_port, port, MACH_MSG_TYPE_MOVE_SEND);
644 
645 		/* Unreachable; ports will be destructed when IPC space is destroyed */
646 	}, "%s immovable-send failed with MOVE_SEND", port_desc->port_type_name);
647 
648 	expect_sigkill(^{
649 		mach_port_t dst_port, port;
650 
651 		/* construct a receive right to send the port as descriptor to */
652 		dst_port = get_send_receive_right();
653 
654 		port = port_desc->port_ctor();
655 		(void)send_port_descriptor(dst_port, port, MACH_MSG_TYPE_COPY_SEND);
656 
657 		/* Unreachable; ports will be destructed when IPC space is destroyed */
658 	}, "%s immovable-send failed with COPY_SEND", port_desc->port_type_name);
659 
660 	/*
661 	 * Do not attempt to extract SEND_ONCE for reply port types. Such behavior
662 	 * should be covered by the reply_port_defense test.
663 	 */
664 	if (!port_desc->is_reply_port) {
665 		test_send_immovability_move_so(port_desc);
666 	}
667 }
668 
669 static void
test_ool_port_array(const port_type_desc * port_desc,mach_msg_type_name_t disp)670 test_ool_port_array(
671 	const port_type_desc *port_desc,
672 	mach_msg_type_name_t disp)
673 {
674 	expect_sigkill(^{
675 		mach_port_t dst_port;
676 
677 		/* construct a receive right to send the port as descriptor to */
678 		dst_port = port_desc->port_ctor();
679 
680 		(void)send_ool_port_array(dst_port, disp);
681 
682 		/* Unreachable; ports will be destructed when IPC space is destroyed */
683 	}, "sending OOL port array to %s with %s", port_desc->port_type_name, get_disp_name(disp));
684 }
685 
686 /*
687  * Because of mach hardening opt out, group
688  * reply port tests together and skip them.
689  */
690 T_DECL(reply_port_policies,
691     "Reply port policies tests") {
692 #if TARGET_OS_OSX || TARGET_OS_BRIDGE
693 	T_SKIP("Test disabled on macOS due to mach hardening opt out");
694 #endif /* TARGET_OS_OSX || TARGET_OS_BRIDGE */
695 
696 	test_receive_immovability(&REPLY_PORT_DESC);
697 
698 	test_send_immovability(&REPLY_PORT_DESC);
699 
700 	test_disallowed_register_mach_notification(&REPLY_PORT_DESC,
701 	    MACH_NOTIFY_NO_SENDERS);
702 }
703 
704 T_DECL(immovable_receive_port_types,
705     "Port types we expect to be immovable-receive") {
706 	test_receive_immovability(&CONNECTION_PORT_WITH_PORT_ARRAY_DESC);
707 
708 	test_receive_immovability(&EXCEPTION_PORT_DESC);
709 
710 	test_receive_immovability(&TIMER_PORT_DESC);
711 
712 	test_receive_immovability(&SPECIAL_REPLY_PORT_DESC);
713 
714 	/*
715 	 * kGUARD_EXC_KERN_FAILURE is not fatal on Bridge OS because
716 	 * we don't set TASK_EXC_GUARD_MP_FATAL by default/
717 	 */
718 #if !TARGET_OS_BRIDGE
719 	test_receive_immovability(&SERVICE_PORT_DESC);
720 #endif /* !TARGET_OS_BRIDGE */
721 
722 	test_receive_immovability(&BOOTSTRAP_PORT_DESC);
723 }
724 
725 T_DECL(immovable_receive_move_once_port_types,
726     "Port types we expect to be immovable-receive") {
727 	test_receive_immovability_move_once(&CONNECTION_PORT_DESC);
728 }
729 
730 T_DECL(immovable_send_port_types,
731     "Port types we expect to be immovable-send")
732 {
733 	test_send_immovability(&CONNECTION_PORT_DESC);
734 
735 	test_send_immovability(&SPECIAL_REPLY_PORT_DESC);
736 }
737 
738 T_DECL(ool_port_array_policies,
739     "OOL port array policies")
740 {
741 #if TARGET_OS_VISION
742 	T_SKIP("OOL port array enforcement is disabled");
743 #else
744 	if (ipc_hardening_disabled()) {
745 		T_SKIP("hardening disabled due to boot-args");
746 	}
747 
748 	/*
749 	 * The only port type allowed to receive the MACH_MSG_OOL_PORTS_DESCRIPTOR
750 	 * descriptor is IOT_CONNECTION_PORT_WITH_PORT_ARRAY.
751 	 *
752 	 * Attempt sending MACH_MSG_OOL_PORTS_DESCRIPTOR to any other port type
753 	 * result in a fatal Guard exception.
754 	 */
755 	for (uint32_t i = 0; i < countof(PORT_TYPE_DESC_ARRAY); ++i) {
756 		if (PORT_TYPE_DESC_ARRAY[i].port_ctor == create_conn_with_port_array_port) {
757 			continue;
758 		}
759 
760 		test_ool_port_array((PORT_TYPE_DESC_ARRAY + i),
761 		    MACH_MSG_TYPE_COPY_SEND);
762 	}
763 
764 	/*
765 	 * Now try to send to IOT_CONNECTION_PORT_WITH_PORT_ARRAY ports,
766 	 * but use disallowed dispositions.
767 	 *
768 	 * The only allowed disposition is COPY_SEND.
769 	 */
770 	test_ool_port_array(&CONNECTION_PORT_WITH_PORT_ARRAY_DESC,
771 	    MACH_MSG_TYPE_MOVE_SEND);
772 
773 	test_ool_port_array(&CONNECTION_PORT_WITH_PORT_ARRAY_DESC,
774 	    MACH_MSG_TYPE_MAKE_SEND);
775 
776 	test_ool_port_array(&CONNECTION_PORT_WITH_PORT_ARRAY_DESC,
777 	    MACH_MSG_TYPE_MOVE_SEND_ONCE);
778 
779 	test_ool_port_array(&CONNECTION_PORT_WITH_PORT_ARRAY_DESC,
780 	    MACH_MSG_TYPE_MAKE_SEND_ONCE);
781 
782 	test_ool_port_array(&CONNECTION_PORT_WITH_PORT_ARRAY_DESC,
783 	    MACH_MSG_TYPE_MOVE_RECEIVE);
784 
785 	/*
786 	 * Finally, try sending OOL port array to IOT_CONNECTION_PORT_WITH_PORT_ARRAY,
787 	 * with (the only) allowed disposition, but send two arrays in one kmsg.
788 	 */
789 	expect_sigkill(^{
790 		mach_port_t dst_port;
791 
792 		/* construct a receive right to send the port as descriptor to */
793 		dst_port = create_conn_with_port_array_port();
794 
795 		(void)send_ool_port_multiple_arrays(dst_port, MACH_MSG_TYPE_COPY_SEND);
796 
797 		/* Unreachable; ports will be destructed when IPC space is destroyed */
798 	}, "sending two OOL port arrays");
799 #endif /* TARGET_OS_VISION */
800 }
801 
802 T_DECL(disallowed_no_more_senders_port_destroy_port_types,
803     "Port types we disallow no-more-senders notifications for")
804 {
805 	test_disallowed_register_mach_notification(&SPECIAL_REPLY_PORT_DESC,
806 	    MACH_NOTIFY_NO_SENDERS);
807 }
808 
809 T_DECL(provisional_reply_port,
810     "Provisional reply ports have no restrictions")
811 {
812 	mach_port_t prp, remote_port, recv_port;
813 	kern_return_t kr;
814 
815 	prp = create_provisional_reply_port();
816 	remote_port = get_send_receive_right();
817 
818 	kr = mach_port_insert_right(mach_task_self(), prp, prp,
819 	    MACH_MSG_TYPE_MAKE_SEND);
820 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_insert_right");
821 
822 	/* send a send right to the provisional reply port*/
823 	kr = send_port_descriptor(remote_port, prp, MACH_MSG_TYPE_MOVE_SEND);
824 	T_ASSERT_MACH_SUCCESS(kr, "send_port_descriptor");
825 
826 	/* receive that port descriptor, which has to have the same name */
827 	recv_port = recv_port_descriptor(remote_port);
828 	T_QUIET; T_ASSERT_EQ(prp, recv_port, "recv_port_descriptor send");
829 
830 	/* drop only the send right of the provisional reply port */
831 	kr = mach_port_mod_refs(mach_task_self(), prp, MACH_PORT_RIGHT_SEND, -1);
832 
833 	/*
834 	 * Do not move receive right of a provisional reply port as
835 	 * that triggers a non-fatal guard exception
836 	 */
837 
838 	/* cleanup, destruct the ports we used */
839 	kr = mach_port_destruct(mach_task_self(), recv_port, 0, 0);
840 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_destruct recv_port");
841 
842 	kr = mach_port_destruct(mach_task_self(), remote_port, 0, 0);
843 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_destruct remote_port");
844 }
845 
846 T_DECL(mktimer_traps,
847     "Test mktimer traps")
848 {
849 	kern_return_t kr;
850 	mach_port_t port;
851 	uint64_t result_time;
852 
853 	/*
854 	 * Enumerate all port types, makes sure mk_timer_arm
855 	 * fails on every single one besides IOT_TIMER_PORT
856 	 */
857 	for (uint32_t i = 0; i < countof(PORT_TYPE_DESC_ARRAY); ++i) {
858 		if (PORT_TYPE_DESC_ARRAY[i].port_ctor == mk_timer_create) {
859 			continue;
860 		}
861 
862 		/* Create a non-timer port type */
863 		port = PORT_TYPE_DESC_ARRAY[i].port_ctor();
864 		T_QUIET; T_ASSERT_NE(port, MACH_PORT_NULL,
865 		    "constructing a port type %s",
866 		    PORT_TYPE_DESC_ARRAY[i].port_type_name);
867 
868 		kr = mk_timer_arm(port, 1);
869 		T_ASSERT_MACH_ERROR(kr,
870 		    KERN_INVALID_ARGUMENT,
871 		    "mk_timer_arm failed on non timer port type (%s)",
872 		    PORT_TYPE_DESC_ARRAY[i].port_type_name);
873 
874 		kr = mk_timer_cancel(port, &result_time);
875 		T_ASSERT_MACH_ERROR(kr,
876 		    KERN_INVALID_ARGUMENT,
877 		    "mk_timer_cancel failed on non timer port type (%s)",
878 		    PORT_TYPE_DESC_ARRAY[i].port_type_name);
879 
880 		kr = mk_timer_destroy(port);
881 		T_ASSERT_MACH_ERROR(kr,
882 		    KERN_INVALID_ARGUMENT,
883 		    "mk_timer_destroy failed on non timer port type (%s)",
884 		    PORT_TYPE_DESC_ARRAY[i].port_type_name);
885 
886 		/* Destroy the port we created */
887 		destruct_generic_port(port);
888 	}
889 
890 	/* Verify mk_timer_arm succeed with actual timer */
891 	port = TIMER_PORT_DESC.port_ctor();
892 	T_QUIET; T_ASSERT_NE(port, MACH_PORT_NULL,
893 	    "constructing a timer (%s)",
894 	    TIMER_PORT_DESC.port_type_name);
895 
896 	kr = mk_timer_arm(port, 1);
897 	T_ASSERT_MACH_SUCCESS(kr, "mk_timer_arm on actual timer");
898 
899 	kr = mk_timer_cancel(port, &result_time);
900 	T_ASSERT_MACH_SUCCESS(kr, "mk_timer_cancel on actual timer");
901 
902 	kr = mk_timer_destroy(port);
903 	T_ASSERT_MACH_SUCCESS(kr, "mk_timer_destroy");
904 }
905