1 #include <darwintest.h>
2 #include <darwintest_utils.h>
3
4 #include <sys/types.h>
5 #include <sys/sysctl.h>
6 #include <mach/mach.h>
7 #include <mach/mach_vm.h>
8 #include <mach/vm_types.h>
9 #include <sys/mman.h>
10 #include <unistd.h>
11 #include <TargetConditionals.h>
12
13 T_GLOBAL_META(
14 T_META_NAMESPACE("xnu.vm"),
15 T_META_RADAR_COMPONENT_NAME("xnu"),
16 T_META_RADAR_COMPONENT_VERSION("VM")
17 );
18
19 struct child_rc {
20 int ret;
21 int sig;
22 };
23
24 static struct child_rc
25 fork_child_test(void (^block)(void))
26 {
27 struct child_rc rc = { };
28 pid_t child_pid;
29
30 child_pid = fork();
31
32 if (child_pid == 0) {
33 block();
34 exit(0);
35 }
36
37 T_QUIET; T_ASSERT_POSIX_SUCCESS(child_pid, "fork process");
38
39 /* wait for child process to exit */
40 dt_waitpid(child_pid, &rc.ret, &rc.sig, 30);
41 return rc;
42 }
43
44 static mach_vm_address_t
get_permanent_mapping(mach_vm_size_t size)45 get_permanent_mapping(mach_vm_size_t size)
46 {
47 kern_return_t kr;
48 mach_vm_address_t addr;
49
50 kr = mach_vm_allocate(mach_task_self(), &addr, size,
51 VM_FLAGS_ANYWHERE | VM_FLAGS_PERMANENT);
52
53 T_ASSERT_MACH_SUCCESS(kr, "mach_vm_allocate(%lld, PERMANENT) == %p",
54 size, (void *)addr);
55
56 *(int *)addr = 42;
57
58 kr = mach_vm_protect(mach_task_self(), addr, size, FALSE, VM_PROT_READ);
59
60 T_EXPECT_MACH_SUCCESS(kr, "mach_vm_protect(PERMANENT, READ)");
61
62 T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
63
64 return addr;
65 }
66
67 T_DECL(permanent_mapping, "check permanent mappings semantics", T_META_TAG_VM_PREFERRED)
68 {
69 kern_return_t kr;
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