1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/mac/scoped_mach_vm.h"
6
7 #include <mach/mach.h>
8
9 #include "base/test/gtest_util.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 // Note: This test CANNOT be run multiple times within the same process (e.g.
13 // with --gtest_repeat). Allocating and deallocating in quick succession, even
14 // with different sizes, will typically result in the kernel returning the same
15 // address. If the allocation pattern is small->large->small, the second small
16 // allocation will report being part of the previously-deallocated large region.
17 // That will cause the GetRegionInfo() expectations to fail.
18
19 namespace base {
20 namespace mac {
21 namespace {
22
GetRegionInfo(vm_address_t * region_address,vm_size_t * region_size)23 void GetRegionInfo(vm_address_t* region_address, vm_size_t* region_size) {
24 vm_region_basic_info_64 region_info;
25 mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
26 mach_port_t object;
27 kern_return_t kr = vm_region_64(
28 mach_task_self(), region_address, region_size, VM_REGION_BASIC_INFO_64,
29 reinterpret_cast<vm_region_info_t>(®ion_info), &count, &object);
30 EXPECT_EQ(KERN_SUCCESS, kr);
31 }
32
TEST(ScopedMachVMTest,Basic)33 TEST(ScopedMachVMTest, Basic) {
34 vm_address_t address;
35 vm_size_t size = PAGE_SIZE;
36 kern_return_t kr =
37 vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);
38 ASSERT_EQ(KERN_SUCCESS, kr);
39
40 ScopedMachVM scoper(address, size);
41 EXPECT_EQ(address, scoper.address());
42 EXPECT_EQ(size, scoper.size());
43
44 // Test the initial region.
45 vm_address_t region_address = address;
46 vm_size_t region_size;
47 GetRegionInfo(®ion_address, ®ion_size);
48 EXPECT_EQ(KERN_SUCCESS, kr);
49 EXPECT_EQ(address, region_address);
50 EXPECT_EQ(1u * PAGE_SIZE, region_size);
51
52 {
53 ScopedMachVM scoper2;
54 EXPECT_EQ(0u, scoper2.address());
55 EXPECT_EQ(0u, scoper2.size());
56
57 scoper.swap(scoper2);
58
59 EXPECT_EQ(address, scoper2.address());
60 EXPECT_EQ(size, scoper2.size());
61
62 EXPECT_EQ(0u, scoper.address());
63 EXPECT_EQ(0u, scoper.size());
64 }
65
66 // After deallocation, the kernel will return the next highest address.
67 region_address = address;
68 GetRegionInfo(®ion_address, ®ion_size);
69 EXPECT_EQ(KERN_SUCCESS, kr);
70 EXPECT_LT(address, region_address);
71 }
72
TEST(ScopedMachVMTest,Reset)73 TEST(ScopedMachVMTest, Reset) {
74 vm_address_t address;
75 vm_size_t size = PAGE_SIZE;
76 kern_return_t kr =
77 vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);
78 ASSERT_EQ(KERN_SUCCESS, kr);
79
80 ScopedMachVM scoper(address, size);
81
82 // Test the initial region.
83 vm_address_t region_address = address;
84 vm_size_t region_size;
85 GetRegionInfo(®ion_address, ®ion_size);
86 EXPECT_EQ(KERN_SUCCESS, kr);
87 EXPECT_EQ(address, region_address);
88 EXPECT_EQ(1u * PAGE_SIZE, region_size);
89
90 scoper.reset();
91
92 // After deallocation, the kernel will return the next highest address.
93 region_address = address;
94 GetRegionInfo(®ion_address, ®ion_size);
95 EXPECT_EQ(KERN_SUCCESS, kr);
96 EXPECT_LT(address, region_address);
97 }
98
TEST(ScopedMachVMTest,ResetSmallerAddress)99 TEST(ScopedMachVMTest, ResetSmallerAddress) {
100 vm_address_t address;
101 vm_size_t size = 2 * PAGE_SIZE;
102 kern_return_t kr =
103 vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);
104 ASSERT_EQ(KERN_SUCCESS, kr);
105
106 ScopedMachVM scoper(address, PAGE_SIZE);
107
108 // Test the initial region.
109 vm_address_t region_address = address;
110 vm_size_t region_size;
111 GetRegionInfo(®ion_address, ®ion_size);
112 EXPECT_EQ(KERN_SUCCESS, kr);
113 EXPECT_EQ(address, region_address);
114 EXPECT_EQ(2u * PAGE_SIZE, region_size);
115
116 // This will free address..PAGE_SIZE that is currently in the scoper.
117 scoper.reset(address + PAGE_SIZE, PAGE_SIZE);
118
119 // Verify that the region is now only one page.
120 region_address = address;
121 GetRegionInfo(®ion_address, ®ion_size);
122 EXPECT_EQ(address + PAGE_SIZE, region_address);
123 EXPECT_EQ(1u * PAGE_SIZE, region_size);
124 }
125
TEST(ScopedMachVMTest,ResetLargerAddressAndSize)126 TEST(ScopedMachVMTest, ResetLargerAddressAndSize) {
127 vm_address_t address;
128 vm_size_t size = 3 * PAGE_SIZE;
129 kern_return_t kr =
130 vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);
131 ASSERT_EQ(KERN_SUCCESS, kr);
132
133 // Test the initial region.
134 vm_address_t region_address = address;
135 vm_size_t region_size;
136 GetRegionInfo(®ion_address, ®ion_size);
137 EXPECT_EQ(KERN_SUCCESS, kr);
138 EXPECT_EQ(address, region_address);
139 EXPECT_EQ(3u * PAGE_SIZE, region_size);
140
141 ScopedMachVM scoper(address + 2 * PAGE_SIZE, PAGE_SIZE);
142 // Expand the region to be larger.
143 scoper.reset(address, size);
144
145 // Verify that the region is still three pages.
146 region_address = address;
147 GetRegionInfo(®ion_address, ®ion_size);
148 EXPECT_EQ(address, region_address);
149 EXPECT_EQ(3u * PAGE_SIZE, region_size);
150 }
151
TEST(ScopedMachVMTest,ResetLargerAddress)152 TEST(ScopedMachVMTest, ResetLargerAddress) {
153 vm_address_t address;
154 vm_size_t size = 6 * PAGE_SIZE;
155 kern_return_t kr =
156 vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);
157 ASSERT_EQ(KERN_SUCCESS, kr);
158
159 // Test the initial region.
160 vm_address_t region_address = address;
161 vm_size_t region_size;
162 GetRegionInfo(®ion_address, ®ion_size);
163 EXPECT_EQ(KERN_SUCCESS, kr);
164 EXPECT_EQ(address, region_address);
165 EXPECT_EQ(6u * PAGE_SIZE, region_size);
166
167 ScopedMachVM scoper(address + 3 * PAGE_SIZE, 3 * PAGE_SIZE);
168
169 // Shift the region by three pages; the last three pages should be
170 // deallocated, while keeping the first three.
171 scoper.reset(address, 3 * PAGE_SIZE);
172
173 // Verify that the region is just three pages.
174 region_address = address;
175 GetRegionInfo(®ion_address, ®ion_size);
176 EXPECT_EQ(address, region_address);
177 EXPECT_EQ(3u * PAGE_SIZE, region_size);
178 }
179
TEST(ScopedMachVMTest,ResetUnaligned)180 TEST(ScopedMachVMTest, ResetUnaligned) {
181 vm_address_t address;
182 vm_size_t size = 2 * PAGE_SIZE;
183 kern_return_t kr =
184 vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);
185 ASSERT_EQ(KERN_SUCCESS, kr);
186
187 ScopedMachVM scoper;
188
189 // Test the initial region.
190 vm_address_t region_address = address;
191 vm_size_t region_size;
192 GetRegionInfo(®ion_address, ®ion_size);
193 EXPECT_EQ(address, region_address);
194 EXPECT_EQ(2u * PAGE_SIZE, region_size);
195
196 // Initialize with unaligned size.
197 scoper.reset_unaligned(address + PAGE_SIZE, PAGE_SIZE - 3);
198 // Reset with another unaligned size.
199 scoper.reset_unaligned(address + PAGE_SIZE, PAGE_SIZE - 11);
200
201 // The entire unaligned page gets deallocated.
202 region_address = address;
203 GetRegionInfo(®ion_address, ®ion_size);
204 EXPECT_EQ(address, region_address);
205 EXPECT_EQ(1u * PAGE_SIZE, region_size);
206
207 // Reset with the remaining page.
208 scoper.reset_unaligned(address, PAGE_SIZE);
209 }
210
211 #if DCHECK_IS_ON()
212
TEST(ScopedMachVMTest,ResetMustBeAligned)213 TEST(ScopedMachVMTest, ResetMustBeAligned) {
214 vm_address_t address;
215 vm_size_t size = 2 * PAGE_SIZE;
216 kern_return_t kr =
217 vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);
218 ASSERT_EQ(KERN_SUCCESS, kr);
219
220 ScopedMachVM scoper;
221 EXPECT_DCHECK_DEATH(scoper.reset(address, PAGE_SIZE + 1));
222 }
223
224 #endif // DCHECK_IS_ON()
225
226 } // namespace
227 } // namespace mac
228 } // namespace base
229