OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 #include "util/mach/task_memory.h" | |
16 | |
17 #include <mach/mach.h> | |
18 | |
19 #include <algorithm> | |
20 #include <string> | |
21 | |
22 #include "base/mac/scoped_mach_vm.h" | |
23 #include "gtest/gtest.h" | |
24 #include "util/test/mac/mach_errors.h" | |
25 | |
26 namespace { | |
27 | |
28 using namespace crashpad; | |
29 using namespace crashpad::test; | |
30 | |
31 TEST(TaskMemory, ReadSelf) { | |
32 vm_address_t address = 0; | |
33 const vm_size_t kSize = 4 * PAGE_SIZE; | |
34 kern_return_t kr = | |
35 vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); | |
36 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate"); | |
37 base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); | |
38 | |
39 char* region = reinterpret_cast<char*>(address); | |
40 for (size_t index = 0; index < kSize; ++index) { | |
41 region[index] = (index % 256) ^ ((index >> 8) % 256); | |
42 } | |
43 | |
44 TaskMemory memory(mach_task_self()); | |
45 std::string result(kSize, '\0'); | |
46 | |
47 // Ensure that the entire region can be read. | |
48 ASSERT_TRUE(memory.Read(address, kSize, &result[0])); | |
49 EXPECT_EQ(0, memcmp(region, &result[0], kSize)); | |
50 | |
51 // Ensure that a read of length 0 succeeds and doesn’t touch the result. | |
52 result.assign(kSize, '\0'); | |
53 std::string zeroes = result; | |
54 ASSERT_TRUE(memory.Read(address, 0, &result[0])); | |
55 EXPECT_EQ(zeroes, result); | |
56 | |
57 // Ensure that a read starting at an unaligned address works. | |
58 ASSERT_TRUE(memory.Read(address + 1, kSize - 1, &result[0])); | |
59 EXPECT_EQ(0, memcmp(region + 1, &result[0], kSize - 1)); | |
60 | |
61 // Ensure that a read ending at an unaligned address works. | |
62 ASSERT_TRUE(memory.Read(address, kSize - 1, &result[0])); | |
63 EXPECT_EQ(0, memcmp(region, &result[0], kSize - 1)); | |
64 | |
65 // Ensure that a read starting and ending at unaligned addresses works. | |
66 ASSERT_TRUE(memory.Read(address + 1, kSize - 2, &result[0])); | |
67 EXPECT_EQ(0, memcmp(region + 1, &result[0], kSize - 2)); | |
68 | |
69 // Ensure that a read of exactly one page works. | |
70 ASSERT_TRUE(memory.Read(address + PAGE_SIZE, PAGE_SIZE, &result[0])); | |
71 EXPECT_EQ(0, memcmp(region + PAGE_SIZE, &result[0], PAGE_SIZE)); | |
72 | |
73 // Ensure that a read of a single byte works. | |
74 ASSERT_TRUE(memory.Read(address + 2, 1, &result[0])); | |
75 EXPECT_EQ(region[2], result[0]); | |
76 | |
77 // Ensure that a read of length zero works and doesn’t touch the data. | |
78 result[0] = 'M'; | |
79 ASSERT_TRUE(memory.Read(address + 3, 0, &result[0])); | |
80 EXPECT_EQ('M', result[0]); | |
81 } | |
82 | |
83 TEST(TaskMemory, ReadSelfUnmapped) { | |
84 vm_address_t address = 0; | |
85 const vm_size_t kSize = 2 * PAGE_SIZE; | |
86 kern_return_t kr = | |
87 vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); | |
88 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate"); | |
89 base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); | |
90 | |
91 char* region = reinterpret_cast<char*>(address); | |
92 for (size_t index = 0; index < kSize; ++index) { | |
93 // Don’t include any NUL bytes, because ReadCString stops when it encounters | |
94 // a NUL. | |
95 region[index] = (index % 255) + 1; | |
96 } | |
97 | |
98 kr = vm_protect( | |
99 mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE); | |
100 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_protect"); | |
101 | |
102 TaskMemory memory(mach_task_self()); | |
103 std::string result(kSize, '\0'); | |
104 | |
105 EXPECT_FALSE(memory.Read(address, kSize, &result[0])); | |
106 EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0])); | |
107 EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0])); | |
108 EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0])); | |
109 EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0])); | |
110 EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0])); | |
111 | |
112 // Repeat the test with an unmapped page instead of an unreadable one. This | |
113 // portion of the test may be flaky in the presence of other threads, if | |
114 // another thread maps something in the region that is deallocated here. | |
115 kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE); | |
116 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_deallocate"); | |
117 vm_owner.reset(address, PAGE_SIZE); | |
118 | |
119 EXPECT_FALSE(memory.Read(address, kSize, &result[0])); | |
120 EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0])); | |
121 EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0])); | |
122 EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0])); | |
123 EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0])); | |
124 EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0])); | |
125 } | |
126 | |
127 // This function consolidates the cast from a char* to mach_vm_address_t in one | |
128 // location when reading from the current task. | |
129 bool ReadCStringSelf(TaskMemory* memory, | |
130 const char* pointer, | |
131 std::string* result) { | |
132 return memory->ReadCString(reinterpret_cast<mach_vm_address_t>(pointer), | |
133 result); | |
134 } | |
135 | |
136 TEST(TaskMemory, ReadCStringSelf) { | |
137 TaskMemory memory(mach_task_self()); | |
138 std::string result; | |
139 | |
140 const char kConstCharEmpty[] = ""; | |
141 ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharEmpty, &result)); | |
142 EXPECT_TRUE(result.empty()); | |
143 EXPECT_EQ(kConstCharEmpty, result); | |
144 | |
145 const char kConstCharShort[] = "A short const char[]"; | |
146 ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharShort, &result)); | |
147 EXPECT_FALSE(result.empty()); | |
148 EXPECT_EQ(kConstCharShort, result); | |
149 | |
150 static const char kStaticConstCharEmpty[] = ""; | |
151 ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharEmpty, &result)); | |
152 EXPECT_TRUE(result.empty()); | |
153 EXPECT_EQ(kStaticConstCharEmpty, result); | |
154 | |
155 static const char kStaticConstCharShort[] = "A short static const char[]"; | |
156 ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharShort, &result)); | |
157 EXPECT_FALSE(result.empty()); | |
158 EXPECT_EQ(kStaticConstCharShort, result); | |
159 | |
160 std::string string_short("A short std::string in a function"); | |
161 ASSERT_TRUE(ReadCStringSelf(&memory, &string_short[0], &result)); | |
162 EXPECT_FALSE(result.empty()); | |
163 EXPECT_EQ(string_short, result); | |
164 | |
165 std::string string_long; | |
166 const size_t kStringLongSize = 4 * PAGE_SIZE; | |
167 for (size_t index = 0; index < kStringLongSize; ++index) { | |
168 // Don’t include any NUL bytes, because ReadCString stops when it encounters | |
169 // a NUL. | |
170 string_long.append(1, (index % 255) + 1); | |
171 } | |
172 ASSERT_EQ(kStringLongSize, string_long.size()); | |
173 ASSERT_TRUE(ReadCStringSelf(&memory, &string_long[0], &result)); | |
174 EXPECT_FALSE(result.empty()); | |
175 EXPECT_EQ(kStringLongSize, result.size()); | |
176 EXPECT_EQ(string_long, result); | |
177 } | |
178 | |
179 TEST(TaskMemory, ReadCStringSelfUnmapped) { | |
180 vm_address_t address = 0; | |
181 const vm_size_t kSize = 2 * PAGE_SIZE; | |
182 kern_return_t kr = | |
183 vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); | |
184 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate"); | |
185 base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); | |
186 | |
187 char* region = reinterpret_cast<char*>(address); | |
188 for (size_t index = 0; index < kSize; ++index) { | |
189 // Don’t include any NUL bytes, because ReadCString stops when it encounters | |
190 // a NUL. | |
191 region[index] = (index % 255) + 1; | |
192 } | |
193 | |
194 kr = vm_protect( | |
195 mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE); | |
196 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_protect"); | |
197 | |
198 TaskMemory memory(mach_task_self()); | |
199 std::string result; | |
200 EXPECT_FALSE(memory.ReadCString(address, &result)); | |
201 | |
202 // Make sure that if the string is NUL-terminated within the mapped memory | |
203 // region, it can be read properly. | |
204 char terminator_or_not = '\0'; | |
205 std::swap(region[PAGE_SIZE - 1], terminator_or_not); | |
206 ASSERT_TRUE(memory.ReadCString(address, &result)); | |
207 EXPECT_FALSE(result.empty()); | |
208 EXPECT_EQ(PAGE_SIZE - 1u, result.size()); | |
209 EXPECT_EQ(region, result); | |
210 | |
211 // Repeat the test with an unmapped page instead of an unreadable one. This | |
212 // portion of the test may be flaky in the presence of other threads, if | |
213 // another thread maps something in the region that is deallocated here. | |
214 std::swap(region[PAGE_SIZE - 1], terminator_or_not); | |
215 kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE); | |
216 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_deallocate"); | |
217 vm_owner.reset(address, PAGE_SIZE); | |
218 | |
219 EXPECT_FALSE(memory.ReadCString(address, &result)); | |
220 | |
221 // Clear the result before testing that the string can be read. This makes | |
222 // sure that the result is actually filled in, because it already contains the | |
223 // expected value from the tests above. | |
224 result.clear(); | |
225 std::swap(region[PAGE_SIZE - 1], terminator_or_not); | |
226 ASSERT_TRUE(memory.ReadCString(address, &result)); | |
227 EXPECT_FALSE(result.empty()); | |
228 EXPECT_EQ(PAGE_SIZE - 1u, result.size()); | |
229 EXPECT_EQ(region, result); | |
230 } | |
231 | |
232 // This function consolidates the cast from a char* to mach_vm_address_t in one | |
233 // location when reading from the current task. | |
234 bool ReadCStringSizeLimitedSelf(TaskMemory* memory, | |
235 const char* pointer, | |
236 size_t size, | |
237 std::string* result) { | |
238 return memory->ReadCStringSizeLimited( | |
239 reinterpret_cast<mach_vm_address_t>(pointer), size, result); | |
240 } | |
241 | |
242 TEST(TaskMemory, ReadCStringSizeLimited) { | |
Robert Sesek
2014/08/03 14:56:43
This test is rather large, and failures in it may
| |
243 TaskMemory memory(mach_task_self()); | |
244 std::string result; | |
245 | |
246 const char kConstCharEmpty[] = ""; | |
247 ASSERT_TRUE(ReadCStringSizeLimitedSelf( | |
248 &memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result)); | |
249 EXPECT_TRUE(result.empty()); | |
250 EXPECT_EQ(kConstCharEmpty, result); | |
251 result.clear(); | |
252 ASSERT_TRUE(ReadCStringSizeLimitedSelf( | |
253 &memory, kConstCharEmpty, arraysize(kConstCharEmpty) + 1, &result)); | |
254 EXPECT_TRUE(result.empty()); | |
255 EXPECT_EQ(kConstCharEmpty, result); | |
256 result.clear(); | |
257 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kConstCharEmpty, 0, &result)); | |
258 EXPECT_TRUE(result.empty()); | |
259 EXPECT_EQ(kConstCharEmpty, result); | |
260 | |
261 const char kConstCharShort[] = "A short const char[]"; | |
262 ASSERT_TRUE(ReadCStringSizeLimitedSelf( | |
263 &memory, kConstCharShort, arraysize(kConstCharShort), &result)); | |
264 EXPECT_FALSE(result.empty()); | |
265 EXPECT_EQ(kConstCharShort, result); | |
266 result.clear(); | |
267 ASSERT_TRUE(ReadCStringSizeLimitedSelf( | |
268 &memory, kConstCharShort, arraysize(kConstCharShort) + 1, &result)); | |
269 EXPECT_FALSE(result.empty()); | |
270 EXPECT_EQ(kConstCharShort, result); | |
271 result.clear(); | |
272 ASSERT_FALSE(ReadCStringSizeLimitedSelf( | |
273 &memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result)); | |
274 | |
275 static const char kStaticConstCharEmpty[] = ""; | |
276 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, | |
277 kStaticConstCharEmpty, | |
278 arraysize(kStaticConstCharEmpty), | |
279 &result)); | |
280 EXPECT_TRUE(result.empty()); | |
281 EXPECT_EQ(kStaticConstCharEmpty, result); | |
282 result.clear(); | |
283 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, | |
284 kStaticConstCharEmpty, | |
285 arraysize(kStaticConstCharEmpty) + 1, | |
286 &result)); | |
287 EXPECT_TRUE(result.empty()); | |
288 EXPECT_EQ(kStaticConstCharEmpty, result); | |
289 result.clear(); | |
290 ASSERT_TRUE( | |
291 ReadCStringSizeLimitedSelf(&memory, kStaticConstCharEmpty, 0, &result)); | |
292 EXPECT_TRUE(result.empty()); | |
293 EXPECT_EQ(kStaticConstCharEmpty, result); | |
294 | |
295 static const char kStaticConstCharShort[] = "A short static const char[]"; | |
296 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, | |
297 kStaticConstCharShort, | |
298 arraysize(kStaticConstCharShort), | |
299 &result)); | |
300 EXPECT_FALSE(result.empty()); | |
301 EXPECT_EQ(kStaticConstCharShort, result); | |
302 result.clear(); | |
303 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, | |
304 kStaticConstCharShort, | |
305 arraysize(kStaticConstCharShort) + 1, | |
306 &result)); | |
307 EXPECT_FALSE(result.empty()); | |
308 EXPECT_EQ(kStaticConstCharShort, result); | |
309 result.clear(); | |
310 ASSERT_FALSE(ReadCStringSizeLimitedSelf(&memory, | |
311 kStaticConstCharShort, | |
312 arraysize(kStaticConstCharShort) - 1, | |
313 &result)); | |
314 | |
315 std::string string_short("A short std::string in a function"); | |
316 ASSERT_TRUE(ReadCStringSizeLimitedSelf( | |
317 &memory, &string_short[0], string_short.size() + 1, &result)); | |
318 EXPECT_FALSE(result.empty()); | |
319 EXPECT_EQ(string_short, result); | |
320 result.clear(); | |
321 ASSERT_TRUE(ReadCStringSizeLimitedSelf( | |
322 &memory, &string_short[0], string_short.size() + 2, &result)); | |
323 EXPECT_FALSE(result.empty()); | |
324 EXPECT_EQ(string_short, result); | |
325 result.clear(); | |
326 ASSERT_FALSE(ReadCStringSizeLimitedSelf( | |
327 &memory, &string_short[0], string_short.size(), &result)); | |
328 | |
329 std::string string_long; | |
330 const size_t kStringLongSize = 4 * PAGE_SIZE; | |
331 for (size_t index = 0; index < kStringLongSize; ++index) { | |
332 // Don’t include any NUL bytes, because ReadCString stops when it encounters | |
333 // a NUL. | |
334 string_long.append(1, (index % 255) + 1); | |
335 } | |
336 ASSERT_EQ(kStringLongSize, string_long.size()); | |
337 ASSERT_TRUE(ReadCStringSizeLimitedSelf( | |
338 &memory, &string_long[0], string_long.size() + 1, &result)); | |
339 EXPECT_FALSE(result.empty()); | |
340 EXPECT_EQ(kStringLongSize, result.size()); | |
341 EXPECT_EQ(string_long, result); | |
342 result.clear(); | |
343 ASSERT_TRUE(ReadCStringSizeLimitedSelf( | |
344 &memory, &string_long[0], string_long.size() + 2, &result)); | |
345 EXPECT_FALSE(result.empty()); | |
346 EXPECT_EQ(kStringLongSize, result.size()); | |
347 EXPECT_EQ(string_long, result); | |
348 result.clear(); | |
349 ASSERT_FALSE(ReadCStringSizeLimitedSelf( | |
350 &memory, &string_long[0], string_long.size(), &result)); | |
351 } | |
352 | |
353 } // namespace | |
OLD | NEW |