1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <darwintest.h>
5 #include <mach/mach.h>
6 #include <mach/mach_vm.h>
7 #include <sys/sysctl.h>
8 #include <spawn.h>
9 #include <signal.h>
10 #import <System/sys/codesign.h>
11
12 #define IKOT_TASK_CONTROL 2
13
14 T_GLOBAL_META(
15 T_META_NAMESPACE("xnu.ipc"),
16 T_META_RADAR_COMPONENT_NAME("xnu"),
17 T_META_RADAR_COMPONENT_VERSION("IPC"),
18 T_META_RUN_CONCURRENTLY(TRUE));
19
20 static void
test_extract_immovable_task_port(pid_t pid)21 test_extract_immovable_task_port(pid_t pid)
22 {
23 kern_return_t kr;
24 mach_port_t tport = MACH_PORT_NULL;
25 ipc_info_space_t space_info;
26 ipc_info_name_array_t table;
27 mach_msg_type_number_t tableCount;
28 ipc_info_tree_name_array_t tree; /* unused */
29 mach_msg_type_number_t treeCount; /* unused */
30
31 mach_port_t extracted;
32 mach_msg_type_name_t right;
33
34
35 kr = task_for_pid(mach_task_self(), pid, &tport);
36 T_EXPECT_MACH_SUCCESS(kr, "task_for_pid(), tport: 0x%x", tport);
37
38 T_LOG("Target pid: %d", pid);
39
40 if (pid == getpid()) {
41 /* self extraction should succeed */
42 kr = mach_port_extract_right(mach_task_self(), mach_task_self(), MACH_MSG_TYPE_COPY_SEND, &extracted, &right);
43 T_EXPECT_MACH_SUCCESS(kr, "mach_port_extract_right() on immovable port in current space should succeed");
44 } else {
45 unsigned int kotype = 0, kobject = 0;
46 mach_port_name_t tport_name = MACH_PORT_NULL;
47 int tport_idx = 0;
48 kr = mach_port_space_info(tport, &space_info, &table, &tableCount, &tree, &treeCount);
49 T_EXPECT_MACH_SUCCESS(kr, "mach_port_space_info()");
50
51 for (int i = 0; i < tableCount; i++) {
52 T_LOG("Searching for task port..name: 0x%x", table[i].iin_name);
53 kr = mach_port_kernel_object(tport, table[i].iin_name, &kotype, &kobject);
54 if (KERN_SUCCESS == kr && kotype == IKOT_TASK_CONTROL) {
55 tport_name = table[i].iin_name;
56 tport_idx = i;
57 break;
58 } else if (kr) {
59 T_LOG("mach_port_kernel_object() failed on name 0x%x, kr: 0x%x", table[i].iin_name, kr);
60 }
61 }
62
63 if (!tport_name) {
64 T_FAIL("Did not find task port in child's space");
65 }
66 T_LOG("Remote tport name: 0x%x", tport_name);
67 kr = mach_port_extract_right(tport, tport_name, MACH_MSG_TYPE_COPY_SEND, &extracted, &right);
68 T_EXPECT_EQ(kr, KERN_INVALID_CAPABILITY, "mach_port_extract_right() on immovable port in child's space should fail (no crash): 0x%x", kr);
69
70 T_LOG("Still alive after extract right..");
71
72 kr = mach_port_mod_refs(tport, tport_name, MACH_PORT_RIGHT_SEND, -table[tport_idx].iin_urefs);
73 T_EXPECT_EQ(kr, KERN_INVALID_CAPABILITY, "mach_port_mod_refs() on pinned port in child's space should fail (no crash): 0x%x", kr);
74
75 T_LOG("Still alive after deallocate..");
76 }
77 }
78
79 T_DECL(extract_right_soft_fail, "Immovable/pinned violation on foreign task's space should not crash caller",
80 T_META_CHECK_LEAKS(false))
81 {
82 uint32_t opts = 0;
83 size_t size = sizeof(&opts);
84 pid_t child_pid;
85 kern_return_t ret;
86 int status, fd[2], fd2[2];
87
88 T_LOG("Check if immovable control port has been enabled\n");
89 ret = sysctlbyname("kern.ipc_control_port_options", &opts, &size, NULL, 0);
90
91 if (!ret && (opts & 0x08) == 0) {
92 T_SKIP("1p immovable control port hard enforcement isn't enabled");
93 }
94
95 /* extracting mach_task_self() should succeed */
96 test_extract_immovable_task_port(getpid());
97
98 ret = pipe(fd);
99 T_EXPECT_NE(ret, -1, "pipe creation");
100
101 ret = pipe(fd2);
102 T_EXPECT_NE(ret, -1, "pipe creation2");
103
104 child_pid = fork();
105
106 if (child_pid < 0) {
107 T_FAIL("fork failed()");
108 }
109
110 if (child_pid == 0) {
111 char data[6];
112 close(fd[0]);
113 close(fd2[1]);
114 write(fd[1], "wakeup", 6); /* Sync point 1 */
115 close(fd[1]);
116
117 read(fd2[0], data, 6); /* Sync point 2 */
118 close(fd2[0]);
119 } else {
120 char data[6];
121 close(fd[1]);
122 close(fd2[0]);
123 read(fd[0], data, 6); /* Sync point 1 */
124 close(fd[0]);
125
126 /* extracting child's immovable task port should fail without crash */
127 test_extract_immovable_task_port(child_pid);
128
129 write(fd2[1], "wakeup", 6); /* Sync point 2 */
130 close(fd2[1]);
131
132 kill(child_pid, SIGKILL);
133 wait(&status);
134 }
135 }
136