1 #include <darwintest.h>
2 #include <darwintest_utils.h>
3
4 #include <string.h>
5 #include <sys/types.h>
6 #include <sys/sysctl.h>
7 #include <mach/mach.h>
8 #include <mach/mach_vm.h>
9 #include <mach/vm_types.h>
10 #include <sys/mman.h>
11 #include <unistd.h>
12 #include <TargetConditionals.h>
13
14 T_GLOBAL_META(
15 T_META_NAMESPACE("xnu.vm"),
16 T_META_RADAR_COMPONENT_NAME("xnu"),
17 T_META_RADAR_COMPONENT_VERSION("VM")
18 );
19
20 struct child_rc {
21 int ret;
22 int sig;
23 };
24
25 static struct child_rc
26 fork_child_test(void (^block)(void))
27 {
28 struct child_rc rc = { };
29 pid_t child_pid;
30
31 child_pid = fork();
32
33 if (child_pid == 0) {
34 block();
35 exit(0);
36 }
37
38 T_QUIET; T_ASSERT_POSIX_SUCCESS(child_pid, "fork process");
39
40 /* wait for child process to exit */
41 dt_waitpid(child_pid, &rc.ret, &rc.sig, 30);
42 return rc;
43 }
44
45 static mach_vm_address_t
get_permanent_mapping(mach_vm_size_t size)46 get_permanent_mapping(mach_vm_size_t size)
47 {
48 kern_return_t kr;
49 mach_vm_address_t addr;
50
51 kr = mach_vm_allocate(mach_task_self(), &addr, size,
52 VM_FLAGS_ANYWHERE | VM_FLAGS_PERMANENT);
53
54 T_ASSERT_MACH_SUCCESS(kr, "mach_vm_allocate(%lld, PERMANENT) == %p",
55 size, (void *)addr);
56
57 *(int *)addr = 42;
58
59 kr = mach_vm_protect(mach_task_self(), addr, size, FALSE, VM_PROT_READ);
60
61 T_EXPECT_MACH_SUCCESS(kr, "mach_vm_protect(PERMANENT, READ)");
62
63 T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
64
65 return addr;
66 }
67
68 T_DECL(permanent_mapping, "check permanent mappings semantics", T_META_TAG_VM_PREFERRED)
69 {
70 mach_vm_size_t size = 1 << 20;
71 struct child_rc rc;
72
73 T_LOG("try to bypass permanent mappings with VM_FLAGS_OVERWRITE");
74 rc = fork_child_test(^{
75 mach_vm_address_t addr, addr2;
76 kern_return_t kr2;
77
78 addr = get_permanent_mapping(size);
79
80 T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
81
82 addr2 = addr;
83 kr2 = mach_vm_allocate(mach_task_self(), &addr2, size,
84 VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE);
85
86 /*
87 * because the permanent mapping wasn't removed,
88 * we should get an error.
89 */
90 T_ASSERT_MACH_ERROR(kr2, KERN_NO_SPACE,
91 "mach_vm_allocate(VM_FLAGS_OVERWRITE)");
92
93 /*
94 * because the permanent mapping was neutered,
95 * accessing it should crash.
96 */
97 T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
98 });
99 T_EXPECT_EQ(rc.sig, SIGBUS, "accessing the mapping caused a SIGBUS");
100
101 T_LOG("try to bypass permanent mappings with a VM_PROT_COPY mprotect");
102 rc = fork_child_test(^{
103 kern_return_t kr2;
104 mach_vm_address_t addr;
105
106 addr = get_permanent_mapping(size);
107
108 T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
109
110 kr2 = mach_vm_protect(mach_task_self(), addr, size, TRUE,
111 VM_PROT_COPY | VM_PROT_DEFAULT);
112
113 /*
114 * because the permanent mapping wasn't removed,
115 * we should get an error.
116 */
117 T_ASSERT_MACH_ERROR(kr2, KERN_NO_SPACE,
118 "mach_vm_protect(VM_PROT_COPY)");
119
120 /*
121 * because the permanent mapping was neutered,
122 * accessing it should crash.
123 */
124 T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
125 });
126 T_EXPECT_EQ(rc.sig, SIGBUS, "accessing the mapping caused a SIGBUS");
127
128 T_LOG("try to bypass permanent mappings with a vm_remap");
129 rc = fork_child_test(^{
130 kern_return_t kr2;
131 mach_vm_address_t addr, remap_addr, addr2;
132 vm_prot_t cur_prot, max_prot;
133
134 addr = get_permanent_mapping(size);
135
136 T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
137
138 addr2 = 0;
139 kr2 = mach_vm_allocate(mach_task_self(), &addr2, size,
140 VM_FLAGS_ANYWHERE);
141 T_QUIET; T_EXPECT_MACH_SUCCESS(kr2, "vm_allocate()");
142
143 remap_addr = addr;
144 kr2 = mach_vm_remap(mach_task_self(), &remap_addr, size, 0,
145 VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
146 mach_task_self(), addr2, TRUE,
147 &cur_prot, &max_prot, VM_INHERIT_DEFAULT);
148
149 /*
150 * because the permanent mapping wasn't removed,
151 * we should get an error.
152 */
153 T_ASSERT_MACH_ERROR(kr2, KERN_NO_SPACE,
154 "mach_vm_remap()");
155
156 /*
157 * because the permanent mapping was neutered,
158 * accessing it should crash.
159 */
160 T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
161 });
162 T_EXPECT_EQ(rc.sig, SIGBUS, "accessing the mapping caused a SIGBUS");
163
164 T_LOG("try to bypass permanent mappings with a vm_deallocate");
165 rc = fork_child_test(^{
166 kern_return_t kr2;
167 mach_vm_address_t addr;
168
169 addr = get_permanent_mapping(size);
170
171 T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
172
173 kr2 = mach_vm_deallocate(mach_task_self(), addr, size);
174
175 /*
176 * the permanent mapping wasn't removed but was made
177 * inaccessible; we should not get an error.
178 */
179 T_ASSERT_MACH_SUCCESS(kr2, "mach_vm_deallocate()");
180
181 /*
182 * because the permanent mapping was neutered,
183 * accessing it should crash.
184 */
185 T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
186 });
187 T_EXPECT_EQ(rc.sig, SIGBUS, "accessing the mapping caused a SIGBUS");
188 }
189
190 T_DECL(vm_tag_describe,
191 "test mach_vm_tag_describe()",
192 T_META_TAG_VM_PREFERRED)
193 {
194 for (unsigned int i = 0; i <= VM_MEMORY_COUNT; i++) {
195 const char *desc = mach_vm_tag_describe(i);
196 T_LOG("%i: %s", i, desc);
197 T_ASSERT_NOTNULL(desc, "Tag description (%i) is non-null", i);
198 T_EXPECT_NE_STR(desc, "", "Tag description (%i) is non-empty", i);
199 T_EXPECT_LE(strlen(desc), 24UL, "Tag description must be less than 24 characters");
200 }
201 }
202