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