1 // Copyright (c) 2010 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include <stdint.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #include <sys/mman.h>
34 #include <sys/poll.h>
35 #include <sys/socket.h>
36 #include <sys/uio.h>
37 #include <sys/wait.h>
38 #if defined(__mips__)
39 #include <sys/cachectl.h>
40 #endif
41
42 #include <string>
43
44 #include "breakpad_googletest_includes.h"
45 #include "client/linux/handler/exception_handler.h"
46 #include "client/linux/minidump_writer/minidump_writer.h"
47 #include "common/linux/eintr_wrapper.h"
48 #include "common/linux/file_id.h"
49 #include "common/linux/ignore_ret.h"
50 #include "common/linux/linux_libc_support.h"
51 #include "common/tests/auto_tempdir.h"
52 #include "common/using_std_string.h"
53 #include "third_party/lss/linux_syscall_support.h"
54 #include "google_breakpad/processor/minidump.h"
55
56 using namespace google_breakpad;
57
58 namespace {
59
60 // Flush the instruction cache for a given memory range.
61 // Only required on ARM and mips.
FlushInstructionCache(const char * memory,uint32_t memory_size)62 void FlushInstructionCache(const char* memory, uint32_t memory_size) {
63 #if defined(__arm__)
64 long begin = reinterpret_cast<long>(memory);
65 long end = begin + static_cast<long>(memory_size);
66 # if defined(__ANDROID__)
67 // Provided by Android's <unistd.h>
68 cacheflush(begin, end, 0);
69 # elif defined(__linux__)
70 // GLibc/ARM doesn't provide a wrapper for it, do a direct syscall.
71 # ifndef __ARM_NR_cacheflush
72 # define __ARM_NR_cacheflush 0xf0002
73 # endif
74 syscall(__ARM_NR_cacheflush, begin, end, 0);
75 # else
76 # error "Your operating system is not supported yet"
77 # endif
78 #elif defined(__mips__)
79 # if defined(__ANDROID__)
80 // Provided by Android's <unistd.h>
81 long begin = reinterpret_cast<long>(memory);
82 long end = begin + static_cast<long>(memory_size);
83 cacheflush(begin, end, 0);
84 # elif defined(__linux__)
85 // See http://www.linux-mips.org/wiki/Cacheflush_Syscall.
86 cacheflush(const_cast<char*>(memory), memory_size, ICACHE);
87 # else
88 # error "Your operating system is not supported yet"
89 # endif
90 #endif
91 }
92
93 // Length of a formatted GUID string =
94 // sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
95 const int kGUIDStringSize = 37;
96
sigchld_handler(int signo)97 void sigchld_handler(int signo) { }
98
CreateTMPFile(const string & dir,string * path)99 int CreateTMPFile(const string& dir, string* path) {
100 string file = dir + "/exception-handler-unittest.XXXXXX";
101 const char* c_file = file.c_str();
102 // Copy that string, mkstemp needs a C string it can modify.
103 char* c_path = strdup(c_file);
104 const int fd = mkstemp(c_path);
105 if (fd >= 0)
106 *path = c_path;
107 free(c_path);
108 return fd;
109 }
110
111 class ExceptionHandlerTest : public ::testing::Test {
112 protected:
SetUp()113 void SetUp() {
114 // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN.
115 struct sigaction sa;
116 memset(&sa, 0, sizeof(sa));
117 sa.sa_handler = sigchld_handler;
118 ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1);
119 }
120
TearDown()121 void TearDown() {
122 sigaction(SIGCHLD, &old_action, NULL);
123 }
124
125 struct sigaction old_action;
126 };
127
128
WaitForProcessToTerminate(pid_t process_id,int expected_status)129 void WaitForProcessToTerminate(pid_t process_id, int expected_status) {
130 int status;
131 ASSERT_NE(HANDLE_EINTR(waitpid(process_id, &status, 0)), -1);
132 ASSERT_TRUE(WIFSIGNALED(status));
133 ASSERT_EQ(expected_status, WTERMSIG(status));
134 }
135
136 // Reads the minidump path sent over the pipe |fd| and sets it in |path|.
ReadMinidumpPathFromPipe(int fd,string * path)137 void ReadMinidumpPathFromPipe(int fd, string* path) {
138 struct pollfd pfd;
139 memset(&pfd, 0, sizeof(pfd));
140 pfd.fd = fd;
141 pfd.events = POLLIN | POLLERR;
142
143 const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
144 ASSERT_EQ(1, r);
145 ASSERT_TRUE(pfd.revents & POLLIN);
146
147 int32_t len;
148 ASSERT_EQ(static_cast<ssize_t>(sizeof(len)), read(fd, &len, sizeof(len)));
149 ASSERT_LT(len, 2048);
150 char* filename = static_cast<char*>(malloc(len + 1));
151 ASSERT_EQ(len, read(fd, filename, len));
152 filename[len] = 0;
153 close(fd);
154 *path = filename;
155 free(filename);
156 }
157
158 } // namespace
159
TEST(ExceptionHandlerTest,SimpleWithPath)160 TEST(ExceptionHandlerTest, SimpleWithPath) {
161 AutoTempDir temp_dir;
162 ExceptionHandler handler(
163 MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
164 EXPECT_EQ(temp_dir.path(), handler.minidump_descriptor().directory());
165 string temp_subdir = temp_dir.path() + "/subdir";
166 handler.set_minidump_descriptor(MinidumpDescriptor(temp_subdir));
167 EXPECT_EQ(temp_subdir, handler.minidump_descriptor().directory());
168 }
169
TEST(ExceptionHandlerTest,SimpleWithFD)170 TEST(ExceptionHandlerTest, SimpleWithFD) {
171 AutoTempDir temp_dir;
172 string path;
173 const int fd = CreateTMPFile(temp_dir.path(), &path);
174 ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, true, -1);
175 close(fd);
176 }
177
DoneCallback(const MinidumpDescriptor & descriptor,void * context,bool succeeded)178 static bool DoneCallback(const MinidumpDescriptor& descriptor,
179 void* context,
180 bool succeeded) {
181 if (!succeeded)
182 return false;
183
184 if (!descriptor.IsFD()) {
185 int fd = reinterpret_cast<intptr_t>(context);
186 uint32_t len = 0;
187 len = my_strlen(descriptor.path());
188 IGNORE_RET(HANDLE_EINTR(sys_write(fd, &len, sizeof(len))));
189 IGNORE_RET(HANDLE_EINTR(sys_write(fd, descriptor.path(), len)));
190 }
191 return true;
192 }
193
194 #ifndef ADDRESS_SANITIZER
195
196 // This is a replacement for "*reinterpret_cast<volatile int*>(NULL) = 0;"
197 // It is needed because GCC is allowed to assume that the program will
198 // not execute any undefined behavior (UB) operation. Further, when GCC
199 // observes that UB statement is reached, it can assume that all statements
200 // leading to the UB one are never executed either, and can completely
201 // optimize them out. In the case of ExceptionHandlerTest::ExternalDumper,
202 // GCC-4.9 optimized out the entire set up of ExceptionHandler, causing
203 // test failure.
204 volatile int *p_null; // external linkage, so GCC can't tell that it
205 // remains NULL. Volatile just for a good measure.
DoNullPointerDereference()206 static void DoNullPointerDereference() {
207 *p_null = 1;
208 }
209
ChildCrash(bool use_fd)210 void ChildCrash(bool use_fd) {
211 AutoTempDir temp_dir;
212 int fds[2] = {0};
213 int minidump_fd = -1;
214 string minidump_path;
215 if (use_fd) {
216 minidump_fd = CreateTMPFile(temp_dir.path(), &minidump_path);
217 } else {
218 ASSERT_NE(pipe(fds), -1);
219 }
220
221 const pid_t child = fork();
222 if (child == 0) {
223 {
224 google_breakpad::scoped_ptr<ExceptionHandler> handler;
225 if (use_fd) {
226 handler.reset(new ExceptionHandler(MinidumpDescriptor(minidump_fd),
227 NULL, NULL, NULL, true, -1));
228 } else {
229 close(fds[0]); // Close the reading end.
230 void* fd_param = reinterpret_cast<void*>(fds[1]);
231 handler.reset(new ExceptionHandler(MinidumpDescriptor(temp_dir.path()),
232 NULL, DoneCallback, fd_param,
233 true, -1));
234 }
235 // Crash with the exception handler in scope.
236 DoNullPointerDereference();
237 }
238 }
239 if (!use_fd)
240 close(fds[1]); // Close the writting end.
241
242 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
243
244 if (!use_fd)
245 ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
246
247 struct stat st;
248 ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
249 ASSERT_GT(st.st_size, 0);
250 unlink(minidump_path.c_str());
251 }
252
TEST(ExceptionHandlerTest,ChildCrashWithPath)253 TEST(ExceptionHandlerTest, ChildCrashWithPath) {
254 ASSERT_NO_FATAL_FAILURE(ChildCrash(false));
255 }
256
TEST(ExceptionHandlerTest,ChildCrashWithFD)257 TEST(ExceptionHandlerTest, ChildCrashWithFD) {
258 ASSERT_NO_FATAL_FAILURE(ChildCrash(true));
259 }
260
261 #endif // !ADDRESS_SANITIZER
262
DoneCallbackReturnFalse(const MinidumpDescriptor & descriptor,void * context,bool succeeded)263 static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor,
264 void* context,
265 bool succeeded) {
266 return false;
267 }
268
DoneCallbackReturnTrue(const MinidumpDescriptor & descriptor,void * context,bool succeeded)269 static bool DoneCallbackReturnTrue(const MinidumpDescriptor& descriptor,
270 void* context,
271 bool succeeded) {
272 return true;
273 }
274
DoneCallbackRaiseSIGKILL(const MinidumpDescriptor & descriptor,void * context,bool succeeded)275 static bool DoneCallbackRaiseSIGKILL(const MinidumpDescriptor& descriptor,
276 void* context,
277 bool succeeded) {
278 raise(SIGKILL);
279 return true;
280 }
281
FilterCallbackReturnFalse(void * context)282 static bool FilterCallbackReturnFalse(void* context) {
283 return false;
284 }
285
FilterCallbackReturnTrue(void * context)286 static bool FilterCallbackReturnTrue(void* context) {
287 return true;
288 }
289
290 // SIGKILL cannot be blocked and a handler cannot be installed for it. In the
291 // following tests, if the child dies with signal SIGKILL, then the signal was
292 // redelivered to this handler. If the child dies with SIGSEGV then it wasn't.
RaiseSIGKILL(int sig)293 static void RaiseSIGKILL(int sig) {
294 raise(SIGKILL);
295 }
296
InstallRaiseSIGKILL()297 static bool InstallRaiseSIGKILL() {
298 struct sigaction sa;
299 memset(&sa, 0, sizeof(sa));
300 sa.sa_handler = RaiseSIGKILL;
301 return sigaction(SIGSEGV, &sa, NULL) != -1;
302 }
303
304 #ifndef ADDRESS_SANITIZER
305
CrashWithCallbacks(ExceptionHandler::FilterCallback filter,ExceptionHandler::MinidumpCallback done,string path)306 static void CrashWithCallbacks(ExceptionHandler::FilterCallback filter,
307 ExceptionHandler::MinidumpCallback done,
308 string path) {
309 ExceptionHandler handler(
310 MinidumpDescriptor(path), filter, done, NULL, true, -1);
311 // Crash with the exception handler in scope.
312 DoNullPointerDereference();
313 }
314
TEST(ExceptionHandlerTest,RedeliveryOnFilterCallbackFalse)315 TEST(ExceptionHandlerTest, RedeliveryOnFilterCallbackFalse) {
316 AutoTempDir temp_dir;
317
318 const pid_t child = fork();
319 if (child == 0) {
320 ASSERT_TRUE(InstallRaiseSIGKILL());
321 CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path());
322 }
323
324 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
325 }
326
TEST(ExceptionHandlerTest,RedeliveryOnDoneCallbackFalse)327 TEST(ExceptionHandlerTest, RedeliveryOnDoneCallbackFalse) {
328 AutoTempDir temp_dir;
329
330 const pid_t child = fork();
331 if (child == 0) {
332 ASSERT_TRUE(InstallRaiseSIGKILL());
333 CrashWithCallbacks(NULL, DoneCallbackReturnFalse, temp_dir.path());
334 }
335
336 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
337 }
338
TEST(ExceptionHandlerTest,NoRedeliveryOnDoneCallbackTrue)339 TEST(ExceptionHandlerTest, NoRedeliveryOnDoneCallbackTrue) {
340 AutoTempDir temp_dir;
341
342 const pid_t child = fork();
343 if (child == 0) {
344 ASSERT_TRUE(InstallRaiseSIGKILL());
345 CrashWithCallbacks(NULL, DoneCallbackReturnTrue, temp_dir.path());
346 }
347
348 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
349 }
350
TEST(ExceptionHandlerTest,NoRedeliveryOnFilterCallbackTrue)351 TEST(ExceptionHandlerTest, NoRedeliveryOnFilterCallbackTrue) {
352 AutoTempDir temp_dir;
353
354 const pid_t child = fork();
355 if (child == 0) {
356 ASSERT_TRUE(InstallRaiseSIGKILL());
357 CrashWithCallbacks(FilterCallbackReturnTrue, NULL, temp_dir.path());
358 }
359
360 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
361 }
362
TEST(ExceptionHandlerTest,RedeliveryToDefaultHandler)363 TEST(ExceptionHandlerTest, RedeliveryToDefaultHandler) {
364 AutoTempDir temp_dir;
365
366 const pid_t child = fork();
367 if (child == 0) {
368 CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path());
369 }
370
371 // As RaiseSIGKILL wasn't installed, the redelivery should just kill the child
372 // with SIGSEGV.
373 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
374 }
375
376 // Check that saving and restoring the signal handler with 'signal'
377 // instead of 'sigaction' doesn't make the Breakpad signal handler
378 // crash. See comments in ExceptionHandler::SignalHandler for full
379 // details.
TEST(ExceptionHandlerTest,RedeliveryOnBadSignalHandlerFlag)380 TEST(ExceptionHandlerTest, RedeliveryOnBadSignalHandlerFlag) {
381 AutoTempDir temp_dir;
382 const pid_t child = fork();
383 if (child == 0) {
384 // Install the RaiseSIGKILL handler for SIGSEGV.
385 ASSERT_TRUE(InstallRaiseSIGKILL());
386
387 // Create a new exception handler, this installs a new SIGSEGV
388 // handler, after saving the old one.
389 ExceptionHandler handler(
390 MinidumpDescriptor(temp_dir.path()), NULL,
391 DoneCallbackReturnFalse, NULL, true, -1);
392
393 // Install the default SIGSEGV handler, saving the current one.
394 // Then re-install the current one with 'signal', this loses the
395 // SA_SIGINFO flag associated with the Breakpad handler.
396 sighandler_t old_handler = signal(SIGSEGV, SIG_DFL);
397 ASSERT_NE(reinterpret_cast<void*>(old_handler),
398 reinterpret_cast<void*>(SIG_ERR));
399 ASSERT_NE(reinterpret_cast<void*>(signal(SIGSEGV, old_handler)),
400 reinterpret_cast<void*>(SIG_ERR));
401
402 // Crash with the exception handler in scope.
403 DoNullPointerDereference();
404 }
405 // SIGKILL means Breakpad's signal handler didn't crash.
406 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
407 }
408
TEST(ExceptionHandlerTest,StackedHandlersDeliveredToTop)409 TEST(ExceptionHandlerTest, StackedHandlersDeliveredToTop) {
410 AutoTempDir temp_dir;
411
412 const pid_t child = fork();
413 if (child == 0) {
414 ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()),
415 NULL,
416 NULL,
417 NULL,
418 true,
419 -1);
420 CrashWithCallbacks(NULL, DoneCallbackRaiseSIGKILL, temp_dir.path());
421 }
422 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
423 }
424
TEST(ExceptionHandlerTest,StackedHandlersNotDeliveredToBottom)425 TEST(ExceptionHandlerTest, StackedHandlersNotDeliveredToBottom) {
426 AutoTempDir temp_dir;
427
428 const pid_t child = fork();
429 if (child == 0) {
430 ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()),
431 NULL,
432 DoneCallbackRaiseSIGKILL,
433 NULL,
434 true,
435 -1);
436 CrashWithCallbacks(NULL, NULL, temp_dir.path());
437 }
438 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
439 }
440
TEST(ExceptionHandlerTest,StackedHandlersFilteredToBottom)441 TEST(ExceptionHandlerTest, StackedHandlersFilteredToBottom) {
442 AutoTempDir temp_dir;
443
444 const pid_t child = fork();
445 if (child == 0) {
446 ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()),
447 NULL,
448 DoneCallbackRaiseSIGKILL,
449 NULL,
450 true,
451 -1);
452 CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path());
453 }
454 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
455 }
456
TEST(ExceptionHandlerTest,StackedHandlersUnhandledToBottom)457 TEST(ExceptionHandlerTest, StackedHandlersUnhandledToBottom) {
458 AutoTempDir temp_dir;
459
460 const pid_t child = fork();
461 if (child == 0) {
462 ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()),
463 NULL,
464 DoneCallbackRaiseSIGKILL,
465 NULL,
466 true,
467 -1);
468 CrashWithCallbacks(NULL, DoneCallbackReturnFalse, temp_dir.path());
469 }
470 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
471 }
472
473 #endif // !ADDRESS_SANITIZER
474
475 const unsigned char kIllegalInstruction[] = {
476 #if defined(__mips__)
477 // mfc2 zero,Impl - usually illegal in userspace.
478 0x48, 0x00, 0x00, 0x48
479 #else
480 // This crashes with SIGILL on x86/x86-64/arm.
481 0xff, 0xff, 0xff, 0xff
482 #endif
483 };
484
485 // Test that memory around the instruction pointer is written
486 // to the dump as a MinidumpMemoryRegion.
TEST(ExceptionHandlerTest,InstructionPointerMemory)487 TEST(ExceptionHandlerTest, InstructionPointerMemory) {
488 AutoTempDir temp_dir;
489 int fds[2];
490 ASSERT_NE(pipe(fds), -1);
491
492 // These are defined here so the parent can use them to check the
493 // data from the minidump afterwards.
494 const uint32_t kMemorySize = 256; // bytes
495 const int kOffset = kMemorySize / 2;
496
497 const pid_t child = fork();
498 if (child == 0) {
499 close(fds[0]);
500 ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
501 DoneCallback, reinterpret_cast<void*>(fds[1]),
502 true, -1);
503 // Get some executable memory.
504 char* memory =
505 reinterpret_cast<char*>(mmap(NULL,
506 kMemorySize,
507 PROT_READ | PROT_WRITE | PROT_EXEC,
508 MAP_PRIVATE | MAP_ANON,
509 -1,
510 0));
511 if (!memory)
512 exit(0);
513
514 // Write some instructions that will crash. Put them in the middle
515 // of the block of memory, because the minidump should contain 128
516 // bytes on either side of the instruction pointer.
517 memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction));
518 FlushInstructionCache(memory, kMemorySize);
519
520 // Now execute the instructions, which should crash.
521 typedef void (*void_function)(void);
522 void_function memory_function =
523 reinterpret_cast<void_function>(memory + kOffset);
524 memory_function();
525 }
526 close(fds[1]);
527
528 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL));
529
530 string minidump_path;
531 ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
532
533 struct stat st;
534 ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
535 ASSERT_GT(st.st_size, 0);
536
537 // Read the minidump. Locate the exception record and the
538 // memory list, and then ensure that there is a memory region
539 // in the memory list that covers the instruction pointer from
540 // the exception record.
541 Minidump minidump(minidump_path);
542 ASSERT_TRUE(minidump.Read());
543
544 MinidumpException* exception = minidump.GetException();
545 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
546 ASSERT_TRUE(exception);
547 ASSERT_TRUE(memory_list);
548 ASSERT_LT(0U, memory_list->region_count());
549
550 MinidumpContext* context = exception->GetContext();
551 ASSERT_TRUE(context);
552
553 uint64_t instruction_pointer;
554 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
555
556 MinidumpMemoryRegion* region =
557 memory_list->GetMemoryRegionForAddress(instruction_pointer);
558 ASSERT_TRUE(region);
559
560 EXPECT_EQ(kMemorySize, region->GetSize());
561 const uint8_t* bytes = region->GetMemory();
562 ASSERT_TRUE(bytes);
563
564 uint8_t prefix_bytes[kOffset];
565 uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(kIllegalInstruction)];
566 memset(prefix_bytes, 0, sizeof(prefix_bytes));
567 memset(suffix_bytes, 0, sizeof(suffix_bytes));
568 EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
569 EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction,
570 sizeof(kIllegalInstruction)) == 0);
571 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction),
572 suffix_bytes, sizeof(suffix_bytes)) == 0);
573
574 unlink(minidump_path.c_str());
575 }
576
577 // Test that the memory region around the instruction pointer is
578 // bounded correctly on the low end.
TEST(ExceptionHandlerTest,InstructionPointerMemoryMinBound)579 TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
580 AutoTempDir temp_dir;
581 int fds[2];
582 ASSERT_NE(pipe(fds), -1);
583
584 // These are defined here so the parent can use them to check the
585 // data from the minidump afterwards.
586 const uint32_t kMemorySize = 256; // bytes
587 const int kOffset = 0;
588
589 const pid_t child = fork();
590 if (child == 0) {
591 close(fds[0]);
592 ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
593 DoneCallback, reinterpret_cast<void*>(fds[1]),
594 true, -1);
595 // Get some executable memory.
596 char* memory =
597 reinterpret_cast<char*>(mmap(NULL,
598 kMemorySize,
599 PROT_READ | PROT_WRITE | PROT_EXEC,
600 MAP_PRIVATE | MAP_ANON,
601 -1,
602 0));
603 if (!memory)
604 exit(0);
605
606 // Write some instructions that will crash. Put them in the middle
607 // of the block of memory, because the minidump should contain 128
608 // bytes on either side of the instruction pointer.
609 memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction));
610 FlushInstructionCache(memory, kMemorySize);
611
612 // Now execute the instructions, which should crash.
613 typedef void (*void_function)(void);
614 void_function memory_function =
615 reinterpret_cast<void_function>(memory + kOffset);
616 memory_function();
617 }
618 close(fds[1]);
619
620 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL));
621
622 string minidump_path;
623 ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
624
625 struct stat st;
626 ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
627 ASSERT_GT(st.st_size, 0);
628
629 // Read the minidump. Locate the exception record and the
630 // memory list, and then ensure that there is a memory region
631 // in the memory list that covers the instruction pointer from
632 // the exception record.
633 Minidump minidump(minidump_path);
634 ASSERT_TRUE(minidump.Read());
635
636 MinidumpException* exception = minidump.GetException();
637 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
638 ASSERT_TRUE(exception);
639 ASSERT_TRUE(memory_list);
640 ASSERT_LT(0U, memory_list->region_count());
641
642 MinidumpContext* context = exception->GetContext();
643 ASSERT_TRUE(context);
644
645 uint64_t instruction_pointer;
646 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
647
648 MinidumpMemoryRegion* region =
649 memory_list->GetMemoryRegionForAddress(instruction_pointer);
650 ASSERT_TRUE(region);
651
652 EXPECT_EQ(kMemorySize / 2, region->GetSize());
653 const uint8_t* bytes = region->GetMemory();
654 ASSERT_TRUE(bytes);
655
656 uint8_t suffix_bytes[kMemorySize / 2 - sizeof(kIllegalInstruction)];
657 memset(suffix_bytes, 0, sizeof(suffix_bytes));
658 EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction,
659 sizeof(kIllegalInstruction)) == 0);
660 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction),
661 suffix_bytes, sizeof(suffix_bytes)) == 0);
662 unlink(minidump_path.c_str());
663 }
664
665 // Test that the memory region around the instruction pointer is
666 // bounded correctly on the high end.
TEST(ExceptionHandlerTest,InstructionPointerMemoryMaxBound)667 TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
668 AutoTempDir temp_dir;
669 int fds[2];
670 ASSERT_NE(pipe(fds), -1);
671
672 // These are defined here so the parent can use them to check the
673 // data from the minidump afterwards.
674 // Use 4k here because the OS will hand out a single page even
675 // if a smaller size is requested, and this test wants to
676 // test the upper bound of the memory range.
677 const uint32_t kMemorySize = 4096; // bytes
678 const int kOffset = kMemorySize - sizeof(kIllegalInstruction);
679
680 const pid_t child = fork();
681 if (child == 0) {
682 close(fds[0]);
683 ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
684 DoneCallback, reinterpret_cast<void*>(fds[1]),
685 true, -1);
686 // Get some executable memory.
687 char* memory =
688 reinterpret_cast<char*>(mmap(NULL,
689 kMemorySize,
690 PROT_READ | PROT_WRITE | PROT_EXEC,
691 MAP_PRIVATE | MAP_ANON,
692 -1,
693 0));
694 if (!memory)
695 exit(0);
696
697 // Write some instructions that will crash. Put them in the middle
698 // of the block of memory, because the minidump should contain 128
699 // bytes on either side of the instruction pointer.
700 memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction));
701 FlushInstructionCache(memory, kMemorySize);
702
703 // Now execute the instructions, which should crash.
704 typedef void (*void_function)(void);
705 void_function memory_function =
706 reinterpret_cast<void_function>(memory + kOffset);
707 memory_function();
708 }
709 close(fds[1]);
710
711 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL));
712
713 string minidump_path;
714 ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
715
716 struct stat st;
717 ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
718 ASSERT_GT(st.st_size, 0);
719
720 // Read the minidump. Locate the exception record and the memory list, and
721 // then ensure that there is a memory region in the memory list that covers
722 // the instruction pointer from the exception record.
723 Minidump minidump(minidump_path);
724 ASSERT_TRUE(minidump.Read());
725
726 MinidumpException* exception = minidump.GetException();
727 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
728 ASSERT_TRUE(exception);
729 ASSERT_TRUE(memory_list);
730 ASSERT_LT(0U, memory_list->region_count());
731
732 MinidumpContext* context = exception->GetContext();
733 ASSERT_TRUE(context);
734
735 uint64_t instruction_pointer;
736 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
737
738 MinidumpMemoryRegion* region =
739 memory_list->GetMemoryRegionForAddress(instruction_pointer);
740 ASSERT_TRUE(region);
741
742 const size_t kPrefixSize = 128; // bytes
743 EXPECT_EQ(kPrefixSize + sizeof(kIllegalInstruction), region->GetSize());
744 const uint8_t* bytes = region->GetMemory();
745 ASSERT_TRUE(bytes);
746
747 uint8_t prefix_bytes[kPrefixSize];
748 memset(prefix_bytes, 0, sizeof(prefix_bytes));
749 EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
750 EXPECT_TRUE(memcmp(bytes + kPrefixSize,
751 kIllegalInstruction, sizeof(kIllegalInstruction)) == 0);
752
753 unlink(minidump_path.c_str());
754 }
755
756 #ifndef ADDRESS_SANITIZER
757
758 // Ensure that an extra memory block doesn't get added when the instruction
759 // pointer is not in mapped memory.
TEST(ExceptionHandlerTest,InstructionPointerMemoryNullPointer)760 TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
761 AutoTempDir temp_dir;
762 int fds[2];
763 ASSERT_NE(pipe(fds), -1);
764
765 const pid_t child = fork();
766 if (child == 0) {
767 close(fds[0]);
768 ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
769 DoneCallback, reinterpret_cast<void*>(fds[1]),
770 true, -1);
771 // Try calling a NULL pointer.
772 typedef void (*void_function)(void);
773 void_function memory_function = reinterpret_cast<void_function>(NULL);
774 memory_function();
775 }
776 close(fds[1]);
777
778 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
779
780 string minidump_path;
781 ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
782
783 struct stat st;
784 ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
785 ASSERT_GT(st.st_size, 0);
786
787 // Read the minidump. Locate the exception record and the
788 // memory list, and then ensure that there is a memory region
789 // in the memory list that covers the instruction pointer from
790 // the exception record.
791 Minidump minidump(minidump_path);
792 ASSERT_TRUE(minidump.Read());
793
794 MinidumpException* exception = minidump.GetException();
795 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
796 ASSERT_TRUE(exception);
797 ASSERT_TRUE(memory_list);
798 ASSERT_EQ(static_cast<unsigned int>(1), memory_list->region_count());
799
800 unlink(minidump_path.c_str());
801 }
802
803 #endif // !ADDRESS_SANITIZER
804
805 // Test that anonymous memory maps can be annotated with names and IDs.
TEST(ExceptionHandlerTest,ModuleInfo)806 TEST(ExceptionHandlerTest, ModuleInfo) {
807 // These are defined here so the parent can use them to check the
808 // data from the minidump afterwards.
809 const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
810 const char* kMemoryName = "a fake module";
811 const uint8_t kModuleGUID[sizeof(MDGUID)] = {
812 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
813 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
814 };
815 char module_identifier_buffer[kGUIDStringSize];
816 FileID::ConvertIdentifierToString(kModuleGUID,
817 module_identifier_buffer,
818 sizeof(module_identifier_buffer));
819 string module_identifier(module_identifier_buffer);
820 // Strip out dashes
821 size_t pos;
822 while ((pos = module_identifier.find('-')) != string::npos) {
823 module_identifier.erase(pos, 1);
824 }
825 // And append a zero, because module IDs include an "age" field
826 // which is always zero on Linux.
827 module_identifier += "0";
828
829 // Get some memory.
830 char* memory =
831 reinterpret_cast<char*>(mmap(NULL,
832 kMemorySize,
833 PROT_READ | PROT_WRITE,
834 MAP_PRIVATE | MAP_ANON,
835 -1,
836 0));
837 const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
838 ASSERT_TRUE(memory);
839
840 AutoTempDir temp_dir;
841 ExceptionHandler handler(
842 MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
843
844 // Add info about the anonymous memory mapping.
845 handler.AddMappingInfo(kMemoryName,
846 kModuleGUID,
847 kMemoryAddress,
848 kMemorySize,
849 0);
850 ASSERT_TRUE(handler.WriteMinidump());
851
852 const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor();
853 // Read the minidump. Load the module list, and ensure that the mmap'ed
854 // |memory| is listed with the given module name and debug ID.
855 Minidump minidump(minidump_desc.path());
856 ASSERT_TRUE(minidump.Read());
857
858 MinidumpModuleList* module_list = minidump.GetModuleList();
859 ASSERT_TRUE(module_list);
860 const MinidumpModule* module =
861 module_list->GetModuleForAddress(kMemoryAddress);
862 ASSERT_TRUE(module);
863
864 EXPECT_EQ(kMemoryAddress, module->base_address());
865 EXPECT_EQ(kMemorySize, module->size());
866 EXPECT_EQ(kMemoryName, module->code_file());
867 EXPECT_EQ(module_identifier, module->debug_identifier());
868
869 unlink(minidump_desc.path());
870 }
871
872 static const unsigned kControlMsgSize =
873 CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
874
875 static bool
CrashHandler(const void * crash_context,size_t crash_context_size,void * context)876 CrashHandler(const void* crash_context, size_t crash_context_size,
877 void* context) {
878 const int fd = (intptr_t) context;
879 int fds[2];
880 if (pipe(fds) == -1) {
881 // There doesn't seem to be any way to reliably handle
882 // this failure without the parent process hanging
883 // At least make sure that this process doesn't access
884 // unexpected file descriptors
885 fds[0] = -1;
886 fds[1] = -1;
887 }
888 struct kernel_msghdr msg = {0};
889 struct kernel_iovec iov;
890 iov.iov_base = const_cast<void*>(crash_context);
891 iov.iov_len = crash_context_size;
892 msg.msg_iov = &iov;
893 msg.msg_iovlen = 1;
894 char cmsg[kControlMsgSize];
895 memset(cmsg, 0, kControlMsgSize);
896 msg.msg_control = cmsg;
897 msg.msg_controllen = sizeof(cmsg);
898
899 struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
900 hdr->cmsg_level = SOL_SOCKET;
901 hdr->cmsg_type = SCM_RIGHTS;
902 hdr->cmsg_len = CMSG_LEN(sizeof(int));
903 *((int*) CMSG_DATA(hdr)) = fds[1];
904 hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr);
905 hdr->cmsg_level = SOL_SOCKET;
906 hdr->cmsg_type = SCM_CREDENTIALS;
907 hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred));
908 struct ucred *cred = reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
909 cred->uid = getuid();
910 cred->gid = getgid();
911 cred->pid = getpid();
912
913 ssize_t ret = HANDLE_EINTR(sys_sendmsg(fd, &msg, 0));
914 sys_close(fds[1]);
915 if (ret <= 0)
916 return false;
917
918 char b;
919 IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
920
921 return true;
922 }
923
924 #ifndef ADDRESS_SANITIZER
925
TEST(ExceptionHandlerTest,ExternalDumper)926 TEST(ExceptionHandlerTest, ExternalDumper) {
927 int fds[2];
928 ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
929 static const int on = 1;
930 setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
931 setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
932
933 const pid_t child = fork();
934 if (child == 0) {
935 close(fds[0]);
936 ExceptionHandler handler(MinidumpDescriptor("/tmp1"), NULL, NULL,
937 reinterpret_cast<void*>(fds[1]), true, -1);
938 handler.set_crash_handler(CrashHandler);
939 DoNullPointerDereference();
940 }
941 close(fds[1]);
942 struct msghdr msg = {0};
943 struct iovec iov;
944 static const unsigned kCrashContextSize =
945 sizeof(ExceptionHandler::CrashContext);
946 char context[kCrashContextSize];
947 char control[kControlMsgSize];
948 iov.iov_base = context;
949 iov.iov_len = kCrashContextSize;
950 msg.msg_iov = &iov;
951 msg.msg_iovlen = 1;
952 msg.msg_control = control;
953 msg.msg_controllen = kControlMsgSize;
954
955 const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0));
956 ASSERT_EQ(static_cast<ssize_t>(kCrashContextSize), n);
957 ASSERT_EQ(kControlMsgSize, msg.msg_controllen);
958 ASSERT_EQ(static_cast<__typeof__(msg.msg_flags)>(0), msg.msg_flags);
959 ASSERT_EQ(0, close(fds[0]));
960
961 pid_t crashing_pid = -1;
962 int signal_fd = -1;
963 for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
964 hdr = CMSG_NXTHDR(&msg, hdr)) {
965 if (hdr->cmsg_level != SOL_SOCKET)
966 continue;
967 if (hdr->cmsg_type == SCM_RIGHTS) {
968 const unsigned len = hdr->cmsg_len -
969 (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
970 ASSERT_EQ(sizeof(int), len);
971 signal_fd = *(reinterpret_cast<int*>(CMSG_DATA(hdr)));
972 } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
973 const struct ucred *cred =
974 reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
975 crashing_pid = cred->pid;
976 }
977 }
978
979 ASSERT_NE(crashing_pid, -1);
980 ASSERT_NE(signal_fd, -1);
981
982 AutoTempDir temp_dir;
983 string templ = temp_dir.path() + "/exception-handler-unittest";
984 ASSERT_TRUE(WriteMinidump(templ.c_str(), crashing_pid, context,
985 kCrashContextSize));
986 static const char b = 0;
987 ASSERT_EQ(1, (HANDLE_EINTR(write(signal_fd, &b, 1))));
988 ASSERT_EQ(0, close(signal_fd));
989
990 ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
991
992 struct stat st;
993 ASSERT_EQ(0, stat(templ.c_str(), &st));
994 ASSERT_GT(st.st_size, 0);
995 unlink(templ.c_str());
996 }
997
998 #endif // !ADDRESS_SANITIZER
999
TEST(ExceptionHandlerTest,WriteMinidumpExceptionStream)1000 TEST(ExceptionHandlerTest, WriteMinidumpExceptionStream) {
1001 AutoTempDir temp_dir;
1002 ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
1003 NULL, false, -1);
1004 ASSERT_TRUE(handler.WriteMinidump());
1005
1006 string minidump_path = handler.minidump_descriptor().path();
1007
1008 // Read the minidump and check the exception stream.
1009 Minidump minidump(minidump_path);
1010 ASSERT_TRUE(minidump.Read());
1011 MinidumpException* exception = minidump.GetException();
1012 ASSERT_TRUE(exception);
1013 const MDRawExceptionStream* raw = exception->exception();
1014 ASSERT_TRUE(raw);
1015 EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED,
1016 raw->exception_record.exception_code);
1017 }
1018
TEST(ExceptionHandlerTest,GenerateMultipleDumpsWithFD)1019 TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithFD) {
1020 AutoTempDir temp_dir;
1021 string path;
1022 const int fd = CreateTMPFile(temp_dir.path(), &path);
1023 ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, false, -1);
1024 ASSERT_TRUE(handler.WriteMinidump());
1025 // Check by the size of the data written to the FD that a minidump was
1026 // generated.
1027 off_t size = lseek(fd, 0, SEEK_CUR);
1028 ASSERT_GT(size, 0);
1029
1030 // Generate another minidump.
1031 ASSERT_TRUE(handler.WriteMinidump());
1032 size = lseek(fd, 0, SEEK_CUR);
1033 ASSERT_GT(size, 0);
1034 }
1035
TEST(ExceptionHandlerTest,GenerateMultipleDumpsWithPath)1036 TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithPath) {
1037 AutoTempDir temp_dir;
1038 ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
1039 NULL, false, -1);
1040 ASSERT_TRUE(handler.WriteMinidump());
1041
1042 const MinidumpDescriptor& minidump_1 = handler.minidump_descriptor();
1043 struct stat st;
1044 ASSERT_EQ(0, stat(minidump_1.path(), &st));
1045 ASSERT_GT(st.st_size, 0);
1046 string minidump_1_path(minidump_1.path());
1047 // Check it is a valid minidump.
1048 Minidump minidump1(minidump_1_path);
1049 ASSERT_TRUE(minidump1.Read());
1050 unlink(minidump_1.path());
1051
1052 // Generate another minidump, it should go to a different file.
1053 ASSERT_TRUE(handler.WriteMinidump());
1054 const MinidumpDescriptor& minidump_2 = handler.minidump_descriptor();
1055 ASSERT_EQ(0, stat(minidump_2.path(), &st));
1056 ASSERT_GT(st.st_size, 0);
1057 string minidump_2_path(minidump_2.path());
1058 // Check it is a valid minidump.
1059 Minidump minidump2(minidump_2_path);
1060 ASSERT_TRUE(minidump2.Read());
1061 unlink(minidump_2.path());
1062
1063 // 2 distinct files should be produced.
1064 ASSERT_STRNE(minidump_1_path.c_str(), minidump_2_path.c_str());
1065 }
1066
1067 // Test that an additional memory region can be added to the minidump.
TEST(ExceptionHandlerTest,AdditionalMemory)1068 TEST(ExceptionHandlerTest, AdditionalMemory) {
1069 const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
1070
1071 // Get some heap memory.
1072 uint8_t* memory = new uint8_t[kMemorySize];
1073 const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
1074 ASSERT_TRUE(memory);
1075
1076 // Stick some data into the memory so the contents can be verified.
1077 for (uint32_t i = 0; i < kMemorySize; ++i) {
1078 memory[i] = i % 255;
1079 }
1080
1081 AutoTempDir temp_dir;
1082 ExceptionHandler handler(
1083 MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
1084
1085 // Add the memory region to the list of memory to be included.
1086 handler.RegisterAppMemory(memory, kMemorySize);
1087 handler.WriteMinidump();
1088
1089 const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor();
1090
1091 // Read the minidump. Ensure that the memory region is present
1092 Minidump minidump(minidump_desc.path());
1093 ASSERT_TRUE(minidump.Read());
1094
1095 MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
1096 ASSERT_TRUE(dump_memory_list);
1097 const MinidumpMemoryRegion* region =
1098 dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
1099 ASSERT_TRUE(region);
1100
1101 EXPECT_EQ(kMemoryAddress, region->GetBase());
1102 EXPECT_EQ(kMemorySize, region->GetSize());
1103
1104 // Verify memory contents.
1105 EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
1106
1107 delete[] memory;
1108 }
1109
1110 // Test that a memory region that was previously registered
1111 // can be unregistered.
TEST(ExceptionHandlerTest,AdditionalMemoryRemove)1112 TEST(ExceptionHandlerTest, AdditionalMemoryRemove) {
1113 const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
1114
1115 // Get some heap memory.
1116 uint8_t* memory = new uint8_t[kMemorySize];
1117 const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
1118 ASSERT_TRUE(memory);
1119
1120 AutoTempDir temp_dir;
1121 ExceptionHandler handler(
1122 MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
1123
1124 // Add the memory region to the list of memory to be included.
1125 handler.RegisterAppMemory(memory, kMemorySize);
1126
1127 // ...and then remove it
1128 handler.UnregisterAppMemory(memory);
1129 handler.WriteMinidump();
1130
1131 const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor();
1132
1133 // Read the minidump. Ensure that the memory region is not present.
1134 Minidump minidump(minidump_desc.path());
1135 ASSERT_TRUE(minidump.Read());
1136
1137 MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
1138 ASSERT_TRUE(dump_memory_list);
1139 const MinidumpMemoryRegion* region =
1140 dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
1141 EXPECT_FALSE(region);
1142
1143 delete[] memory;
1144 }
1145
SimpleCallback(const MinidumpDescriptor & descriptor,void * context,bool succeeded)1146 static bool SimpleCallback(const MinidumpDescriptor& descriptor,
1147 void* context,
1148 bool succeeded) {
1149 string* filename = reinterpret_cast<string*>(context);
1150 *filename = descriptor.path();
1151 return true;
1152 }
1153
TEST(ExceptionHandlerTest,WriteMinidumpForChild)1154 TEST(ExceptionHandlerTest, WriteMinidumpForChild) {
1155 int fds[2];
1156 ASSERT_NE(-1, pipe(fds));
1157
1158 const pid_t child = fork();
1159 if (child == 0) {
1160 close(fds[1]);
1161 char b;
1162 HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
1163 close(fds[0]);
1164 syscall(__NR_exit);
1165 }
1166 close(fds[0]);
1167
1168 AutoTempDir temp_dir;
1169 string minidump_filename;
1170 ASSERT_TRUE(
1171 ExceptionHandler::WriteMinidumpForChild(child, child,
1172 temp_dir.path(), SimpleCallback,
1173 (void*)&minidump_filename));
1174
1175 Minidump minidump(minidump_filename);
1176 ASSERT_TRUE(minidump.Read());
1177 // Check that the crashing thread is the main thread of |child|
1178 MinidumpException* exception = minidump.GetException();
1179 ASSERT_TRUE(exception);
1180 uint32_t thread_id;
1181 ASSERT_TRUE(exception->GetThreadID(&thread_id));
1182 EXPECT_EQ(child, static_cast<int32_t>(thread_id));
1183
1184 const MDRawExceptionStream* raw = exception->exception();
1185 ASSERT_TRUE(raw);
1186 EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED,
1187 raw->exception_record.exception_code);
1188
1189 close(fds[1]);
1190 unlink(minidump_filename.c_str());
1191 }
1192