• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
SetUp()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    */
TestBody()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 
TEST_F(LinuxPtraceDumperChildTest,Setup)121 TEST_F(LinuxPtraceDumperChildTest, Setup) {
122   LinuxPtraceDumper dumper(getppid());
123 }
124 
TEST_F(LinuxPtraceDumperChildTest,FindMappings)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 
TEST_F(LinuxPtraceDumperChildTest,ThreadList)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:
StackHelper()153   StackHelper()
154     : fd_(-1), mapping_(NULL), size_(0) {}
~StackHelper()155   ~StackHelper() {
156     if (size_)
157       munmap(mapping_, size_);
158     if (fd_ >= 0)
159       close(fd_);
160   }
Init(int fd,char * mapping,size_t size)161   void Init(int fd, char* mapping, size_t size) {
162     fd_ = fd;
163     mapping_ = mapping;
164     size_ = size;
165   }
166 
mapping() const167   char* mapping() const { return mapping_; }
size() const168   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 
SetUp()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 
TEST_F(LinuxPtraceDumperMappingsTest,MergedMappings)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 
TEST_F(LinuxPtraceDumperChildTest,BuildProcPath)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.
TEST_F(LinuxPtraceDumperChildTest,MappingsIncludeLinuxGate)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.
TEST_F(LinuxPtraceDumperChildTest,LinuxGateMappingID)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 
TEST_F(LinuxPtraceDumperChildTest,FileIDsMatch)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