Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(56)

Side by Side Diff: util/mach/task_memory_test.cc

Issue 438993002: Add TaskMemory, which can read another Mach task’s memory, and its test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Address review feedback Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « util/mach/task_memory.cc ('k') | util/test/errors.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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_ConstCharEmpty) {
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
252 result.clear();
253 ASSERT_TRUE(ReadCStringSizeLimitedSelf(
254 &memory, kConstCharEmpty, arraysize(kConstCharEmpty) + 1, &result));
255 EXPECT_TRUE(result.empty());
256 EXPECT_EQ(kConstCharEmpty, result);
257
258 result.clear();
259 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kConstCharEmpty, 0, &result));
260 EXPECT_TRUE(result.empty());
261 EXPECT_EQ(kConstCharEmpty, result);
262 }
263
264 TEST(TaskMemory, ReadCStringSizeLimited_ConstCharShort) {
265 TaskMemory memory(mach_task_self());
266 std::string result;
267
268 const char kConstCharShort[] = "A short const char[]";
269 ASSERT_TRUE(ReadCStringSizeLimitedSelf(
270 &memory, kConstCharShort, arraysize(kConstCharShort), &result));
271 EXPECT_FALSE(result.empty());
272 EXPECT_EQ(kConstCharShort, result);
273
274 result.clear();
275 ASSERT_TRUE(ReadCStringSizeLimitedSelf(
276 &memory, kConstCharShort, arraysize(kConstCharShort) + 1, &result));
277 EXPECT_FALSE(result.empty());
278 EXPECT_EQ(kConstCharShort, result);
279
280 ASSERT_FALSE(ReadCStringSizeLimitedSelf(
281 &memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result));
282 }
283
284 TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharEmpty) {
285 TaskMemory memory(mach_task_self());
286 std::string result;
287
288 static const char kStaticConstCharEmpty[] = "";
289 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
290 kStaticConstCharEmpty,
291 arraysize(kStaticConstCharEmpty),
292 &result));
293 EXPECT_TRUE(result.empty());
294 EXPECT_EQ(kStaticConstCharEmpty, result);
295
296 result.clear();
297 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
298 kStaticConstCharEmpty,
299 arraysize(kStaticConstCharEmpty) + 1,
300 &result));
301 EXPECT_TRUE(result.empty());
302 EXPECT_EQ(kStaticConstCharEmpty, result);
303
304 result.clear();
305 ASSERT_TRUE(
306 ReadCStringSizeLimitedSelf(&memory, kStaticConstCharEmpty, 0, &result));
307 EXPECT_TRUE(result.empty());
308 EXPECT_EQ(kStaticConstCharEmpty, result);
309 }
310
311 TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharShort) {
312 TaskMemory memory(mach_task_self());
313 std::string result;
314
315 static const char kStaticConstCharShort[] = "A short static const char[]";
316 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
317 kStaticConstCharShort,
318 arraysize(kStaticConstCharShort),
319 &result));
320 EXPECT_FALSE(result.empty());
321 EXPECT_EQ(kStaticConstCharShort, result);
322
323 result.clear();
324 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
325 kStaticConstCharShort,
326 arraysize(kStaticConstCharShort) + 1,
327 &result));
328 EXPECT_FALSE(result.empty());
329 EXPECT_EQ(kStaticConstCharShort, result);
330
331 ASSERT_FALSE(ReadCStringSizeLimitedSelf(&memory,
332 kStaticConstCharShort,
333 arraysize(kStaticConstCharShort) - 1,
334 &result));
335 }
336
337 TEST(TaskMemory, ReadCStringSizeLimited_StringShort) {
338 TaskMemory memory(mach_task_self());
339 std::string result;
340
341 std::string string_short("A short std::string in a function");
342 ASSERT_TRUE(ReadCStringSizeLimitedSelf(
343 &memory, &string_short[0], string_short.size() + 1, &result));
344 EXPECT_FALSE(result.empty());
345 EXPECT_EQ(string_short, result);
346
347 result.clear();
348 ASSERT_TRUE(ReadCStringSizeLimitedSelf(
349 &memory, &string_short[0], string_short.size() + 2, &result));
350 EXPECT_FALSE(result.empty());
351 EXPECT_EQ(string_short, result);
352
353 ASSERT_FALSE(ReadCStringSizeLimitedSelf(
354 &memory, &string_short[0], string_short.size(), &result));
355 }
356
357 TEST(TaskMemory, ReadCStringSizeLimited_StringLong) {
358 TaskMemory memory(mach_task_self());
359 std::string result;
360
361 std::string string_long;
362 const size_t kStringLongSize = 4 * PAGE_SIZE;
363 for (size_t index = 0; index < kStringLongSize; ++index) {
364 // Don’t include any NUL bytes, because ReadCString stops when it encounters
365 // a NUL.
366 string_long.append(1, (index % 255) + 1);
367 }
368 ASSERT_EQ(kStringLongSize, string_long.size());
369 ASSERT_TRUE(ReadCStringSizeLimitedSelf(
370 &memory, &string_long[0], string_long.size() + 1, &result));
371 EXPECT_FALSE(result.empty());
372 EXPECT_EQ(kStringLongSize, result.size());
373 EXPECT_EQ(string_long, result);
374
375 result.clear();
376 ASSERT_TRUE(ReadCStringSizeLimitedSelf(
377 &memory, &string_long[0], string_long.size() + 2, &result));
378 EXPECT_FALSE(result.empty());
379 EXPECT_EQ(kStringLongSize, result.size());
380 EXPECT_EQ(string_long, result);
381
382 ASSERT_FALSE(ReadCStringSizeLimitedSelf(
383 &memory, &string_long[0], string_long.size(), &result));
384 }
385
386 } // namespace
OLDNEW
« no previous file with comments | « util/mach/task_memory.cc ('k') | util/test/errors.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698