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