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