1*e3723e1fSApple OSS Distributions #include <darwintest.h>
2*e3723e1fSApple OSS Distributions #include <darwintest_utils.h>
3*e3723e1fSApple OSS Distributions
4*e3723e1fSApple OSS Distributions #include <sys/types.h>
5*e3723e1fSApple OSS Distributions #include <sys/sysctl.h>
6*e3723e1fSApple OSS Distributions #include <mach/mach.h>
7*e3723e1fSApple OSS Distributions #include <mach/mach_vm.h>
8*e3723e1fSApple OSS Distributions #include <mach/task_info.h>
9*e3723e1fSApple OSS Distributions #include <mach/vm_param.h>
10*e3723e1fSApple OSS Distributions #include <mach/vm_types.h>
11*e3723e1fSApple OSS Distributions #include <sys/mman.h>
12*e3723e1fSApple OSS Distributions #include <unistd.h>
13*e3723e1fSApple OSS Distributions #include <TargetConditionals.h>
14*e3723e1fSApple OSS Distributions
15*e3723e1fSApple OSS Distributions enum {
16*e3723e1fSApple OSS Distributions DEFAULT = 0,
17*e3723e1fSApple OSS Distributions HEAP,
18*e3723e1fSApple OSS Distributions LARGE_FILE
19*e3723e1fSApple OSS Distributions };
20*e3723e1fSApple OSS Distributions
21*e3723e1fSApple OSS Distributions static char _filepath[MAXPATHLEN];
22*e3723e1fSApple OSS Distributions static struct mach_vm_range parent_default;
23*e3723e1fSApple OSS Distributions static struct mach_vm_range parent_heap;
24*e3723e1fSApple OSS Distributions
25*e3723e1fSApple OSS Distributions #define CHILD_PROCESS_COUNT (20)
26*e3723e1fSApple OSS Distributions #undef KiB
27*e3723e1fSApple OSS Distributions #undef MiB
28*e3723e1fSApple OSS Distributions #undef GiB
29*e3723e1fSApple OSS Distributions #define KiB(x) ((uint64_t)(x) << 10)
30*e3723e1fSApple OSS Distributions #define MiB(x) ((uint64_t)(x) << 20)
31*e3723e1fSApple OSS Distributions #define GiB(x) ((uint64_t)(x) << 30)
32*e3723e1fSApple OSS Distributions
33*e3723e1fSApple OSS Distributions #define ALLOCATION_SIZE (PAGE_SIZE)
34*e3723e1fSApple OSS Distributions #define LARGE_ALLOCATION_SIZE (GiB(1))
35*e3723e1fSApple OSS Distributions #define PER_ALLOC_AMT_GB (GiB(256))
36*e3723e1fSApple OSS Distributions #define N_ALLOC 5
37*e3723e1fSApple OSS Distributions
38*e3723e1fSApple OSS Distributions
39*e3723e1fSApple OSS Distributions /*
40*e3723e1fSApple OSS Distributions * Choose an arbitrary memory tag which applies to each of default/heap range
41*e3723e1fSApple OSS Distributions * for testing placement of allocations.
42*e3723e1fSApple OSS Distributions */
43*e3723e1fSApple OSS Distributions #define VM_MEMORY_RANGE_DEFAULT (VM_MAKE_TAG(VM_MEMORY_STACK))
44*e3723e1fSApple OSS Distributions #define VM_MEMORY_RANGE_HEAP (VM_MAKE_TAG(VM_MEMORY_MALLOC_SMALL))
45*e3723e1fSApple OSS Distributions
46*e3723e1fSApple OSS Distributions #define RANGE_DEFAULT_FLAGS (VM_FLAGS_ANYWHERE | VM_MEMORY_RANGE_DEFAULT)
47*e3723e1fSApple OSS Distributions #define RANGE_HEAP_FLAGS (VM_FLAGS_ANYWHERE | VM_MEMORY_RANGE_HEAP)
48*e3723e1fSApple OSS Distributions
49*e3723e1fSApple OSS Distributions T_GLOBAL_META(
50*e3723e1fSApple OSS Distributions T_META_NAMESPACE("xnu.vm"),
51*e3723e1fSApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"),
52*e3723e1fSApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("VM"),
53*e3723e1fSApple OSS Distributions T_META_ENABLED(!TARGET_OS_OSX),
54*e3723e1fSApple OSS Distributions T_META_OWNER("mmorran")
55*e3723e1fSApple OSS Distributions );
56*e3723e1fSApple OSS Distributions
57*e3723e1fSApple OSS Distributions static bool
ranges_enabled(void)58*e3723e1fSApple OSS Distributions ranges_enabled(void)
59*e3723e1fSApple OSS Distributions {
60*e3723e1fSApple OSS Distributions struct mach_vm_range range;
61*e3723e1fSApple OSS Distributions size_t range_sz = sizeof(range);
62*e3723e1fSApple OSS Distributions
63*e3723e1fSApple OSS Distributions bzero(&range, sizeof(range));
64*e3723e1fSApple OSS Distributions
65*e3723e1fSApple OSS Distributions /*
66*e3723e1fSApple OSS Distributions * We will fail with ENOENT or EINVAL if ranges are either not supported
67*e3723e1fSApple OSS Distributions * or not enabled on our process.
68*e3723e1fSApple OSS Distributions */
69*e3723e1fSApple OSS Distributions return sysctlbyname("vm.vm_map_user_range_default",
70*e3723e1fSApple OSS Distributions &range, &range_sz, NULL, 0) == 0;
71*e3723e1fSApple OSS Distributions }
72*e3723e1fSApple OSS Distributions
73*e3723e1fSApple OSS Distributions #define CHECK_RANGES_ENABLED() \
74*e3723e1fSApple OSS Distributions if (!ranges_enabled()) { \
75*e3723e1fSApple OSS Distributions T_SKIP("VM map ranges not enabled"); \
76*e3723e1fSApple OSS Distributions }
77*e3723e1fSApple OSS Distributions
78*e3723e1fSApple OSS Distributions static struct mach_vm_range
get_range(int target_range)79*e3723e1fSApple OSS Distributions get_range(int target_range)
80*e3723e1fSApple OSS Distributions {
81*e3723e1fSApple OSS Distributions int ret = EINVAL;
82*e3723e1fSApple OSS Distributions struct mach_vm_range range;
83*e3723e1fSApple OSS Distributions size_t range_sz = sizeof(range);
84*e3723e1fSApple OSS Distributions
85*e3723e1fSApple OSS Distributions bzero(&range, sizeof(range));
86*e3723e1fSApple OSS Distributions
87*e3723e1fSApple OSS Distributions switch (target_range) {
88*e3723e1fSApple OSS Distributions case DEFAULT:
89*e3723e1fSApple OSS Distributions ret = sysctlbyname("vm.vm_map_user_range_default", &range, &range_sz, NULL, 0);
90*e3723e1fSApple OSS Distributions T_QUIET;
91*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(ret, "successfully retrieved user default range");
92*e3723e1fSApple OSS Distributions break;
93*e3723e1fSApple OSS Distributions
94*e3723e1fSApple OSS Distributions case HEAP:
95*e3723e1fSApple OSS Distributions ret = sysctlbyname("vm.vm_map_user_range_heap", &range, &range_sz, NULL, 0);
96*e3723e1fSApple OSS Distributions T_QUIET;
97*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(ret, "successfully retrieved user heap range");
98*e3723e1fSApple OSS Distributions break;
99*e3723e1fSApple OSS Distributions
100*e3723e1fSApple OSS Distributions case LARGE_FILE:
101*e3723e1fSApple OSS Distributions ret = sysctlbyname("vm.vm_map_user_range_large_file", &range, &range_sz, NULL, 0);
102*e3723e1fSApple OSS Distributions T_QUIET;
103*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(ret, "successfully retrieved user large file range");
104*e3723e1fSApple OSS Distributions break;
105*e3723e1fSApple OSS Distributions
106*e3723e1fSApple OSS Distributions default:
107*e3723e1fSApple OSS Distributions /* Fall through with EINVAL */
108*e3723e1fSApple OSS Distributions break;
109*e3723e1fSApple OSS Distributions }
110*e3723e1fSApple OSS Distributions
111*e3723e1fSApple OSS Distributions return range;
112*e3723e1fSApple OSS Distributions }
113*e3723e1fSApple OSS Distributions
114*e3723e1fSApple OSS Distributions static task_vm_info_data_t
get_vm_task_info(void)115*e3723e1fSApple OSS Distributions get_vm_task_info(void)
116*e3723e1fSApple OSS Distributions {
117*e3723e1fSApple OSS Distributions task_vm_info_data_t ti;
118*e3723e1fSApple OSS Distributions
119*e3723e1fSApple OSS Distributions mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
120*e3723e1fSApple OSS Distributions kern_return_t const kr = task_info(mach_task_self(),
121*e3723e1fSApple OSS Distributions TASK_VM_INFO,
122*e3723e1fSApple OSS Distributions (task_info_t) &ti,
123*e3723e1fSApple OSS Distributions &count);
124*e3723e1fSApple OSS Distributions T_QUIET;
125*e3723e1fSApple OSS Distributions T_ASSERT_MACH_SUCCESS(kr, "get task_info()");
126*e3723e1fSApple OSS Distributions return ti;
127*e3723e1fSApple OSS Distributions }
128*e3723e1fSApple OSS Distributions
129*e3723e1fSApple OSS Distributions static mach_vm_address_t
assert_allocate(mach_vm_address_t dst,int vm_flags)130*e3723e1fSApple OSS Distributions assert_allocate(mach_vm_address_t dst, int vm_flags)
131*e3723e1fSApple OSS Distributions {
132*e3723e1fSApple OSS Distributions int ret = mach_vm_allocate(mach_task_self(), &dst, ALLOCATION_SIZE, vm_flags);
133*e3723e1fSApple OSS Distributions T_ASSERT_MACH_SUCCESS(ret, "vm_allocate");
134*e3723e1fSApple OSS Distributions return dst;
135*e3723e1fSApple OSS Distributions }
136*e3723e1fSApple OSS Distributions
137*e3723e1fSApple OSS Distributions static void
assert_in_range(struct mach_vm_range range,mach_vm_offset_t addr)138*e3723e1fSApple OSS Distributions assert_in_range(struct mach_vm_range range, mach_vm_offset_t addr)
139*e3723e1fSApple OSS Distributions {
140*e3723e1fSApple OSS Distributions T_LOG("checking if %llx <= %llx <= %llx", range.min_address, addr,
141*e3723e1fSApple OSS Distributions range.max_address);
142*e3723e1fSApple OSS Distributions T_EXPECT_GE(addr, range.min_address, "allocation above min address");
143*e3723e1fSApple OSS Distributions T_EXPECT_LE(addr, range.max_address, "allocation below max address");
144*e3723e1fSApple OSS Distributions }
145*e3723e1fSApple OSS Distributions
146*e3723e1fSApple OSS Distributions static void
assert_in_heap_range(mach_vm_offset_t addr)147*e3723e1fSApple OSS Distributions assert_in_heap_range(mach_vm_offset_t addr)
148*e3723e1fSApple OSS Distributions {
149*e3723e1fSApple OSS Distributions struct mach_vm_range range = get_range(HEAP);
150*e3723e1fSApple OSS Distributions
151*e3723e1fSApple OSS Distributions assert_in_range(range, addr);
152*e3723e1fSApple OSS Distributions }
153*e3723e1fSApple OSS Distributions
154*e3723e1fSApple OSS Distributions static void *
assert_mmap(void * addr,size_t sz,int fd,int flags)155*e3723e1fSApple OSS Distributions assert_mmap(void *addr, size_t sz, int fd, int flags)
156*e3723e1fSApple OSS Distributions {
157*e3723e1fSApple OSS Distributions void *ret = mmap(addr, sz, VM_PROT_READ | VM_PROT_WRITE,
158*e3723e1fSApple OSS Distributions flags, fd, 0);
159*e3723e1fSApple OSS Distributions T_EXPECT_NE(ret, MAP_FAILED, "mmap should not have MAP_FAILED");
160*e3723e1fSApple OSS Distributions T_EXPECT_NE(ret, NULL, "mmap should have returned a valid pointer");
161*e3723e1fSApple OSS Distributions return ret;
162*e3723e1fSApple OSS Distributions }
163*e3723e1fSApple OSS Distributions
164*e3723e1fSApple OSS Distributions static void
assert_allocate_eq(mach_vm_address_t dst,int vm_flags)165*e3723e1fSApple OSS Distributions assert_allocate_eq(mach_vm_address_t dst, int vm_flags)
166*e3723e1fSApple OSS Distributions {
167*e3723e1fSApple OSS Distributions mach_vm_address_t target = dst;
168*e3723e1fSApple OSS Distributions
169*e3723e1fSApple OSS Distributions T_ASSERT_MACH_SUCCESS(mach_vm_allocate(mach_task_self(), &target,
170*e3723e1fSApple OSS Distributions ALLOCATION_SIZE, vm_flags), "vm_allocate");
171*e3723e1fSApple OSS Distributions
172*e3723e1fSApple OSS Distributions T_EXPECT_EQ(target, dst, "target/dst differ");
173*e3723e1fSApple OSS Distributions }
174*e3723e1fSApple OSS Distributions
175*e3723e1fSApple OSS Distributions static mach_vm_address_t
assert_allocate_in_range(int target_range,mach_vm_address_t dst,int vm_flags)176*e3723e1fSApple OSS Distributions assert_allocate_in_range(int target_range, mach_vm_address_t dst, int vm_flags)
177*e3723e1fSApple OSS Distributions {
178*e3723e1fSApple OSS Distributions struct mach_vm_range range = get_range(target_range);
179*e3723e1fSApple OSS Distributions dst = assert_allocate(dst, vm_flags);
180*e3723e1fSApple OSS Distributions
181*e3723e1fSApple OSS Distributions assert_in_range(range, (mach_vm_offset_t)dst);
182*e3723e1fSApple OSS Distributions
183*e3723e1fSApple OSS Distributions return dst;
184*e3723e1fSApple OSS Distributions }
185*e3723e1fSApple OSS Distributions
186*e3723e1fSApple OSS Distributions static void *
assert_mmap_in_range(void * addr,int target_range,size_t sz,int fd,int flags)187*e3723e1fSApple OSS Distributions assert_mmap_in_range(void *addr, int target_range, size_t sz, int fd, int flags)
188*e3723e1fSApple OSS Distributions {
189*e3723e1fSApple OSS Distributions struct mach_vm_range range = get_range(target_range);
190*e3723e1fSApple OSS Distributions void *dst = assert_mmap(addr, sz, fd, flags);
191*e3723e1fSApple OSS Distributions
192*e3723e1fSApple OSS Distributions assert_in_range(range, (mach_vm_offset_t)dst);
193*e3723e1fSApple OSS Distributions
194*e3723e1fSApple OSS Distributions return dst;
195*e3723e1fSApple OSS Distributions }
196*e3723e1fSApple OSS Distributions
197*e3723e1fSApple OSS Distributions static void
ensure_mmap_fails(void * addr,size_t sz,int flags,int fd)198*e3723e1fSApple OSS Distributions ensure_mmap_fails(void *addr, size_t sz, int flags, int fd)
199*e3723e1fSApple OSS Distributions {
200*e3723e1fSApple OSS Distributions void *ret = mmap(addr, sz, VM_PROT_READ | VM_PROT_WRITE, flags, fd, 0);
201*e3723e1fSApple OSS Distributions T_QUIET;
202*e3723e1fSApple OSS Distributions T_EXPECT_EQ_PTR(ret, MAP_FAILED, "mmap should fail");
203*e3723e1fSApple OSS Distributions }
204*e3723e1fSApple OSS Distributions
205*e3723e1fSApple OSS Distributions __attribute__((overloadable))
206*e3723e1fSApple OSS Distributions static void
207*e3723e1fSApple OSS Distributions fork_child_test(void (^child_test)(void))
208*e3723e1fSApple OSS Distributions {
209*e3723e1fSApple OSS Distributions pid_t child_pid;
210*e3723e1fSApple OSS Distributions int err;
211*e3723e1fSApple OSS Distributions
212*e3723e1fSApple OSS Distributions child_pid = fork();
213*e3723e1fSApple OSS Distributions
214*e3723e1fSApple OSS Distributions if (child_pid == 0) {
215*e3723e1fSApple OSS Distributions /* child process */
216*e3723e1fSApple OSS Distributions T_LOG("in child");
217*e3723e1fSApple OSS Distributions child_test();
218*e3723e1fSApple OSS Distributions exit(0);
219*e3723e1fSApple OSS Distributions } else {
220*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(child_pid, "fork process");
221*e3723e1fSApple OSS Distributions
222*e3723e1fSApple OSS Distributions /* wait for child process to exit */
223*e3723e1fSApple OSS Distributions if (dt_waitpid(child_pid, &err, NULL, 30) == false) {
224*e3723e1fSApple OSS Distributions T_FAIL("dt_waitpid() failed on child pid %d", child_pid);
225*e3723e1fSApple OSS Distributions }
226*e3723e1fSApple OSS Distributions }
227*e3723e1fSApple OSS Distributions }
228*e3723e1fSApple OSS Distributions
229*e3723e1fSApple OSS Distributions __attribute__((overloadable))
230*e3723e1fSApple OSS Distributions static void
fork_child_test(void (* child_test)(void))231*e3723e1fSApple OSS Distributions fork_child_test(void (*child_test)(void))
232*e3723e1fSApple OSS Distributions {
233*e3723e1fSApple OSS Distributions fork_child_test(^{
234*e3723e1fSApple OSS Distributions child_test();
235*e3723e1fSApple OSS Distributions });
236*e3723e1fSApple OSS Distributions }
237*e3723e1fSApple OSS Distributions
238*e3723e1fSApple OSS Distributions static void
cleanup_file(void)239*e3723e1fSApple OSS Distributions cleanup_file(void)
240*e3723e1fSApple OSS Distributions {
241*e3723e1fSApple OSS Distributions unlink(_filepath);
242*e3723e1fSApple OSS Distributions bzero(_filepath, MAXPATHLEN);
243*e3723e1fSApple OSS Distributions }
244*e3723e1fSApple OSS Distributions
245*e3723e1fSApple OSS Distributions T_DECL(range_allocate_heap,
246*e3723e1fSApple OSS Distributions "ensure malloc tagged memory is allocated within the heap range", T_META_TAG_VM_PREFERRED)
247*e3723e1fSApple OSS Distributions {
248*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
249*e3723e1fSApple OSS Distributions
250*e3723e1fSApple OSS Distributions assert_allocate_in_range(HEAP, 0, RANGE_HEAP_FLAGS);
251*e3723e1fSApple OSS Distributions }
252*e3723e1fSApple OSS Distributions
253*e3723e1fSApple OSS Distributions T_DECL(range_allocate_anywhere,
254*e3723e1fSApple OSS Distributions "ensure allocation is within target range when hint is outwith range", T_META_TAG_VM_PREFERRED)
255*e3723e1fSApple OSS Distributions {
256*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
257*e3723e1fSApple OSS Distributions
258*e3723e1fSApple OSS Distributions struct mach_vm_range range = get_range(HEAP);
259*e3723e1fSApple OSS Distributions
260*e3723e1fSApple OSS Distributions assert_allocate_in_range(HEAP, range.min_address - ALLOCATION_SIZE, RANGE_HEAP_FLAGS);
261*e3723e1fSApple OSS Distributions }
262*e3723e1fSApple OSS Distributions
263*e3723e1fSApple OSS Distributions T_DECL(range_allocate_stack,
264*e3723e1fSApple OSS Distributions "ensure a stack allocation is in the default range", T_META_TAG_VM_PREFERRED)
265*e3723e1fSApple OSS Distributions {
266*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
267*e3723e1fSApple OSS Distributions
268*e3723e1fSApple OSS Distributions assert_allocate_in_range(DEFAULT, 0, RANGE_DEFAULT_FLAGS);
269*e3723e1fSApple OSS Distributions }
270*e3723e1fSApple OSS Distributions
271*e3723e1fSApple OSS Distributions static void
ensure_fixed_mappings_succeed_cross(int heap)272*e3723e1fSApple OSS Distributions ensure_fixed_mappings_succeed_cross(int heap)
273*e3723e1fSApple OSS Distributions {
274*e3723e1fSApple OSS Distributions vm_map_address_t addr;
275*e3723e1fSApple OSS Distributions
276*e3723e1fSApple OSS Distributions addr = assert_allocate(0, VM_FLAGS_ANYWHERE | heap);
277*e3723e1fSApple OSS Distributions vm_deallocate(mach_task_self(), addr, ALLOCATION_SIZE);
278*e3723e1fSApple OSS Distributions
279*e3723e1fSApple OSS Distributions assert_allocate_eq(addr, VM_FLAGS_FIXED | VM_MEMORY_RANGE_DEFAULT);
280*e3723e1fSApple OSS Distributions assert_allocate_eq(addr, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_MEMORY_RANGE_DEFAULT);
281*e3723e1fSApple OSS Distributions vm_deallocate(mach_task_self(), addr, ALLOCATION_SIZE);
282*e3723e1fSApple OSS Distributions
283*e3723e1fSApple OSS Distributions assert_allocate_eq(addr, VM_FLAGS_FIXED | VM_MEMORY_RANGE_HEAP);
284*e3723e1fSApple OSS Distributions assert_allocate_eq(addr, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_MEMORY_RANGE_HEAP);
285*e3723e1fSApple OSS Distributions vm_deallocate(mach_task_self(), addr, ALLOCATION_SIZE);
286*e3723e1fSApple OSS Distributions }
287*e3723e1fSApple OSS Distributions
288*e3723e1fSApple OSS Distributions static void
ensure_rogue_fixed_fails(void)289*e3723e1fSApple OSS Distributions ensure_rogue_fixed_fails(void)
290*e3723e1fSApple OSS Distributions {
291*e3723e1fSApple OSS Distributions struct mach_vm_range def = get_range(DEFAULT);
292*e3723e1fSApple OSS Distributions struct mach_vm_range heap = get_range(HEAP);
293*e3723e1fSApple OSS Distributions mach_vm_address_t addr;
294*e3723e1fSApple OSS Distributions kern_return_t kr;
295*e3723e1fSApple OSS Distributions
296*e3723e1fSApple OSS Distributions if (def.max_address + 3 * ALLOCATION_SIZE <= heap.min_address) {
297*e3723e1fSApple OSS Distributions addr = heap.min_address - 2 * ALLOCATION_SIZE;
298*e3723e1fSApple OSS Distributions } else {
299*e3723e1fSApple OSS Distributions /*
300*e3723e1fSApple OSS Distributions * in the unlikely event when there's no space
301*e3723e1fSApple OSS Distributions * between default and heap, then there must be
302*e3723e1fSApple OSS Distributions * a hole after heap.
303*e3723e1fSApple OSS Distributions */
304*e3723e1fSApple OSS Distributions addr = heap.max_address + ALLOCATION_SIZE;
305*e3723e1fSApple OSS Distributions }
306*e3723e1fSApple OSS Distributions
307*e3723e1fSApple OSS Distributions kr = mach_vm_allocate(mach_task_self(), &addr,
308*e3723e1fSApple OSS Distributions ALLOCATION_SIZE, VM_FLAGS_FIXED);
309*e3723e1fSApple OSS Distributions T_EXPECT_MACH_ERROR(kr, KERN_INVALID_ADDRESS, "should fail");
310*e3723e1fSApple OSS Distributions }
311*e3723e1fSApple OSS Distributions
312*e3723e1fSApple OSS Distributions static void
ensure_fixed_mapping(void)313*e3723e1fSApple OSS Distributions ensure_fixed_mapping(void)
314*e3723e1fSApple OSS Distributions {
315*e3723e1fSApple OSS Distributions ensure_fixed_mappings_succeed_cross(VM_MEMORY_RANGE_DEFAULT);
316*e3723e1fSApple OSS Distributions ensure_fixed_mappings_succeed_cross(VM_MEMORY_RANGE_HEAP);
317*e3723e1fSApple OSS Distributions
318*e3723e1fSApple OSS Distributions ensure_rogue_fixed_fails();
319*e3723e1fSApple OSS Distributions }
320*e3723e1fSApple OSS Distributions
321*e3723e1fSApple OSS Distributions T_DECL(range_allocate_fixed, "ensure fixed target is honored (even with an incorrect tag)", T_META_TAG_VM_PREFERRED)
322*e3723e1fSApple OSS Distributions {
323*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
324*e3723e1fSApple OSS Distributions
325*e3723e1fSApple OSS Distributions ensure_fixed_mapping();
326*e3723e1fSApple OSS Distributions fork_child_test(ensure_fixed_mapping);
327*e3723e1fSApple OSS Distributions }
328*e3723e1fSApple OSS Distributions
329*e3723e1fSApple OSS Distributions T_DECL(range_mmap_anon, "ensure anon mapping within HEAP range", T_META_TAG_VM_PREFERRED)
330*e3723e1fSApple OSS Distributions {
331*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
332*e3723e1fSApple OSS Distributions
333*e3723e1fSApple OSS Distributions assert_mmap_in_range(NULL, HEAP, ALLOCATION_SIZE, -1, MAP_ANON | MAP_PRIVATE);
334*e3723e1fSApple OSS Distributions }
335*e3723e1fSApple OSS Distributions
336*e3723e1fSApple OSS Distributions T_DECL(range_mmap_file, "ensure file is mapped within HEAP range", T_META_TAG_VM_PREFERRED)
337*e3723e1fSApple OSS Distributions {
338*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
339*e3723e1fSApple OSS Distributions
340*e3723e1fSApple OSS Distributions int fd = -1;
341*e3723e1fSApple OSS Distributions
342*e3723e1fSApple OSS Distributions /* prepare temp file */
343*e3723e1fSApple OSS Distributions strncpy(_filepath, "/tmp/mapfile.XXXXXX", MAXPATHLEN);
344*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(fd = mkstemp(_filepath), NULL);
345*e3723e1fSApple OSS Distributions atexit(cleanup_file);
346*e3723e1fSApple OSS Distributions
347*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(ftruncate(fd, (off_t)ALLOCATION_SIZE), NULL);
348*e3723e1fSApple OSS Distributions
349*e3723e1fSApple OSS Distributions /* map it in to the heap rage */
350*e3723e1fSApple OSS Distributions #if TARGET_OS_OSX
351*e3723e1fSApple OSS Distributions T_LOG("mapping file in DEFAULT range");
352*e3723e1fSApple OSS Distributions assert_mmap_in_range(NULL, DEFAULT, ALLOCATION_SIZE, fd, MAP_FILE | MAP_SHARED);
353*e3723e1fSApple OSS Distributions #else
354*e3723e1fSApple OSS Distributions T_LOG("mapping file in HEAP range");
355*e3723e1fSApple OSS Distributions assert_mmap_in_range(NULL, HEAP, ALLOCATION_SIZE, fd, MAP_FILE | MAP_SHARED);
356*e3723e1fSApple OSS Distributions #endif
357*e3723e1fSApple OSS Distributions }
358*e3723e1fSApple OSS Distributions
359*e3723e1fSApple OSS Distributions
360*e3723e1fSApple OSS Distributions T_DECL(range_mmap_alias_tag, "ensure anon mapping with tag is honored", T_META_TAG_VM_PREFERRED)
361*e3723e1fSApple OSS Distributions {
362*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
363*e3723e1fSApple OSS Distributions
364*e3723e1fSApple OSS Distributions assert_mmap_in_range(NULL, DEFAULT, ALLOCATION_SIZE, VM_MEMORY_RANGE_DEFAULT, MAP_ANON | MAP_PRIVATE);
365*e3723e1fSApple OSS Distributions }
366*e3723e1fSApple OSS Distributions
367*e3723e1fSApple OSS Distributions T_DECL(range_mmap_with_low_hint,
368*e3723e1fSApple OSS Distributions "ensure allocation is within target range when hint is below range", T_META_TAG_VM_PREFERRED)
369*e3723e1fSApple OSS Distributions {
370*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
371*e3723e1fSApple OSS Distributions
372*e3723e1fSApple OSS Distributions struct mach_vm_range range = get_range(HEAP);
373*e3723e1fSApple OSS Distributions mach_vm_address_t target = range.min_address - ALLOCATION_SIZE;
374*e3723e1fSApple OSS Distributions
375*e3723e1fSApple OSS Distributions assert_mmap_in_range((void *)target, HEAP, ALLOCATION_SIZE, -1, MAP_ANON | MAP_PRIVATE);
376*e3723e1fSApple OSS Distributions }
377*e3723e1fSApple OSS Distributions
378*e3723e1fSApple OSS Distributions T_DECL(range_mmap_with_high_hint,
379*e3723e1fSApple OSS Distributions "ensure allocation is within target range when hint is within range", T_META_TAG_VM_PREFERRED)
380*e3723e1fSApple OSS Distributions {
381*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
382*e3723e1fSApple OSS Distributions
383*e3723e1fSApple OSS Distributions struct mach_vm_range range = get_range(HEAP);
384*e3723e1fSApple OSS Distributions mach_vm_address_t target = range.max_address - 100 * ALLOCATION_SIZE;
385*e3723e1fSApple OSS Distributions
386*e3723e1fSApple OSS Distributions void *dst = assert_mmap_in_range((void *)target, HEAP, ALLOCATION_SIZE, -1, MAP_ANON | MAP_PRIVATE);
387*e3723e1fSApple OSS Distributions
388*e3723e1fSApple OSS Distributions T_EXPECT_EQ((mach_vm_address_t)dst, target, "unexpected allocation address");
389*e3723e1fSApple OSS Distributions }
390*e3723e1fSApple OSS Distributions
391*e3723e1fSApple OSS Distributions T_DECL(range_mmap_with_bad_hint,
392*e3723e1fSApple OSS Distributions "ensure allocation fails when hint is above range", T_META_TAG_VM_PREFERRED)
393*e3723e1fSApple OSS Distributions {
394*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
395*e3723e1fSApple OSS Distributions
396*e3723e1fSApple OSS Distributions struct mach_vm_range range = get_range(HEAP);
397*e3723e1fSApple OSS Distributions mach_vm_address_t target = range.max_address + 0x100000000;
398*e3723e1fSApple OSS Distributions
399*e3723e1fSApple OSS Distributions /* mmap should retry with 0 base on initial KERN_NO_SPACE failure */
400*e3723e1fSApple OSS Distributions assert_mmap_in_range((void *)target, HEAP, ALLOCATION_SIZE, -1, MAP_ANON | MAP_PRIVATE);
401*e3723e1fSApple OSS Distributions }
402*e3723e1fSApple OSS Distributions
403*e3723e1fSApple OSS Distributions T_DECL(range_mach_vm_map_with_bad_hint,
404*e3723e1fSApple OSS Distributions "ensure mach_vm_map fails when hint is above range", T_META_TAG_VM_PREFERRED)
405*e3723e1fSApple OSS Distributions {
406*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
407*e3723e1fSApple OSS Distributions
408*e3723e1fSApple OSS Distributions struct mach_vm_range range = get_range(HEAP);
409*e3723e1fSApple OSS Distributions mach_vm_address_t addr = range.max_address + 0x100000000;
410*e3723e1fSApple OSS Distributions
411*e3723e1fSApple OSS Distributions /*
412*e3723e1fSApple OSS Distributions * unlike mmap & vm_allocate, mach_vm_map should fail when given a hint
413*e3723e1fSApple OSS Distributions * out with the target range.
414*e3723e1fSApple OSS Distributions */
415*e3723e1fSApple OSS Distributions int ret = mach_vm_map(mach_task_self(), &addr, ALLOCATION_SIZE,
416*e3723e1fSApple OSS Distributions (mach_vm_offset_t)0, VM_FLAGS_ANYWHERE, MACH_PORT_NULL,
417*e3723e1fSApple OSS Distributions (memory_object_offset_t)0, FALSE, VM_PROT_DEFAULT, VM_PROT_ALL,
418*e3723e1fSApple OSS Distributions VM_INHERIT_DEFAULT);
419*e3723e1fSApple OSS Distributions T_QUIET; T_EXPECT_EQ(ret, KERN_NO_SPACE, "expected KERN_NO_SPACE");
420*e3723e1fSApple OSS Distributions }
421*e3723e1fSApple OSS Distributions
422*e3723e1fSApple OSS Distributions T_DECL(range_mach_vm_remap_default,
423*e3723e1fSApple OSS Distributions "ensure mach_vm_remap is successful in default range", T_META_TAG_VM_PREFERRED)
424*e3723e1fSApple OSS Distributions {
425*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
426*e3723e1fSApple OSS Distributions
427*e3723e1fSApple OSS Distributions vm_prot_t curprot;
428*e3723e1fSApple OSS Distributions vm_prot_t maxprot;
429*e3723e1fSApple OSS Distributions
430*e3723e1fSApple OSS Distributions mach_vm_address_t addr = assert_allocate_in_range(DEFAULT, 0, RANGE_DEFAULT_FLAGS);
431*e3723e1fSApple OSS Distributions mach_vm_address_t target = addr + ALLOCATION_SIZE;
432*e3723e1fSApple OSS Distributions
433*e3723e1fSApple OSS Distributions int ret = mach_vm_remap(mach_task_self(), &target, ALLOCATION_SIZE,
434*e3723e1fSApple OSS Distributions (mach_vm_offset_t)0, VM_FLAGS_ANYWHERE, mach_task_self(),
435*e3723e1fSApple OSS Distributions addr, FALSE, &curprot, &maxprot, VM_INHERIT_NONE);
436*e3723e1fSApple OSS Distributions T_QUIET; T_EXPECT_EQ(ret, KERN_SUCCESS, "expected KERN_SUCCESS");
437*e3723e1fSApple OSS Distributions }
438*e3723e1fSApple OSS Distributions
439*e3723e1fSApple OSS Distributions T_DECL(range_mach_vm_remap_heap_with_hint,
440*e3723e1fSApple OSS Distributions "ensure mach_vm_remap is successful in heap range", T_META_TAG_VM_PREFERRED)
441*e3723e1fSApple OSS Distributions {
442*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
443*e3723e1fSApple OSS Distributions
444*e3723e1fSApple OSS Distributions vm_prot_t curprot;
445*e3723e1fSApple OSS Distributions vm_prot_t maxprot;
446*e3723e1fSApple OSS Distributions
447*e3723e1fSApple OSS Distributions mach_vm_address_t addr = assert_allocate_in_range(HEAP, 0, RANGE_HEAP_FLAGS);
448*e3723e1fSApple OSS Distributions mach_vm_address_t target = addr + ALLOCATION_SIZE;
449*e3723e1fSApple OSS Distributions
450*e3723e1fSApple OSS Distributions int ret = mach_vm_remap(mach_task_self(), &target, ALLOCATION_SIZE,
451*e3723e1fSApple OSS Distributions (mach_vm_offset_t)0, VM_FLAGS_ANYWHERE, mach_task_self(),
452*e3723e1fSApple OSS Distributions addr, FALSE, &curprot, &maxprot, VM_INHERIT_NONE);
453*e3723e1fSApple OSS Distributions T_QUIET; T_EXPECT_EQ(ret, KERN_SUCCESS, "expected KERN_SUCCESS");
454*e3723e1fSApple OSS Distributions assert_in_heap_range(target);
455*e3723e1fSApple OSS Distributions }
456*e3723e1fSApple OSS Distributions
457*e3723e1fSApple OSS Distributions T_DECL(range_mach_vm_remap_heap,
458*e3723e1fSApple OSS Distributions "ensure mach_vm_remap remains in same range", T_META_TAG_VM_PREFERRED)
459*e3723e1fSApple OSS Distributions {
460*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
461*e3723e1fSApple OSS Distributions
462*e3723e1fSApple OSS Distributions vm_prot_t curprot;
463*e3723e1fSApple OSS Distributions vm_prot_t maxprot;
464*e3723e1fSApple OSS Distributions
465*e3723e1fSApple OSS Distributions mach_vm_address_t addr = assert_allocate_in_range(HEAP, 0, RANGE_HEAP_FLAGS);
466*e3723e1fSApple OSS Distributions mach_vm_address_t target = 0;
467*e3723e1fSApple OSS Distributions
468*e3723e1fSApple OSS Distributions int ret = mach_vm_remap(mach_task_self(), &target, ALLOCATION_SIZE,
469*e3723e1fSApple OSS Distributions (mach_vm_offset_t)0, VM_FLAGS_ANYWHERE, mach_task_self(),
470*e3723e1fSApple OSS Distributions addr, FALSE, &curprot, &maxprot, VM_INHERIT_NONE);
471*e3723e1fSApple OSS Distributions T_EXPECT_EQ(ret, KERN_SUCCESS, "expected KERN_SUCCESS");
472*e3723e1fSApple OSS Distributions assert_in_heap_range(target);
473*e3723e1fSApple OSS Distributions }
474*e3723e1fSApple OSS Distributions
475*e3723e1fSApple OSS Distributions static void
ensure_range(void)476*e3723e1fSApple OSS Distributions ensure_range(void)
477*e3723e1fSApple OSS Distributions {
478*e3723e1fSApple OSS Distributions struct mach_vm_range def = get_range(DEFAULT);
479*e3723e1fSApple OSS Distributions struct mach_vm_range heap = get_range(HEAP);
480*e3723e1fSApple OSS Distributions
481*e3723e1fSApple OSS Distributions T_EXPECT_GT(heap.min_address, def.max_address,
482*e3723e1fSApple OSS Distributions "ranges should not overlap");
483*e3723e1fSApple OSS Distributions T_EXPECT_LE(heap.max_address, MACH_VM_MAX_ADDRESS,
484*e3723e1fSApple OSS Distributions "expected max <= %llx", MACH_VM_MAX_ADDRESS);
485*e3723e1fSApple OSS Distributions T_EXPECT_EQ(heap.min_address,
486*e3723e1fSApple OSS Distributions heap.min_address & (unsigned long)~0x1FFFFF,
487*e3723e1fSApple OSS Distributions "expected alignment on 2MB TT boundary");
488*e3723e1fSApple OSS Distributions }
489*e3723e1fSApple OSS Distributions
490*e3723e1fSApple OSS Distributions static void
ensure_child_range(void)491*e3723e1fSApple OSS Distributions ensure_child_range(void)
492*e3723e1fSApple OSS Distributions {
493*e3723e1fSApple OSS Distributions struct mach_vm_range def = get_range(DEFAULT);
494*e3723e1fSApple OSS Distributions struct mach_vm_range heap = get_range(HEAP);
495*e3723e1fSApple OSS Distributions
496*e3723e1fSApple OSS Distributions T_QUIET; T_EXPECT_EQ(def.min_address, parent_default.min_address,
497*e3723e1fSApple OSS Distributions "expected forked default min to be equal");
498*e3723e1fSApple OSS Distributions T_QUIET; T_EXPECT_EQ(def.max_address, parent_default.max_address,
499*e3723e1fSApple OSS Distributions "expected forked default max to be equal");
500*e3723e1fSApple OSS Distributions T_QUIET; T_EXPECT_EQ(heap.min_address, parent_heap.min_address,
501*e3723e1fSApple OSS Distributions "expected forked heap min to be equal");
502*e3723e1fSApple OSS Distributions T_QUIET; T_EXPECT_EQ(heap.max_address, parent_heap.max_address,
503*e3723e1fSApple OSS Distributions "expected forked heap max to be equal");
504*e3723e1fSApple OSS Distributions }
505*e3723e1fSApple OSS Distributions
506*e3723e1fSApple OSS Distributions T_DECL(range_ensure_bounds, "ensure ranges respect map bounds", T_META_TAG_VM_PREFERRED)
507*e3723e1fSApple OSS Distributions {
508*e3723e1fSApple OSS Distributions CHECK_RANGES_ENABLED();
509*e3723e1fSApple OSS Distributions
510*e3723e1fSApple OSS Distributions parent_default = get_range(DEFAULT);
511*e3723e1fSApple OSS Distributions parent_heap = get_range(HEAP);
512*e3723e1fSApple OSS Distributions
513*e3723e1fSApple OSS Distributions ensure_range();
514*e3723e1fSApple OSS Distributions
515*e3723e1fSApple OSS Distributions for (uint32_t i = 0; i < CHILD_PROCESS_COUNT; i++) {
516*e3723e1fSApple OSS Distributions fork_child_test(ensure_child_range);
517*e3723e1fSApple OSS Distributions }
518*e3723e1fSApple OSS Distributions }
519*e3723e1fSApple OSS Distributions
520*e3723e1fSApple OSS Distributions static bool
parse_void_ranges(struct mach_vm_range * void1,struct mach_vm_range * void2)521*e3723e1fSApple OSS Distributions parse_void_ranges(struct mach_vm_range *void1, struct mach_vm_range *void2)
522*e3723e1fSApple OSS Distributions {
523*e3723e1fSApple OSS Distributions char buf[256];
524*e3723e1fSApple OSS Distributions size_t bsz = sizeof(buf) - 1;
525*e3723e1fSApple OSS Distributions char *s;
526*e3723e1fSApple OSS Distributions
527*e3723e1fSApple OSS Distributions if (sysctlbyname("vm.malloc_ranges", buf, &bsz, NULL, 0) == -1) {
528*e3723e1fSApple OSS Distributions if (errno == ENOENT) {
529*e3723e1fSApple OSS Distributions return false;
530*e3723e1fSApple OSS Distributions }
531*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(-1, "sysctlbyname(vm.malloc_ranges)");
532*e3723e1fSApple OSS Distributions }
533*e3723e1fSApple OSS Distributions buf[bsz] = '\0';
534*e3723e1fSApple OSS Distributions
535*e3723e1fSApple OSS Distributions s = buf;
536*e3723e1fSApple OSS Distributions
537*e3723e1fSApple OSS Distributions void1->min_address = strtoull(s, &s, 16);
538*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_EQ(*s, ':', "should have a ':'");
539*e3723e1fSApple OSS Distributions s++;
540*e3723e1fSApple OSS Distributions
541*e3723e1fSApple OSS Distributions void1->max_address = strtoull(s, &s, 16);
542*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_EQ(*s, ' ', "should have a ' '");
543*e3723e1fSApple OSS Distributions s++;
544*e3723e1fSApple OSS Distributions
545*e3723e1fSApple OSS Distributions void2->min_address = strtoull(s, &s, 16);
546*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_EQ(*s, ':', "should have a ':'");
547*e3723e1fSApple OSS Distributions s++;
548*e3723e1fSApple OSS Distributions
549*e3723e1fSApple OSS Distributions void2->max_address = strtoull(s, &s, 16);
550*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_EQ(*s, '\0', "should be done");
551*e3723e1fSApple OSS Distributions
552*e3723e1fSApple OSS Distributions return true;
553*e3723e1fSApple OSS Distributions }
554*e3723e1fSApple OSS Distributions
555*e3723e1fSApple OSS Distributions T_DECL(create_range, "ensure create ranges kinda works", T_META_TAG_VM_PREFERRED)
556*e3723e1fSApple OSS Distributions {
557*e3723e1fSApple OSS Distributions struct mach_vm_range void1, void2, *r;
558*e3723e1fSApple OSS Distributions
559*e3723e1fSApple OSS Distributions mach_vm_range_recipe_v1_t array[10];
560*e3723e1fSApple OSS Distributions uint32_t nranges = 0;
561*e3723e1fSApple OSS Distributions
562*e3723e1fSApple OSS Distributions if (!parse_void_ranges(&void1, &void2)) {
563*e3723e1fSApple OSS Distributions T_SKIP("malloc_ranges not supported");
564*e3723e1fSApple OSS Distributions }
565*e3723e1fSApple OSS Distributions
566*e3723e1fSApple OSS Distributions T_LOG("Ranges are %#llx:%#llx %#llx:%#llx",
567*e3723e1fSApple OSS Distributions void1.min_address, void1.max_address,
568*e3723e1fSApple OSS Distributions void2.min_address, void2.max_address);
569*e3723e1fSApple OSS Distributions
570*e3723e1fSApple OSS Distributions #define reset() \
571*e3723e1fSApple OSS Distributions nranges = 0
572*e3723e1fSApple OSS Distributions #define add_range(l, r) \
573*e3723e1fSApple OSS Distributions array[nranges++] = (mach_vm_range_recipe_v1_t){ \
574*e3723e1fSApple OSS Distributions .range = { l, r }, .range_tag = MACH_VM_RANGE_FIXED, \
575*e3723e1fSApple OSS Distributions }
576*e3723e1fSApple OSS Distributions #define create_ranges() \
577*e3723e1fSApple OSS Distributions mach_vm_range_create(mach_task_self(), MACH_VM_RANGE_FLAVOR_V1, \
578*e3723e1fSApple OSS Distributions (mach_vm_range_recipes_raw_t)array, sizeof(array[0]) * nranges)
579*e3723e1fSApple OSS Distributions
580*e3723e1fSApple OSS Distributions if (void1.min_address + MiB(128) > void1.max_address) {
581*e3723e1fSApple OSS Distributions r = &void2;
582*e3723e1fSApple OSS Distributions } else {
583*e3723e1fSApple OSS Distributions r = &void1;
584*e3723e1fSApple OSS Distributions }
585*e3723e1fSApple OSS Distributions
586*e3723e1fSApple OSS Distributions reset();
587*e3723e1fSApple OSS Distributions add_range(void1.min_address - MiB(10), void1.min_address);
588*e3723e1fSApple OSS Distributions T_EXPECT_MACH_ERROR(create_ranges(), KERN_INVALID_ARGUMENT,
589*e3723e1fSApple OSS Distributions "should fail: range outside of voids");
590*e3723e1fSApple OSS Distributions
591*e3723e1fSApple OSS Distributions reset();
592*e3723e1fSApple OSS Distributions add_range(r->min_address + MiB(1), r->min_address + MiB(3));
593*e3723e1fSApple OSS Distributions add_range(r->min_address, r->min_address + MiB(2));
594*e3723e1fSApple OSS Distributions T_EXPECT_MACH_ERROR(create_ranges(), KERN_INVALID_ARGUMENT,
595*e3723e1fSApple OSS Distributions "should fail: overlapping ranges");
596*e3723e1fSApple OSS Distributions
597*e3723e1fSApple OSS Distributions reset();
598*e3723e1fSApple OSS Distributions add_range(r->min_address, r->min_address + MiB(1));
599*e3723e1fSApple OSS Distributions add_range(r->min_address + MiB(2), r->min_address + MiB(3));
600*e3723e1fSApple OSS Distributions T_EXPECT_MACH_SUCCESS(create_ranges(), "should succeed");
601*e3723e1fSApple OSS Distributions
602*e3723e1fSApple OSS Distributions reset();
603*e3723e1fSApple OSS Distributions add_range(r->min_address, r->min_address + MiB(1));
604*e3723e1fSApple OSS Distributions add_range(r->min_address + MiB(2), r->min_address + MiB(3));
605*e3723e1fSApple OSS Distributions T_EXPECT_MACH_ERROR(create_ranges(), KERN_MEMORY_PRESENT,
606*e3723e1fSApple OSS Distributions "should fail: already allocated");
607*e3723e1fSApple OSS Distributions
608*e3723e1fSApple OSS Distributions reset();
609*e3723e1fSApple OSS Distributions add_range(r->min_address + MiB(4), r->min_address + MiB(5));
610*e3723e1fSApple OSS Distributions add_range(r->min_address + MiB(6), r->min_address + MiB(7));
611*e3723e1fSApple OSS Distributions T_EXPECT_MACH_SUCCESS(create_ranges(), "should succeed");
612*e3723e1fSApple OSS Distributions
613*e3723e1fSApple OSS Distributions __block vm_offset_t offs = 0;
614*e3723e1fSApple OSS Distributions
615*e3723e1fSApple OSS Distributions void (^check_works)(void) = ^{
616*e3723e1fSApple OSS Distributions mach_vm_address_t addr;
617*e3723e1fSApple OSS Distributions kern_return_t kr;
618*e3723e1fSApple OSS Distributions
619*e3723e1fSApple OSS Distributions offs += PAGE_SIZE;
620*e3723e1fSApple OSS Distributions addr = r->min_address + offs;
621*e3723e1fSApple OSS Distributions assert_allocate_eq(addr, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE);
622*e3723e1fSApple OSS Distributions
623*e3723e1fSApple OSS Distributions addr = r->min_address + MiB(2) + offs;
624*e3723e1fSApple OSS Distributions assert_allocate_eq(addr, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE);
625*e3723e1fSApple OSS Distributions
626*e3723e1fSApple OSS Distributions addr = r->min_address + MiB(1);
627*e3723e1fSApple OSS Distributions kr = mach_vm_allocate(mach_task_self(), &addr, ALLOCATION_SIZE,
628*e3723e1fSApple OSS Distributions VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE);
629*e3723e1fSApple OSS Distributions T_EXPECT_MACH_ERROR(kr, KERN_INVALID_ADDRESS, "should fail");
630*e3723e1fSApple OSS Distributions };
631*e3723e1fSApple OSS Distributions
632*e3723e1fSApple OSS Distributions check_works();
633*e3723e1fSApple OSS Distributions fork_child_test(check_works);
634*e3723e1fSApple OSS Distributions
635*e3723e1fSApple OSS Distributions #undef create_ranges
636*e3723e1fSApple OSS Distributions #undef add_range
637*e3723e1fSApple OSS Distributions #undef reset
638*e3723e1fSApple OSS Distributions }
639*e3723e1fSApple OSS Distributions
640*e3723e1fSApple OSS Distributions T_DECL(range_mmap_too_large, "ensure mmap fails when allocation is too large", T_META_TAG_VM_PREFERRED)
641*e3723e1fSApple OSS Distributions {
642*e3723e1fSApple OSS Distributions // Get VM map min_offset and max_offset
643*e3723e1fSApple OSS Distributions task_vm_info_data_t const ti = get_vm_task_info();
644*e3723e1fSApple OSS Distributions T_LOG("task_info range: 0x%llx-0x%llx, covering %llu bytes of memory",
645*e3723e1fSApple OSS Distributions ti.min_address, ti.max_address, ti.max_address - ti.min_address);
646*e3723e1fSApple OSS Distributions
647*e3723e1fSApple OSS Distributions // Try to mmap more memory than the address space can handle
648*e3723e1fSApple OSS Distributions size_t const sz_too_large = ti.max_address - ti.min_address + 1;
649*e3723e1fSApple OSS Distributions T_LOG("Trying to allocate %zu bytes", sz_too_large);
650*e3723e1fSApple OSS Distributions ensure_mmap_fails(NULL, sz_too_large, MAP_ANON | MAP_PRIVATE, -1);
651*e3723e1fSApple OSS Distributions }
652*e3723e1fSApple OSS Distributions
653*e3723e1fSApple OSS Distributions T_DECL(range_mmap_outside_map_range_fixed,
654*e3723e1fSApple OSS Distributions "ensure mmap fails when making a fixed allocation beyond VM map max address", T_META_TAG_VM_PREFERRED)
655*e3723e1fSApple OSS Distributions {
656*e3723e1fSApple OSS Distributions // Get VM map min_offset and max_offset
657*e3723e1fSApple OSS Distributions task_vm_info_data_t const ti = get_vm_task_info();
658*e3723e1fSApple OSS Distributions T_LOG("task_info range: 0x%llx-0x%llx", ti.min_address, ti.max_address);
659*e3723e1fSApple OSS Distributions
660*e3723e1fSApple OSS Distributions // Try to allocate a page between VM map max_offset and MACH_VM_MAX_ADDRESS
661*e3723e1fSApple OSS Distributions mach_vm_address_t const target = ti.max_address + PAGE_SIZE;
662*e3723e1fSApple OSS Distributions T_LOG("Trying to allocate memory at 0x%llx", target);
663*e3723e1fSApple OSS Distributions ensure_mmap_fails((void *)target, ALLOCATION_SIZE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1);
664*e3723e1fSApple OSS Distributions }
665*e3723e1fSApple OSS Distributions
666*e3723e1fSApple OSS Distributions static bool
large_file_range_enabled(void)667*e3723e1fSApple OSS Distributions large_file_range_enabled(void)
668*e3723e1fSApple OSS Distributions {
669*e3723e1fSApple OSS Distributions struct mach_vm_range range;
670*e3723e1fSApple OSS Distributions size_t range_sz = sizeof(range);
671*e3723e1fSApple OSS Distributions
672*e3723e1fSApple OSS Distributions bzero(&range, sizeof(range));
673*e3723e1fSApple OSS Distributions
674*e3723e1fSApple OSS Distributions /*
675*e3723e1fSApple OSS Distributions * We will fail with ENOENT or EINVAL if ranges are either not supported
676*e3723e1fSApple OSS Distributions * or not enabled on our process.
677*e3723e1fSApple OSS Distributions */
678*e3723e1fSApple OSS Distributions int const ret = sysctlbyname("vm.vm_map_user_range_large_file",
679*e3723e1fSApple OSS Distributions &range, &range_sz, NULL, 0);
680*e3723e1fSApple OSS Distributions if (ret) {
681*e3723e1fSApple OSS Distributions T_LOG("vm.vm_map_user_range_large_file errno: %d", errno);
682*e3723e1fSApple OSS Distributions } else {
683*e3723e1fSApple OSS Distributions T_LOG("large file range: (%llx, %llx)",
684*e3723e1fSApple OSS Distributions range.min_address, range.max_address);
685*e3723e1fSApple OSS Distributions }
686*e3723e1fSApple OSS Distributions return ret == 0;
687*e3723e1fSApple OSS Distributions }
688*e3723e1fSApple OSS Distributions
689*e3723e1fSApple OSS Distributions T_DECL(range_mmap_large_file,
690*e3723e1fSApple OSS Distributions "ensure large file is mapped within LARGE_FILE range", T_META_TAG_VM_PREFERRED)
691*e3723e1fSApple OSS Distributions {
692*e3723e1fSApple OSS Distributions if (!large_file_range_enabled()) {
693*e3723e1fSApple OSS Distributions T_SKIP("large file range not enabled");
694*e3723e1fSApple OSS Distributions }
695*e3723e1fSApple OSS Distributions
696*e3723e1fSApple OSS Distributions void *ptrs[N_ALLOC];
697*e3723e1fSApple OSS Distributions uint32_t i;
698*e3723e1fSApple OSS Distributions
699*e3723e1fSApple OSS Distributions int fd = -1;
700*e3723e1fSApple OSS Distributions /* prepare temp file */
701*e3723e1fSApple OSS Distributions char const * tmp_dir = dt_tmpdir();
702*e3723e1fSApple OSS Distributions snprintf(_filepath, MAXPATHLEN, "%s/maplargefile.XXXXXX", tmp_dir);
703*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(fd = mkstemp(_filepath), NULL);
704*e3723e1fSApple OSS Distributions atexit(cleanup_file);
705*e3723e1fSApple OSS Distributions
706*e3723e1fSApple OSS Distributions T_LOG("Attempting to allocate VA space in %llu GB chunks.",
707*e3723e1fSApple OSS Distributions LARGE_ALLOCATION_SIZE);
708*e3723e1fSApple OSS Distributions for (i = 0; i < N_ALLOC; ++i) {
709*e3723e1fSApple OSS Distributions void *p = assert_mmap_in_range(NULL, LARGE_FILE, LARGE_ALLOCATION_SIZE,
710*e3723e1fSApple OSS Distributions fd, MAP_SHARED | MAP_FILE);
711*e3723e1fSApple OSS Distributions if (p == MAP_FAILED) {
712*e3723e1fSApple OSS Distributions if (errno != ENOMEM) {
713*e3723e1fSApple OSS Distributions T_WITH_ERRNO;
714*e3723e1fSApple OSS Distributions T_LOG("mmap failed: stopped at %u of %d/%llu GB chunks",
715*e3723e1fSApple OSS Distributions i + 1, N_ALLOC, LARGE_ALLOCATION_SIZE);
716*e3723e1fSApple OSS Distributions }
717*e3723e1fSApple OSS Distributions break;
718*e3723e1fSApple OSS Distributions } else {
719*e3723e1fSApple OSS Distributions T_LOG("allocation %u: %p", i + 1, p);
720*e3723e1fSApple OSS Distributions }
721*e3723e1fSApple OSS Distributions
722*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(p, "mmap");
723*e3723e1fSApple OSS Distributions ptrs[i] = p;
724*e3723e1fSApple OSS Distributions }
725*e3723e1fSApple OSS Distributions
726*e3723e1fSApple OSS Distributions T_EXPECT_GE_UINT(i, N_ALLOC, "Allocate at least %u/%d %llu-GB chunks of VA space",
727*e3723e1fSApple OSS Distributions i, N_ALLOC, (LARGE_ALLOCATION_SIZE / GiB(1)));
728*e3723e1fSApple OSS Distributions
729*e3723e1fSApple OSS Distributions T_LOG("Unmapping memory");
730*e3723e1fSApple OSS Distributions for (uint32_t j = 0; j < i; ++j) {
731*e3723e1fSApple OSS Distributions int const res = munmap(ptrs[j], LARGE_ALLOCATION_SIZE);
732*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(res, 0, "munmap");
733*e3723e1fSApple OSS Distributions }
734*e3723e1fSApple OSS Distributions }
735