Home | History | Annotate | Download | only in minidump_writer
      1 // Copyright (c) 2009, 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 // linux_ptrace_dumper_unittest.cc:
     31 // Unit tests for google_breakpad::LinuxPtraceDumper.
     32 //
     33 // This file was renamed from linux_dumper_unittest.cc and modified due
     34 // to LinuxDumper being splitted into two classes.
     35 
     36 #include <errno.h>
     37 #include <fcntl.h>
     38 #include <limits.h>
     39 #include <unistd.h>
     40 #include <signal.h>
     41 #include <stdint.h>
     42 #include <string.h>
     43 #include <sys/mman.h>
     44 #include <sys/prctl.h>
     45 #include <sys/poll.h>
     46 #include <sys/stat.h>
     47 #include <sys/types.h>
     48 
     49 #include <string>
     50 
     51 #include "breakpad_googletest_includes.h"
     52 #include "client/linux/minidump_writer/linux_ptrace_dumper.h"
     53 #include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
     54 #include "common/linux/eintr_wrapper.h"
     55 #include "common/linux/file_id.h"
     56 #include "common/linux/ignore_ret.h"
     57 #include "common/linux/safe_readlink.h"
     58 #include "common/memory.h"
     59 #include "common/using_std_string.h"
     60 
     61 #ifndef PR_SET_PTRACER
     62 #define PR_SET_PTRACER 0x59616d61
     63 #endif
     64 
     65 using namespace google_breakpad;
     66 
     67 namespace {
     68 
     69 typedef testing::Test LinuxPtraceDumperTest;
     70 
     71 /* Fixture for running tests in a child process. */
     72 class LinuxPtraceDumperChildTest : public testing::Test {
     73  protected:
     74   virtual void SetUp() {
     75     child_pid_ = fork();
     76 #ifndef __ANDROID__
     77     prctl(PR_SET_PTRACER, child_pid_);
     78 #endif
     79   }
     80 
     81   /* Gtest is calling TestBody from this class, which sets up a child
     82    * process in which the RealTestBody virtual member is called.
     83    * As such, TestBody is not supposed to be overridden in derived classes.
     84    */
     85   virtual void TestBody() /* final */ {
     86     if (child_pid_ == 0) {
     87       // child process
     88       RealTestBody();
     89       exit(HasFatalFailure() ? kFatalFailure :
     90            (HasNonfatalFailure() ? kNonFatalFailure : 0));
     91     }
     92 
     93     ASSERT_TRUE(child_pid_ > 0);
     94     int status;
     95     waitpid(child_pid_, &status, 0);
     96     if (WEXITSTATUS(status) == kFatalFailure) {
     97       GTEST_FATAL_FAILURE_("Test failed in child process");
     98     } else if (WEXITSTATUS(status) == kNonFatalFailure) {
     99       GTEST_NONFATAL_FAILURE_("Test failed in child process");
    100     }
    101   }
    102 
    103   /* Gtest defines TestBody functions through its macros, but classes
    104    * derived from this one need to define RealTestBody instead.
    105    * This is achieved by defining a TestBody macro further below.
    106    */
    107   virtual void RealTestBody() = 0;
    108  private:
    109   static const int kFatalFailure = 1;
    110   static const int kNonFatalFailure = 2;
    111 
    112   pid_t child_pid_;
    113 };
    114 
    115 }  // namespace
    116 
    117 /* Replace TestBody declarations within TEST*() with RealTestBody
    118  * declarations */
    119 #define TestBody RealTestBody
    120 
    121 TEST_F(LinuxPtraceDumperChildTest, Setup) {
    122   LinuxPtraceDumper dumper(getppid());
    123 }
    124 
    125 TEST_F(LinuxPtraceDumperChildTest, FindMappings) {
    126   LinuxPtraceDumper dumper(getppid());
    127   ASSERT_TRUE(dumper.Init());
    128 
    129   ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
    130   ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
    131   ASSERT_FALSE(dumper.FindMapping(NULL));
    132 }
    133 
    134 TEST_F(LinuxPtraceDumperChildTest, ThreadList) {
    135   LinuxPtraceDumper dumper(getppid());
    136   ASSERT_TRUE(dumper.Init());
    137 
    138   ASSERT_GE(dumper.threads().size(), (size_t)1);
    139   bool found = false;
    140   for (size_t i = 0; i < dumper.threads().size(); ++i) {
    141     if (dumper.threads()[i] == getppid()) {
    142       ASSERT_FALSE(found);
    143       found = true;
    144     }
    145   }
    146   ASSERT_TRUE(found);
    147 }
    148 
    149 // Helper stack class to close a file descriptor and unmap
    150 // a mmap'ed mapping.
    151 class StackHelper {
    152  public:
    153   StackHelper()
    154     : fd_(-1), mapping_(NULL), size_(0) {}
    155   ~StackHelper() {
    156     if (size_)
    157       munmap(mapping_, size_);
    158     if (fd_ >= 0)
    159       close(fd_);
    160   }
    161   void Init(int fd, char* mapping, size_t size) {
    162     fd_ = fd;
    163     mapping_ = mapping;
    164     size_ = size;
    165   }
    166 
    167   char* mapping() const { return mapping_; }
    168   size_t size() const { return size_; }
    169 
    170  private:
    171   int fd_;
    172   char* mapping_;
    173   size_t size_;
    174 };
    175 
    176 class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest {
    177  protected:
    178   virtual void SetUp();
    179 
    180   string helper_path_;
    181   size_t page_size_;
    182   StackHelper helper_;
    183 };
    184 
    185 void LinuxPtraceDumperMappingsTest::SetUp() {
    186   helper_path_ = GetHelperBinary();
    187   if (helper_path_.empty()) {
    188     FAIL() << "Couldn't find helper binary";
    189     exit(1);
    190   }
    191 
    192   // mmap two segments out of the helper binary, one
    193   // enclosed in the other, but with different protections.
    194   page_size_ = sysconf(_SC_PAGESIZE);
    195   const size_t kMappingSize = 3 * page_size_;
    196   int fd = open(helper_path_.c_str(), O_RDONLY);
    197   ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_
    198                     << ", Error: " << strerror(errno);
    199   char* mapping =
    200     reinterpret_cast<char*>(mmap(NULL,
    201                                  kMappingSize,
    202                                  PROT_READ,
    203                                  MAP_SHARED,
    204                                  fd,
    205                                  0));
    206   ASSERT_TRUE(mapping);
    207 
    208   // Ensure that things get cleaned up.
    209   helper_.Init(fd, mapping, kMappingSize);
    210 
    211   // Carve a page out of the first mapping with different permissions.
    212   char* inside_mapping =  reinterpret_cast<char*>(
    213       mmap(mapping + 2 * page_size_,
    214            page_size_,
    215            PROT_NONE,
    216            MAP_SHARED | MAP_FIXED,
    217            fd,
    218            // Map a different offset just to
    219            // better test real-world conditions.
    220            page_size_));
    221   ASSERT_TRUE(inside_mapping);
    222 
    223   LinuxPtraceDumperChildTest::SetUp();
    224 }
    225 
    226 TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) {
    227   // Now check that LinuxPtraceDumper interpreted the mappings properly.
    228   LinuxPtraceDumper dumper(getppid());
    229   ASSERT_TRUE(dumper.Init());
    230   int mapping_count = 0;
    231   for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
    232     const MappingInfo& mapping = *dumper.mappings()[i];
    233     if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) {
    234       // This mapping should encompass the entire original mapped
    235       // range.
    236       EXPECT_EQ(reinterpret_cast<uintptr_t>(this->helper_.mapping()),
    237                 mapping.start_addr);
    238       EXPECT_EQ(this->helper_.size(), mapping.size);
    239       EXPECT_EQ(0U, mapping.offset);
    240       mapping_count++;
    241     }
    242   }
    243   EXPECT_EQ(1, mapping_count);
    244 }
    245 
    246 TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) {
    247   const pid_t pid = getppid();
    248   LinuxPtraceDumper dumper(pid);
    249 
    250   char maps_path[NAME_MAX] = "";
    251   char maps_path_expected[NAME_MAX];
    252   snprintf(maps_path_expected, sizeof(maps_path_expected),
    253            "/proc/%d/maps", pid);
    254   EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
    255   EXPECT_STREQ(maps_path_expected, maps_path);
    256 
    257   EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
    258   EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps"));
    259   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
    260   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
    261 
    262   char long_node[NAME_MAX];
    263   size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1;
    264   memset(long_node, 'a', long_node_len);
    265   long_node[long_node_len] = '\0';
    266   EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node));
    267 }
    268 
    269 #if !defined(__ARM_EABI__) && !defined(__mips__)
    270 // Ensure that the linux-gate VDSO is included in the mapping list.
    271 TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) {
    272   LinuxPtraceDumper dumper(getppid());
    273   ASSERT_TRUE(dumper.Init());
    274 
    275   void* linux_gate_loc =
    276     reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]);
    277   ASSERT_TRUE(linux_gate_loc);
    278   bool found_linux_gate = false;
    279 
    280   const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
    281   const MappingInfo* mapping;
    282   for (unsigned i = 0; i < mappings.size(); ++i) {
    283     mapping = mappings[i];
    284     if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
    285       found_linux_gate = true;
    286       break;
    287     }
    288   }
    289   EXPECT_TRUE(found_linux_gate);
    290   EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
    291   EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
    292 }
    293 
    294 // Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
    295 TEST_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) {
    296   LinuxPtraceDumper dumper(getppid());
    297   ASSERT_TRUE(dumper.Init());
    298 
    299   bool found_linux_gate = false;
    300   const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
    301   unsigned index = 0;
    302   for (unsigned i = 0; i < mappings.size(); ++i) {
    303     if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
    304       found_linux_gate = true;
    305       index = i;
    306       break;
    307     }
    308   }
    309   ASSERT_TRUE(found_linux_gate);
    310 
    311   // Need to suspend the child so ptrace actually works.
    312   ASSERT_TRUE(dumper.ThreadsSuspend());
    313   uint8_t identifier[sizeof(MDGUID)];
    314   ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
    315                                                  true,
    316                                                  index,
    317                                                  identifier));
    318   uint8_t empty_identifier[sizeof(MDGUID)];
    319   memset(empty_identifier, 0, sizeof(empty_identifier));
    320   EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
    321   EXPECT_TRUE(dumper.ThreadsResume());
    322 }
    323 #endif
    324 
    325 TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) {
    326   // Calculate the File ID of our binary using both
    327   // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
    328   // and ensure that we get the same result from both.
    329   char exe_name[PATH_MAX];
    330   ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
    331 
    332   LinuxPtraceDumper dumper(getppid());
    333   ASSERT_TRUE(dumper.Init());
    334   const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
    335   bool found_exe = false;
    336   unsigned i;
    337   for (i = 0; i < mappings.size(); ++i) {
    338     const MappingInfo* mapping = mappings[i];
    339     if (!strcmp(mapping->name, exe_name)) {
    340       found_exe = true;
    341       break;
    342     }
    343   }
    344   ASSERT_TRUE(found_exe);
    345 
    346   uint8_t identifier1[sizeof(MDGUID)];
    347   uint8_t identifier2[sizeof(MDGUID)];
    348   EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
    349                                                  identifier1));
    350   FileID fileid(exe_name);
    351   EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
    352   char identifier_string1[37];
    353   char identifier_string2[37];
    354   FileID::ConvertIdentifierToString(identifier1, identifier_string1,
    355                                     37);
    356   FileID::ConvertIdentifierToString(identifier2, identifier_string2,
    357                                     37);
    358   EXPECT_STREQ(identifier_string1, identifier_string2);
    359 }
    360 
    361 /* Get back to normal behavior of TEST*() macros wrt TestBody. */
    362 #undef TestBody
    363 
    364 // Comment out this test due to crosbug.com/6757.  Only seems to
    365 // fail on heavily loaded buildbots and is written with timing
    366 // assumptions.
    367 #if 0
    368 TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
    369   static const int kNumberOfThreadsInHelperProgram = 5;
    370   char kNumberOfThreadsArgument[2];
    371   sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
    372 
    373   int fds[2];
    374   ASSERT_NE(-1, pipe(fds));
    375 
    376   pid_t child_pid = fork();
    377   if (child_pid == 0) {
    378     // In child process.
    379     close(fds[0]);
    380 
    381     string helper_path(GetHelperBinary());
    382     if (helper_path.empty()) {
    383       FAIL() << "Couldn't find helper binary";
    384       exit(1);
    385     }
    386 
    387     // Pass the pipe fd and the number of threads as arguments.
    388     char pipe_fd_string[8];
    389     sprintf(pipe_fd_string, "%d", fds[1]);
    390     execl(helper_path.c_str(),
    391           "linux_dumper_unittest_helper",
    392           pipe_fd_string,
    393           kNumberOfThreadsArgument,
    394           NULL);
    395     // Kill if we get here.
    396     printf("Errno from exec: %d", errno);
    397     FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
    398     exit(0);
    399   }
    400   close(fds[1]);
    401 
    402   // Wait for all child threads to indicate that they have started
    403   for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
    404     struct pollfd pfd;
    405     memset(&pfd, 0, sizeof(pfd));
    406     pfd.fd = fds[0];
    407     pfd.events = POLLIN | POLLERR;
    408 
    409     const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
    410     ASSERT_EQ(1, r);
    411     ASSERT_TRUE(pfd.revents & POLLIN);
    412     uint8_t junk;
    413     ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
    414               static_cast<ssize_t>(sizeof(junk)));
    415   }
    416   close(fds[0]);
    417 
    418   // There is a race here because we may stop a child thread before
    419   // it is actually running the busy loop. Empirically this sleep
    420   // is sufficient to avoid the race.
    421   usleep(100000);
    422 
    423   // Children are ready now.
    424   LinuxPtraceDumper dumper(child_pid);
    425   ASSERT_TRUE(dumper.Init());
    426   EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
    427   EXPECT_TRUE(dumper.ThreadsSuspend());
    428 
    429   ThreadInfo one_thread;
    430   for (size_t i = 0; i < dumper.threads().size(); ++i) {
    431     EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread));
    432     const void* stack;
    433     size_t stack_len;
    434     EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len,
    435         one_thread.stack_pointer));
    436     // In the helper program, we stored a pointer to the thread id in a
    437     // specific register. Check that we can recover its value.
    438 #if defined(__ARM_EABI__)
    439     pid_t* process_tid_location = (pid_t*)(one_thread.regs.uregs[3]);
    440 #elif defined(__aarch64__)
    441     pid_t* process_tid_location = (pid_t*)(one_thread.regs.regs[3]);
    442 #elif defined(__i386)
    443     pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx);
    444 #elif defined(__x86_64)
    445     pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx);
    446 #elif defined(__mips__)
    447     pid_t* process_tid_location =
    448         reinterpret_cast<pid_t*>(one_thread.regs.regs[1]);
    449 #else
    450 #error This test has not been ported to this platform.
    451 #endif
    452     pid_t one_thread_id;
    453     dumper.CopyFromProcess(&one_thread_id,
    454                            dumper.threads()[i],
    455                            process_tid_location,
    456                            4);
    457     EXPECT_EQ(dumper.threads()[i], one_thread_id);
    458   }
    459   EXPECT_TRUE(dumper.ThreadsResume());
    460   kill(child_pid, SIGKILL);
    461 
    462   // Reap child
    463   int status;
    464   ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
    465   ASSERT_TRUE(WIFSIGNALED(status));
    466   ASSERT_EQ(SIGKILL, WTERMSIG(status));
    467 }
    468 #endif
    469