PageRenderTime 101ms CodeModel.GetById 14ms app.highlight 80ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/client/linux/handler/exception_handler_unittest.cc

http://github.com/tomahawk-player/tomahawk
C++ | 786 lines | 575 code | 113 blank | 98 comment | 39 complexity | 452ff11368949d81e1abfac83c4414d2 MD5 | raw file
  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
 39#include <string>
 40
 41#include "breakpad_googletest_includes.h"
 42#include "client/linux/handler/exception_handler.h"
 43#include "client/linux/minidump_writer/minidump_writer.h"
 44#include "common/linux/eintr_wrapper.h"
 45#include "common/linux/file_id.h"
 46#include "common/linux/linux_libc_support.h"
 47#include "common/tests/auto_tempdir.h"
 48#include "third_party/lss/linux_syscall_support.h"
 49#include "google_breakpad/processor/minidump.h"
 50
 51using namespace google_breakpad;
 52
 53// Length of a formatted GUID string =
 54// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
 55const int kGUIDStringSize = 37;
 56
 57static void sigchld_handler(int signo) { }
 58
 59class ExceptionHandlerTest : public ::testing::Test {
 60 protected:
 61  void SetUp() {
 62    // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN.
 63    struct sigaction sa;
 64    memset(&sa, 0, sizeof(sa));
 65    sa.sa_handler = sigchld_handler;
 66    ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1);
 67  }
 68
 69  void TearDown() {
 70    sigaction(SIGCHLD, &old_action, NULL);
 71  }
 72
 73  struct sigaction old_action;
 74};
 75
 76TEST(ExceptionHandlerTest, Simple) {
 77  AutoTempDir temp_dir;
 78  ExceptionHandler handler(temp_dir.path(), NULL, NULL, NULL, true);
 79}
 80
 81static bool DoneCallback(const char* dump_path,
 82                         const char* minidump_id,
 83                         void* context,
 84                         bool succeeded) {
 85  if (!succeeded)
 86    return succeeded;
 87
 88  int fd = (intptr_t) context;
 89  uint32_t len = my_strlen(minidump_id);
 90  HANDLE_EINTR(sys_write(fd, &len, sizeof(len)));
 91  HANDLE_EINTR(sys_write(fd, minidump_id, len));
 92  sys_close(fd);
 93
 94  return true;
 95}
 96
 97TEST(ExceptionHandlerTest, ChildCrash) {
 98  AutoTempDir temp_dir;
 99  int fds[2];
100  ASSERT_NE(pipe(fds), -1);
101
102  const pid_t child = fork();
103  if (child == 0) {
104    close(fds[0]);
105    ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, (void*) fds[1],
106                             true);
107    *reinterpret_cast<volatile int*>(NULL) = 0;
108  }
109  close(fds[1]);
110
111  int status;
112  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
113  ASSERT_TRUE(WIFSIGNALED(status));
114  ASSERT_EQ(WTERMSIG(status), SIGSEGV);
115
116  struct pollfd pfd;
117  memset(&pfd, 0, sizeof(pfd));
118  pfd.fd = fds[0];
119  pfd.events = POLLIN | POLLERR;
120
121  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
122  ASSERT_EQ(r, 1);
123  ASSERT_TRUE(pfd.revents & POLLIN);
124
125  uint32_t len;
126  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
127  ASSERT_LT(len, (uint32_t)2048);
128  char* filename = reinterpret_cast<char*>(malloc(len + 1));
129  ASSERT_EQ(read(fds[0], filename, len), len);
130  filename[len] = 0;
131  close(fds[0]);
132
133  const std::string minidump_filename = temp_dir.path() + "/" + filename +
134                                        ".dmp";
135
136  struct stat st;
137  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
138  ASSERT_GT(st.st_size, 0u);
139  unlink(minidump_filename.c_str());
140}
141
142// Test that memory around the instruction pointer is written
143// to the dump as a MinidumpMemoryRegion.
144TEST(ExceptionHandlerTest, InstructionPointerMemory) {
145  AutoTempDir temp_dir;
146  int fds[2];
147  ASSERT_NE(pipe(fds), -1);
148
149  // These are defined here so the parent can use them to check the
150  // data from the minidump afterwards.
151  const u_int32_t kMemorySize = 256;  // bytes
152  const int kOffset = kMemorySize / 2;
153  // This crashes with SIGILL on x86/x86-64/arm.
154  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
155
156  const pid_t child = fork();
157  if (child == 0) {
158    close(fds[0]);
159    ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback,
160                             (void*) fds[1], true);
161    // Get some executable memory.
162    char* memory =
163      reinterpret_cast<char*>(mmap(NULL,
164                                   kMemorySize,
165                                   PROT_READ | PROT_WRITE | PROT_EXEC,
166                                   MAP_PRIVATE | MAP_ANON,
167                                   -1,
168                                   0));
169    if (!memory)
170      exit(0);
171
172    // Write some instructions that will crash. Put them in the middle
173    // of the block of memory, because the minidump should contain 128
174    // bytes on either side of the instruction pointer.
175    memcpy(memory + kOffset, instructions, sizeof(instructions));
176
177    // Now execute the instructions, which should crash.
178    typedef void (*void_function)(void);
179    void_function memory_function =
180      reinterpret_cast<void_function>(memory + kOffset);
181    memory_function();
182  }
183  close(fds[1]);
184
185  int status;
186  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
187  ASSERT_TRUE(WIFSIGNALED(status));
188  ASSERT_EQ(WTERMSIG(status), SIGILL);
189
190  struct pollfd pfd;
191  memset(&pfd, 0, sizeof(pfd));
192  pfd.fd = fds[0];
193  pfd.events = POLLIN | POLLERR;
194
195  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
196  ASSERT_EQ(r, 1);
197  ASSERT_TRUE(pfd.revents & POLLIN);
198
199  uint32_t len;
200  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
201  ASSERT_LT(len, (uint32_t)2048);
202  char* filename = reinterpret_cast<char*>(malloc(len + 1));
203  ASSERT_EQ(read(fds[0], filename, len), len);
204  filename[len] = 0;
205  close(fds[0]);
206
207  const std::string minidump_filename = temp_dir.path() + "/" + filename +
208                                        ".dmp";
209
210  struct stat st;
211  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
212  ASSERT_GT(st.st_size, 0u);
213
214  // Read the minidump. Locate the exception record and the
215  // memory list, and then ensure that there is a memory region
216  // in the memory list that covers the instruction pointer from
217  // the exception record.
218  Minidump minidump(minidump_filename);
219  ASSERT_TRUE(minidump.Read());
220
221  MinidumpException* exception = minidump.GetException();
222  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
223  ASSERT_TRUE(exception);
224  ASSERT_TRUE(memory_list);
225  ASSERT_LT(0, memory_list->region_count());
226
227  MinidumpContext* context = exception->GetContext();
228  ASSERT_TRUE(context);
229
230  u_int64_t instruction_pointer;
231  switch (context->GetContextCPU()) {
232  case MD_CONTEXT_X86:
233    instruction_pointer = context->GetContextX86()->eip;
234    break;
235  case MD_CONTEXT_AMD64:
236    instruction_pointer = context->GetContextAMD64()->rip;
237    break;
238  case MD_CONTEXT_ARM:
239    instruction_pointer = context->GetContextARM()->iregs[15];
240    break;
241  default:
242    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
243    break;
244  }
245
246  MinidumpMemoryRegion* region =
247    memory_list->GetMemoryRegionForAddress(instruction_pointer);
248  ASSERT_TRUE(region);
249
250  EXPECT_EQ(kMemorySize, region->GetSize());
251  const u_int8_t* bytes = region->GetMemory();
252  ASSERT_TRUE(bytes);
253
254  u_int8_t prefix_bytes[kOffset];
255  u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
256  memset(prefix_bytes, 0, sizeof(prefix_bytes));
257  memset(suffix_bytes, 0, sizeof(suffix_bytes));
258  EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
259  EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
260  EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
261                     suffix_bytes, sizeof(suffix_bytes)) == 0);
262
263  unlink(minidump_filename.c_str());
264  free(filename);
265}
266
267// Test that the memory region around the instruction pointer is
268// bounded correctly on the low end.
269TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
270  AutoTempDir temp_dir;
271  int fds[2];
272  ASSERT_NE(pipe(fds), -1);
273
274  // These are defined here so the parent can use them to check the
275  // data from the minidump afterwards.
276  const u_int32_t kMemorySize = 256;  // bytes
277  const int kOffset = 0;
278  // This crashes with SIGILL on x86/x86-64/arm.
279  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
280
281  const pid_t child = fork();
282  if (child == 0) {
283    close(fds[0]);
284    ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback,
285                             (void*) fds[1], true);
286    // Get some executable memory.
287    char* memory =
288      reinterpret_cast<char*>(mmap(NULL,
289                                   kMemorySize,
290                                   PROT_READ | PROT_WRITE | PROT_EXEC,
291                                   MAP_PRIVATE | MAP_ANON,
292                                   -1,
293                                   0));
294    if (!memory)
295      exit(0);
296
297    // Write some instructions that will crash. Put them in the middle
298    // of the block of memory, because the minidump should contain 128
299    // bytes on either side of the instruction pointer.
300    memcpy(memory + kOffset, instructions, sizeof(instructions));
301
302    // Now execute the instructions, which should crash.
303    typedef void (*void_function)(void);
304    void_function memory_function =
305      reinterpret_cast<void_function>(memory + kOffset);
306    memory_function();
307  }
308  close(fds[1]);
309
310  int status;
311  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
312  ASSERT_TRUE(WIFSIGNALED(status));
313  ASSERT_EQ(WTERMSIG(status), SIGILL);
314
315  struct pollfd pfd;
316  memset(&pfd, 0, sizeof(pfd));
317  pfd.fd = fds[0];
318  pfd.events = POLLIN | POLLERR;
319
320  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
321  ASSERT_EQ(r, 1);
322  ASSERT_TRUE(pfd.revents & POLLIN);
323
324  uint32_t len;
325  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
326  ASSERT_LT(len, (uint32_t)2048);
327  char* filename = reinterpret_cast<char*>(malloc(len + 1));
328  ASSERT_EQ(read(fds[0], filename, len), len);
329  filename[len] = 0;
330  close(fds[0]);
331
332  const std::string minidump_filename = temp_dir.path() + "/" + filename +
333                                        ".dmp";
334
335  struct stat st;
336  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
337  ASSERT_GT(st.st_size, 0u);
338
339  // Read the minidump. Locate the exception record and the
340  // memory list, and then ensure that there is a memory region
341  // in the memory list that covers the instruction pointer from
342  // the exception record.
343  Minidump minidump(minidump_filename);
344  ASSERT_TRUE(minidump.Read());
345
346  MinidumpException* exception = minidump.GetException();
347  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
348  ASSERT_TRUE(exception);
349  ASSERT_TRUE(memory_list);
350  ASSERT_LT(0, memory_list->region_count());
351
352  MinidumpContext* context = exception->GetContext();
353  ASSERT_TRUE(context);
354
355  u_int64_t instruction_pointer;
356  switch (context->GetContextCPU()) {
357  case MD_CONTEXT_X86:
358    instruction_pointer = context->GetContextX86()->eip;
359    break;
360  case MD_CONTEXT_AMD64:
361    instruction_pointer = context->GetContextAMD64()->rip;
362    break;
363  case MD_CONTEXT_ARM:
364    instruction_pointer = context->GetContextARM()->iregs[15];
365    break;
366  default:
367    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
368    break;
369  }
370
371  MinidumpMemoryRegion* region =
372    memory_list->GetMemoryRegionForAddress(instruction_pointer);
373  ASSERT_TRUE(region);
374
375  EXPECT_EQ(kMemorySize / 2, region->GetSize());
376  const u_int8_t* bytes = region->GetMemory();
377  ASSERT_TRUE(bytes);
378
379  u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
380  memset(suffix_bytes, 0, sizeof(suffix_bytes));
381  EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
382  EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
383                     suffix_bytes, sizeof(suffix_bytes)) == 0);
384
385  unlink(minidump_filename.c_str());
386  free(filename);
387}
388
389// Test that the memory region around the instruction pointer is
390// bounded correctly on the high end.
391TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
392  AutoTempDir temp_dir;
393  int fds[2];
394  ASSERT_NE(pipe(fds), -1);
395
396  // These are defined here so the parent can use them to check the
397  // data from the minidump afterwards.
398  // Use 4k here because the OS will hand out a single page even
399  // if a smaller size is requested, and this test wants to
400  // test the upper bound of the memory range.
401  const u_int32_t kMemorySize = 4096;  // bytes
402  // This crashes with SIGILL on x86/x86-64/arm.
403  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
404  const int kOffset = kMemorySize - sizeof(instructions);
405
406  const pid_t child = fork();
407  if (child == 0) {
408    close(fds[0]);
409    ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback,
410                             (void*) fds[1], true);
411    // Get some executable memory.
412    char* memory =
413      reinterpret_cast<char*>(mmap(NULL,
414                                   kMemorySize,
415                                   PROT_READ | PROT_WRITE | PROT_EXEC,
416                                   MAP_PRIVATE | MAP_ANON,
417                                   -1,
418                                   0));
419    if (!memory)
420      exit(0);
421
422    // Write some instructions that will crash. Put them in the middle
423    // of the block of memory, because the minidump should contain 128
424    // bytes on either side of the instruction pointer.
425    memcpy(memory + kOffset, instructions, sizeof(instructions));
426
427    // Now execute the instructions, which should crash.
428    typedef void (*void_function)(void);
429    void_function memory_function =
430      reinterpret_cast<void_function>(memory + kOffset);
431    memory_function();
432  }
433  close(fds[1]);
434
435  int status;
436  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
437  ASSERT_TRUE(WIFSIGNALED(status));
438  ASSERT_EQ(WTERMSIG(status), SIGILL);
439
440  struct pollfd pfd;
441  memset(&pfd, 0, sizeof(pfd));
442  pfd.fd = fds[0];
443  pfd.events = POLLIN | POLLERR;
444
445  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
446  ASSERT_EQ(r, 1);
447  ASSERT_TRUE(pfd.revents & POLLIN);
448
449  uint32_t len;
450  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
451  ASSERT_LT(len, (uint32_t)2048);
452  char* filename = reinterpret_cast<char*>(malloc(len + 1));
453  ASSERT_EQ(read(fds[0], filename, len), len);
454  filename[len] = 0;
455  close(fds[0]);
456
457  const std::string minidump_filename = temp_dir.path() + "/" + filename +
458                                        ".dmp";
459
460  struct stat st;
461  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
462  ASSERT_GT(st.st_size, 0u);
463
464  // Read the minidump. Locate the exception record and the
465  // memory list, and then ensure that there is a memory region
466  // in the memory list that covers the instruction pointer from
467  // the exception record.
468  Minidump minidump(minidump_filename);
469  ASSERT_TRUE(minidump.Read());
470
471  MinidumpException* exception = minidump.GetException();
472  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
473  ASSERT_TRUE(exception);
474  ASSERT_TRUE(memory_list);
475  ASSERT_LT(0, memory_list->region_count());
476
477  MinidumpContext* context = exception->GetContext();
478  ASSERT_TRUE(context);
479
480  u_int64_t instruction_pointer;
481  switch (context->GetContextCPU()) {
482  case MD_CONTEXT_X86:
483    instruction_pointer = context->GetContextX86()->eip;
484    break;
485  case MD_CONTEXT_AMD64:
486    instruction_pointer = context->GetContextAMD64()->rip;
487    break;
488  case MD_CONTEXT_ARM:
489    instruction_pointer = context->GetContextARM()->iregs[15];
490    break;
491  default:
492    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
493    break;
494  }
495
496  MinidumpMemoryRegion* region =
497    memory_list->GetMemoryRegionForAddress(instruction_pointer);
498  ASSERT_TRUE(region);
499
500  const size_t kPrefixSize = 128;  // bytes
501  EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
502  const u_int8_t* bytes = region->GetMemory();
503  ASSERT_TRUE(bytes);
504
505  u_int8_t prefix_bytes[kPrefixSize];
506  memset(prefix_bytes, 0, sizeof(prefix_bytes));
507  EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
508  EXPECT_TRUE(memcmp(bytes + kPrefixSize,
509                     instructions, sizeof(instructions)) == 0);
510
511  unlink(minidump_filename.c_str());
512  free(filename);
513}
514
515// Ensure that an extra memory block doesn't get added when the
516// instruction pointer is not in mapped memory.
517TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
518  AutoTempDir temp_dir;
519  int fds[2];
520  ASSERT_NE(pipe(fds), -1);
521
522
523  const pid_t child = fork();
524  if (child == 0) {
525    close(fds[0]);
526    ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback,
527                             (void*) fds[1], true);
528    // Try calling a NULL pointer.
529    typedef void (*void_function)(void);
530    void_function memory_function =
531      reinterpret_cast<void_function>(NULL);
532    memory_function();
533  }
534  close(fds[1]);
535
536  int status;
537  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
538  ASSERT_TRUE(WIFSIGNALED(status));
539  ASSERT_EQ(WTERMSIG(status), SIGSEGV);
540
541  struct pollfd pfd;
542  memset(&pfd, 0, sizeof(pfd));
543  pfd.fd = fds[0];
544  pfd.events = POLLIN | POLLERR;
545
546  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
547  ASSERT_EQ(r, 1);
548  ASSERT_TRUE(pfd.revents & POLLIN);
549
550  uint32_t len;
551  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
552  ASSERT_LT(len, (uint32_t)2048);
553  char* filename = reinterpret_cast<char*>(malloc(len + 1));
554  ASSERT_EQ(read(fds[0], filename, len), len);
555  filename[len] = 0;
556  close(fds[0]);
557
558  const std::string minidump_filename = temp_dir.path() + "/" + filename +
559                                        ".dmp";
560
561  struct stat st;
562  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
563  ASSERT_GT(st.st_size, 0u);
564
565  // Read the minidump. Locate the exception record and the
566  // memory list, and then ensure that there is a memory region
567  // in the memory list that covers the instruction pointer from
568  // the exception record.
569  Minidump minidump(minidump_filename);
570  ASSERT_TRUE(minidump.Read());
571
572  MinidumpException* exception = minidump.GetException();
573  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
574  ASSERT_TRUE(exception);
575  ASSERT_TRUE(memory_list);
576  ASSERT_EQ((unsigned int)1, memory_list->region_count());
577
578  unlink(minidump_filename.c_str());
579  free(filename);
580}
581
582static bool SimpleCallback(const char* dump_path,
583                           const char* minidump_id,
584                           void* context,
585                           bool succeeded) {
586  if (!succeeded)
587    return succeeded;
588
589  string* minidump_file = reinterpret_cast<string*>(context);
590  minidump_file->append(dump_path);
591  minidump_file->append("/");
592  minidump_file->append(minidump_id);
593  minidump_file->append(".dmp");
594  return true;
595}
596
597// Test that anonymous memory maps can be annotated with names and IDs.
598TEST(ExceptionHandlerTest, ModuleInfo) {
599  // These are defined here so the parent can use them to check the
600  // data from the minidump afterwards.
601  const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
602  const char* kMemoryName = "a fake module";
603  const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
604    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
605    0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
606  };
607  char module_identifier_buffer[kGUIDStringSize];
608  FileID::ConvertIdentifierToString(kModuleGUID,
609                                    module_identifier_buffer,
610                                    sizeof(module_identifier_buffer));
611  string module_identifier(module_identifier_buffer);
612  // Strip out dashes
613  size_t pos;
614  while ((pos = module_identifier.find('-')) != string::npos) {
615    module_identifier.erase(pos, 1);
616  }
617  // And append a zero, because module IDs include an "age" field
618  // which is always zero on Linux.
619  module_identifier += "0";
620
621  // Get some memory.
622  char* memory =
623    reinterpret_cast<char*>(mmap(NULL,
624                                 kMemorySize,
625                                 PROT_READ | PROT_WRITE,
626                                 MAP_PRIVATE | MAP_ANON,
627                                 -1,
628                                 0));
629  const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
630  ASSERT_TRUE(memory);
631
632  string minidump_filename;
633  AutoTempDir temp_dir;
634  ExceptionHandler handler(temp_dir.path(), NULL, SimpleCallback,
635                           (void*)&minidump_filename, true);
636  // Add info about the anonymous memory mapping.
637  handler.AddMappingInfo(kMemoryName,
638                         kModuleGUID,
639                         kMemoryAddress,
640                         kMemorySize,
641                         0);
642  handler.WriteMinidump();
643
644  // Read the minidump. Load the module list, and ensure that
645  // the mmap'ed |memory| is listed with the given module name
646  // and debug ID.
647  Minidump minidump(minidump_filename);
648  ASSERT_TRUE(minidump.Read());
649
650  MinidumpModuleList* module_list = minidump.GetModuleList();
651  ASSERT_TRUE(module_list);
652  const MinidumpModule* module =
653    module_list->GetModuleForAddress(kMemoryAddress);
654  ASSERT_TRUE(module);
655
656  EXPECT_EQ(kMemoryAddress, module->base_address());
657  EXPECT_EQ(kMemorySize, module->size());
658  EXPECT_EQ(kMemoryName, module->code_file());
659  EXPECT_EQ(module_identifier, module->debug_identifier());
660
661  unlink(minidump_filename.c_str());
662}
663
664static const unsigned kControlMsgSize =
665    CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
666
667static bool
668CrashHandler(const void* crash_context, size_t crash_context_size,
669             void* context) {
670  const int fd = (intptr_t) context;
671  int fds[2];
672  if (pipe(fds) == -1) {
673    // There doesn't seem to be any way to reliably handle
674    // this failure without the parent process hanging
675    // At least make sure that this process doesn't access
676    // unexpected file descriptors
677    fds[0] = -1;
678    fds[1] = -1;
679  }
680  struct kernel_msghdr msg = {0};
681  struct kernel_iovec iov;
682  iov.iov_base = const_cast<void*>(crash_context);
683  iov.iov_len = crash_context_size;
684  msg.msg_iov = &iov;
685  msg.msg_iovlen = 1;
686  char cmsg[kControlMsgSize];
687  memset(cmsg, 0, kControlMsgSize);
688  msg.msg_control = cmsg;
689  msg.msg_controllen = sizeof(cmsg);
690
691  struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
692  hdr->cmsg_level = SOL_SOCKET;
693  hdr->cmsg_type = SCM_RIGHTS;
694  hdr->cmsg_len = CMSG_LEN(sizeof(int));
695  *((int*) CMSG_DATA(hdr)) = fds[1];
696  hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr);
697  hdr->cmsg_level = SOL_SOCKET;
698  hdr->cmsg_type = SCM_CREDENTIALS;
699  hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred));
700  struct ucred *cred = reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
701  cred->uid = getuid();
702  cred->gid = getgid();
703  cred->pid = getpid();
704
705  HANDLE_EINTR(sys_sendmsg(fd, &msg, 0));
706  sys_close(fds[1]);
707
708  char b;
709  HANDLE_EINTR(sys_read(fds[0], &b, 1));
710
711  return true;
712}
713
714TEST(ExceptionHandlerTest, ExternalDumper) {
715  int fds[2];
716  ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
717  static const int on = 1;
718  setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
719  setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
720
721  const pid_t child = fork();
722  if (child == 0) {
723    close(fds[0]);
724    ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true);
725    handler.set_crash_handler(CrashHandler);
726    *reinterpret_cast<volatile int*>(NULL) = 0;
727  }
728  close(fds[1]);
729  struct msghdr msg = {0};
730  struct iovec iov;
731  static const unsigned kCrashContextSize =
732      sizeof(ExceptionHandler::CrashContext);
733  char context[kCrashContextSize];
734  char control[kControlMsgSize];
735  iov.iov_base = context;
736  iov.iov_len = kCrashContextSize;
737  msg.msg_iov = &iov;
738  msg.msg_iovlen = 1;
739  msg.msg_control = control;
740  msg.msg_controllen = kControlMsgSize;
741
742  const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0));
743  ASSERT_EQ(n, kCrashContextSize);
744  ASSERT_EQ(msg.msg_controllen, kControlMsgSize);
745  ASSERT_EQ(msg.msg_flags, 0);
746  ASSERT_EQ(close(fds[0]), 0);
747
748  pid_t crashing_pid = -1;
749  int signal_fd = -1;
750  for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
751       hdr = CMSG_NXTHDR(&msg, hdr)) {
752    if (hdr->cmsg_level != SOL_SOCKET)
753      continue;
754    if (hdr->cmsg_type == SCM_RIGHTS) {
755      const unsigned len = hdr->cmsg_len -
756          (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
757      ASSERT_EQ(len, sizeof(int));
758      signal_fd = *((int *) CMSG_DATA(hdr));
759    } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
760      const struct ucred *cred =
761          reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
762      crashing_pid = cred->pid;
763    }
764  }
765
766  ASSERT_NE(crashing_pid, -1);
767  ASSERT_NE(signal_fd, -1);
768
769  AutoTempDir temp_dir;
770  std::string templ = temp_dir.path() + "/exception-handler-unittest";
771  ASSERT_TRUE(WriteMinidump(templ.c_str(), crashing_pid, context,
772                            kCrashContextSize));
773  static const char b = 0;
774  HANDLE_EINTR(write(signal_fd, &b, 1));
775  ASSERT_EQ(close(signal_fd), 0);
776
777  int status;
778  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
779  ASSERT_TRUE(WIFSIGNALED(status));
780  ASSERT_EQ(WTERMSIG(status), SIGSEGV);
781
782  struct stat st;
783  ASSERT_EQ(stat(templ.c_str(), &st), 0);
784  ASSERT_GT(st.st_size, 0u);
785  unlink(templ.c_str());
786}