]>
Commit | Line | Data |
---|---|---|
b0d623f7 A |
1 | /* |
2 | * machvm_tests.c | |
3 | * xnu_quick_test | |
4 | * | |
5 | * Copyright 2008 Apple Inc. All rights reserved. | |
6 | * | |
7 | */ | |
8 | ||
9 | #include "tests.h" | |
10 | #include <mach/mach.h> | |
11 | #include <unistd.h> | |
12 | #include <err.h> | |
13 | #include <sys/param.h> | |
14 | #include <mach-o/ldsyms.h> | |
15 | ||
b0d623f7 A |
16 | int machvm_tests( void * the_argp ) |
17 | { | |
18 | int pagesize = getpagesize(); | |
19 | int regionsizes[] = { 1, 3, 7, 13, 77, 1223 }; /* sizes must be in increasing order */ | |
20 | char *regionbuffers[] = { NULL, NULL, NULL, NULL, NULL, NULL }; | |
21 | int i; | |
22 | kern_return_t kret; | |
23 | ||
24 | /* Use vm_allocate to grab some memory */ | |
25 | for (i=0; i < sizeof(regionsizes)/sizeof(regionsizes[0]); i++) { | |
26 | vm_address_t addr = 0; | |
27 | ||
28 | kret = vm_allocate(mach_task_self(), &addr, regionsizes[i]*pagesize, VM_FLAGS_ANYWHERE); | |
29 | if (kret != KERN_SUCCESS) { | |
30 | warnx("vm_allocate of %d pages failed: %d", regionsizes[i], kret); | |
31 | goto fail; | |
32 | } | |
33 | regionbuffers[i] = (char *)addr; | |
34 | } | |
35 | ||
36 | /* deallocate one range without having touched it, scribble on another, then deallocate that one */ | |
37 | kret = vm_deallocate(mach_task_self(), (vm_address_t)regionbuffers[4], regionsizes[4]*pagesize); | |
38 | if (kret != KERN_SUCCESS) { | |
39 | warnx("vm_deallocate of %d pages failed: %d", regionsizes[4], kret); | |
40 | goto fail; | |
41 | } | |
42 | regionbuffers[4] = NULL; | |
43 | ||
44 | memset(regionbuffers[3], 0x4f, pagesize*MIN(3, regionsizes[3])); | |
45 | ||
46 | kret = vm_deallocate(mach_task_self(), (vm_address_t)regionbuffers[3], regionsizes[3]*pagesize); | |
47 | if (kret != KERN_SUCCESS) { | |
48 | warnx("vm_deallocate of %d pages failed: %d", regionsizes[3], kret); | |
49 | goto fail; | |
50 | } | |
51 | regionbuffers[3] = NULL; | |
52 | ||
53 | // populate the largest buffer with a byte pattern that matches the page offset, then fix it to readonly | |
54 | for (i=0; i < regionsizes[5]; i++) { | |
55 | memset(regionbuffers[5] + i*pagesize, (unsigned char)i, pagesize); | |
56 | } | |
57 | kret = vm_protect(mach_task_self(), (vm_offset_t)regionbuffers[5], regionsizes[5]*pagesize, FALSE, VM_PROT_READ); | |
58 | if (kret != KERN_SUCCESS) { | |
59 | warnx("vm_protect of %d pages failed: %d", regionsizes[5], kret); | |
60 | goto fail; | |
61 | } | |
62 | ||
63 | // read the last few pagse of the largest buffer and verify its contents | |
64 | { | |
65 | vm_offset_t newdata; | |
66 | mach_msg_type_number_t newcount; | |
67 | ||
68 | kret = vm_read(mach_task_self(), (vm_address_t)regionbuffers[5] + (regionsizes[5]-5)*pagesize, 5*pagesize, | |
69 | &newdata, &newcount); | |
70 | if (kret != KERN_SUCCESS) { | |
71 | warnx("vm_read of %d pages failed: %d", 5, kret); | |
72 | goto fail; | |
73 | } | |
74 | ||
75 | if (0 != memcmp((char *)newdata, regionbuffers[5] + (regionsizes[5]-5)*pagesize, | |
76 | 5*pagesize)) { | |
77 | warnx("vm_read comparison of %d pages failed", 5); | |
78 | kret = -1; | |
79 | vm_deallocate(mach_task_self(), newdata, 5*pagesize); | |
80 | goto fail; | |
81 | } | |
82 | ||
83 | kret = vm_deallocate(mach_task_self(), newdata, 5*pagesize); | |
84 | if (kret != KERN_SUCCESS) { | |
85 | warnx("vm_deallocate of %d pages failed: %d", 5, kret); | |
86 | goto fail; | |
87 | } | |
88 | } | |
89 | ||
90 | // do a list read to repopulate slots 3 and 4 | |
91 | { | |
92 | vm_read_entry_t readlist; | |
93 | ||
94 | readlist[0].address = (vm_offset_t)regionbuffers[5] + 10*pagesize; | |
95 | readlist[0].size = regionsizes[3]*pagesize; | |
96 | readlist[1].address = (vm_offset_t)regionbuffers[5] + 10*pagesize + regionsizes[3]*pagesize; | |
97 | readlist[1].size = regionsizes[4]*pagesize; | |
98 | ||
99 | kret = vm_read_list(mach_task_self(), readlist, 2); | |
100 | if (kret != KERN_SUCCESS) { | |
101 | warnx("vm_read_list failed: %d", kret); | |
102 | goto fail; | |
103 | } | |
104 | ||
105 | if (0 != memcmp((char *)readlist[0].address, regionbuffers[5] + 10*pagesize, | |
106 | regionsizes[3]*pagesize)) { | |
107 | warnx("vm_read_list comparison of allocation 0 failed"); | |
108 | kret = -1; | |
109 | vm_deallocate(mach_task_self(), readlist[0].address, readlist[0].size); | |
110 | vm_deallocate(mach_task_self(), readlist[1].address, readlist[1].size); | |
111 | goto fail; | |
112 | } | |
113 | ||
114 | if (0 != memcmp((char *)readlist[1].address, regionbuffers[5] + 10*pagesize + regionsizes[3]*pagesize, | |
115 | regionsizes[4]*pagesize)) { | |
116 | warnx("vm_read_list comparison of allocation 1 failed"); | |
117 | kret = -1; | |
118 | vm_deallocate(mach_task_self(), readlist[0].address, readlist[0].size); | |
119 | vm_deallocate(mach_task_self(), readlist[1].address, readlist[1].size); | |
120 | goto fail; | |
121 | } | |
122 | ||
123 | regionbuffers[3] = (char *)readlist[0].address; | |
124 | regionbuffers[4] = (char *)readlist[1].address; | |
125 | } | |
126 | ||
127 | // do a read_overwrite and copy, which should be about the same | |
128 | { | |
129 | vm_size_t count; | |
130 | ||
131 | kret = vm_read_overwrite(mach_task_self(), (vm_offset_t)regionbuffers[3], | |
132 | regionsizes[0]*pagesize, | |
133 | (vm_offset_t)regionbuffers[0], | |
134 | &count); | |
135 | if (kret != KERN_SUCCESS) { | |
136 | warnx("vm_read_overwrite of %d pages failed: %d", regionsizes[0], kret); | |
137 | goto fail; | |
138 | } | |
139 | ||
140 | kret = vm_copy(mach_task_self(), (vm_offset_t)regionbuffers[0], | |
141 | regionsizes[0]*pagesize, | |
142 | (vm_offset_t)regionbuffers[1]); | |
143 | if (kret != KERN_SUCCESS) { | |
144 | warnx("vm_copy of %d pages failed: %d", regionsizes[0], kret); | |
145 | goto fail; | |
146 | } | |
147 | ||
148 | if (0 != memcmp(regionbuffers[1], regionbuffers[3], | |
149 | regionsizes[0]*pagesize)) { | |
150 | warnx("vm_read_overwrite/vm_copy comparison failed"); | |
151 | kret = -1; | |
152 | goto fail; | |
153 | } | |
154 | } | |
155 | ||
6d2010ae | 156 | // do a vm_copy of our mach-o header and compare. |
b0d623f7 | 157 | |
6d2010ae | 158 | kret = vm_write(mach_task_self(), (vm_address_t)regionbuffers[2], |
b0d623f7 | 159 | (vm_offset_t)&_mh_execute_header, pagesize); |
6d2010ae A |
160 | if (kret != KERN_SUCCESS) { |
161 | warnx("vm_write of %d pages failed: %d", 1, kret); | |
162 | goto fail; | |
b0d623f7 | 163 | } |
6d2010ae A |
164 | |
165 | if (_mh_execute_header.magic != *(uint32_t *)regionbuffers[2]) { | |
166 | warnx("vm_write comparison failed"); | |
167 | kret = -1; | |
168 | goto fail; | |
169 | } | |
b0d623f7 A |
170 | |
171 | // check that the vm_protects above worked | |
172 | { | |
173 | vm_address_t addr = (vm_address_t)regionbuffers[5]+7*pagesize; | |
174 | vm_size_t size = pagesize; | |
175 | int _basic[VM_REGION_BASIC_INFO_COUNT]; | |
176 | vm_region_basic_info_t basic = (vm_region_basic_info_t)_basic; | |
177 | int _basic64[VM_REGION_BASIC_INFO_COUNT_64]; | |
178 | vm_region_basic_info_64_t basic64 = (vm_region_basic_info_64_t)_basic64; | |
6d2010ae A |
179 | int _submap[VM_REGION_SUBMAP_INFO_COUNT]; |
180 | vm_region_submap_info_t submap = (vm_region_submap_info_t)_submap; | |
b0d623f7 A |
181 | mach_msg_type_number_t infocnt; |
182 | mach_port_t objname; | |
6d2010ae | 183 | natural_t nesting_depth = 0; |
b0d623f7 A |
184 | |
185 | #if !__LP64__ | |
186 | infocnt = VM_REGION_BASIC_INFO_COUNT; | |
187 | kret = vm_region(mach_task_self(), &addr, &size, VM_REGION_BASIC_INFO, | |
188 | (vm_region_info_t)basic, &infocnt, &objname); | |
189 | if (kret != KERN_SUCCESS) { | |
190 | warnx("vm_region(VM_REGION_BASIC_INFO) failed: %d", kret); | |
191 | goto fail; | |
192 | } | |
193 | if (VM_REGION_BASIC_INFO_COUNT != infocnt) { | |
194 | warnx("vm_region(VM_REGION_BASIC_INFO) returned a bad info count"); | |
195 | kret = -1; | |
196 | goto fail; | |
197 | } | |
198 | ||
199 | // when we did the vm_read_list above, it should have split this region into | |
200 | // a 10 page sub-region | |
201 | if (addr != (vm_address_t)regionbuffers[5] || size != 10*pagesize) { | |
202 | warnx("vm_region(VM_REGION_BASIC_INFO) returned a bad region range"); | |
203 | kret = -1; | |
204 | goto fail; | |
205 | } | |
206 | ||
207 | if (basic->protection != VM_PROT_READ) { | |
208 | warnx("vm_region(VM_REGION_BASIC_INFO) returned a bad protection"); | |
209 | kret = -1; | |
210 | goto fail; | |
211 | } | |
212 | #endif | |
213 | ||
214 | infocnt = VM_REGION_BASIC_INFO_COUNT_64; | |
215 | // intentionally use VM_REGION_BASIC_INFO and get up-converted | |
216 | kret = vm_region_64(mach_task_self(), &addr, &size, VM_REGION_BASIC_INFO, | |
217 | (vm_region_info_t)basic64, &infocnt, &objname); | |
218 | if (kret != KERN_SUCCESS) { | |
219 | warnx("vm_region_64(VM_REGION_BASIC_INFO) failed: %d", kret); | |
220 | goto fail; | |
221 | } | |
222 | if (VM_REGION_BASIC_INFO_COUNT_64 != infocnt) { | |
223 | warnx("vm_region_64(VM_REGION_BASIC_INFO) returned a bad info count"); | |
224 | kret = -1; | |
225 | goto fail; | |
226 | } | |
227 | ||
228 | // when we did the vm_read_list above, it should have split this region into | |
229 | // a 10 page sub-region | |
230 | if (addr != (vm_address_t)regionbuffers[5] || size != 10*pagesize) { | |
231 | warnx("vm_region_64(VM_REGION_BASIC_INFO) returned a bad region range"); | |
232 | kret = -1; | |
233 | goto fail; | |
234 | } | |
235 | ||
236 | if (basic64->protection != VM_PROT_READ) { | |
237 | warnx("vm_region_64(VM_REGION_BASIC_INFO) returned a bad protection"); | |
238 | kret = -1; | |
239 | goto fail; | |
240 | } | |
241 | ||
242 | #if !__LP64__ | |
243 | // try to compare some stuff. Particularly important for fields after offset | |
6d2010ae A |
244 | if (basic->offset != basic64->offset || |
245 | basic->behavior != basic64->behavior || | |
246 | basic->user_wired_count != basic64->user_wired_count) { | |
247 | warnx("vm_region and vm_region_64 did not agree"); | |
248 | kret = -1; | |
249 | goto fail; | |
250 | } | |
b0d623f7 | 251 | #endif |
6d2010ae A |
252 | |
253 | #if !__LP64__ | |
254 | infocnt = VM_REGION_SUBMAP_INFO_COUNT; | |
255 | kret = vm_region_recurse(mach_task_self(), &addr, &size, | |
256 | &nesting_depth, (vm_region_info_t)submap, | |
257 | &infocnt); | |
258 | if (kret != KERN_SUCCESS) { | |
259 | warnx("vm_region_recurse() failed: %d", kret); | |
260 | goto fail; | |
261 | } | |
262 | ||
263 | if (VM_REGION_SUBMAP_INFO_COUNT != infocnt) { | |
264 | warnx("vm_region_recurse() returned a bad info count"); | |
265 | kret = -1; | |
266 | goto fail; | |
267 | } | |
268 | ||
269 | if (submap->pages_dirtied != 10) { | |
270 | warnx("vm_region_recurse() returned bage pages_dirtied"); | |
271 | kret = -1; | |
272 | goto fail; | |
273 | } | |
274 | ||
275 | #endif /* !__LP64__ */ | |
276 | ||
277 | } | |
278 | ||
279 | // exercise mach_make_memory_entry/vm_map | |
280 | { | |
281 | vm_address_t addr1, addr2; | |
282 | vm_size_t size; | |
283 | mach_port_t mem_handle = MACH_PORT_NULL; | |
284 | ||
285 | addr1 = 0; | |
286 | size = 11*pagesize; | |
287 | kret = vm_allocate(mach_task_self(), &addr1, size, VM_FLAGS_ANYWHERE); | |
288 | if (kret != KERN_SUCCESS) { | |
289 | warnx("vm_allocate failed: %d", kret); | |
290 | kret = -1; | |
291 | goto fail; | |
292 | } | |
293 | ||
294 | *(uint32_t *)(uintptr_t)addr1 = 'test'; | |
295 | ||
296 | kret = mach_make_memory_entry(mach_task_self(), | |
297 | &size, addr1, VM_PROT_DEFAULT, | |
298 | &mem_handle, MACH_PORT_NULL); | |
299 | if (kret != KERN_SUCCESS) { | |
300 | warnx("mach_make_memory_entry failed: %d", kret); | |
301 | kret = -1; | |
302 | goto fail; | |
303 | } | |
304 | ||
305 | kret = vm_deallocate(mach_task_self(), addr1, size); | |
306 | if (kret != KERN_SUCCESS) { | |
307 | warnx("vm_deallocate failed: %d", kret); | |
308 | kret = -1; | |
309 | goto fail; | |
310 | } | |
311 | ||
312 | addr2 = 0; | |
313 | kret = vm_map(mach_task_self(), &addr2, size, 0, VM_FLAGS_ANYWHERE, | |
314 | mem_handle, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_DEFAULT, | |
315 | VM_INHERIT_NONE); | |
316 | if (kret != KERN_SUCCESS) { | |
317 | warnx("vm_map failed: %d", kret); | |
318 | kret = -1; | |
319 | goto fail; | |
320 | } | |
321 | ||
322 | if (*(uint32_t *)(uintptr_t)addr2 != 'test') { | |
323 | warnx("mapped data mismatch"); | |
324 | kret = -1; | |
325 | goto fail; | |
326 | } | |
327 | ||
328 | kret = vm_deallocate(mach_task_self(), addr2, size); | |
329 | if (kret != KERN_SUCCESS) { | |
330 | warnx("vm_deallocate failed: %d", kret); | |
331 | kret = -1; | |
332 | goto fail; | |
333 | } | |
334 | ||
335 | kret = mach_port_mod_refs(mach_task_self(), mem_handle, MACH_PORT_RIGHT_SEND, -1); | |
336 | if (kret != KERN_SUCCESS) { | |
337 | warnx("mach_port_mod_refs(-1) failed: %d", kret); | |
338 | kret = -1; | |
339 | goto fail; | |
340 | } | |
341 | ||
342 | addr2 = 0; | |
343 | kret = vm_map(mach_task_self(), &addr2, size, 0, VM_FLAGS_ANYWHERE, | |
344 | mem_handle, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_DEFAULT, | |
345 | VM_INHERIT_NONE); | |
346 | if (kret == KERN_SUCCESS) { | |
347 | warnx("vm_map succeeded when it should not have"); | |
348 | kret = -1; | |
349 | goto fail; | |
350 | } | |
351 | ||
352 | kret = KERN_SUCCESS; | |
b0d623f7 A |
353 | } |
354 | ||
355 | fail: | |
356 | for (i=0; i < sizeof(regionsizes)/sizeof(regionsizes[0]); i++) { | |
357 | if (regionbuffers[i]) { | |
358 | vm_deallocate(mach_task_self(), (vm_address_t)regionbuffers[i], regionsizes[i]*pagesize); | |
359 | } | |
360 | } | |
361 | ||
362 | return kret; | |
363 | } | |
364 |