google-breakpad: uprev breakpad to 875

Change-Id: Ia2403f9eb2db173035b5f511b169723a3fb7c439
diff --git a/src/client/linux/minidump_writer/linux_dumper_unittest.cc b/src/client/linux/minidump_writer/linux_dumper_unittest.cc
index 323a23a..fb287ca 100644
--- a/src/client/linux/minidump_writer/linux_dumper_unittest.cc
+++ b/src/client/linux/minidump_writer/linux_dumper_unittest.cc
@@ -29,31 +29,45 @@
 
 #include <string>
 
+#include <fcntl.h>
 #include <limits.h>
 #include <unistd.h>
 #include <signal.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 
 #include "breakpad_googletest_includes.h"
 #include "client/linux/minidump_writer/linux_dumper.h"
+#include "common/linux/eintr_wrapper.h"
 #include "common/linux/file_id.h"
 #include "common/memory.h"
 
 using std::string;
 using namespace google_breakpad;
 
-// This provides a wrapper around system calls which may be
-// interrupted by a signal and return EINTR. See man 7 signal.
-#define HANDLE_EINTR(x) ({ \
-  typeof(x) __eintr_result__; \
-  do { \
-    __eintr_result__ = x; \
-  } while (__eintr_result__ == -1 && errno == EINTR); \
-  __eintr_result__;\
-})
-
 namespace {
 typedef testing::Test LinuxDumperTest;
+
+string GetHelperBinary() {
+  // Locate helper binary next to the current binary.
+  char self_path[PATH_MAX];
+  if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
+    return "";
+  }
+  string helper_path(self_path);
+  size_t pos = helper_path.rfind('/');
+  if (pos == string::npos) {
+    return "";
+  }
+  helper_path.erase(pos + 1);
+  helper_path += "linux_dumper_unittest_helper";
+
+  return helper_path;
+}
+
 }
 
 TEST(LinuxDumperTest, Setup) {
@@ -83,7 +97,80 @@
   }
 }
 
-// Comment out this test due to crosbug.com/6712.  Only seems to
+// Helper stack class to close a file descriptor and unmap
+// a mmap'ed mapping.
+class StackHelper {
+public:
+  StackHelper(int fd, char* mapping, size_t size)
+    : fd_(fd), mapping_(mapping), size_(size) {}
+  ~StackHelper() {
+    munmap(mapping_, size_);
+    close(fd_);
+  }
+
+private:
+  int fd_;
+  char* mapping_;
+  size_t size_;
+};
+
+TEST(LinuxDumperTest, MergedMappings) {
+  string helper_path(GetHelperBinary());
+  if (helper_path.empty()) {
+    FAIL() << "Couldn't find helper binary";
+    exit(1);
+  }
+
+  // mmap two segments out of the helper binary, one
+  // enclosed in the other, but with different protections.
+  const size_t kPageSize = sysconf(_SC_PAGESIZE);
+  const size_t kMappingSize = 3 * kPageSize;
+  int fd = open(helper_path.c_str(), O_RDONLY);
+  ASSERT_NE(-1, fd);
+  char* mapping =
+    reinterpret_cast<char*>(mmap(NULL,
+                                 kMappingSize,
+                                 PROT_READ,
+                                 MAP_SHARED,
+                                 fd,
+                                 0));
+  ASSERT_TRUE(mapping);
+
+  const u_int64_t kMappingAddress = reinterpret_cast<u_int64_t>(mapping);
+
+  // Ensure that things get cleaned up.
+  StackHelper helper(fd, mapping, kMappingSize);
+
+  // Carve a page out of the first mapping with different permissions.
+  char* inside_mapping =  reinterpret_cast<char*>(mmap(mapping + 2 *kPageSize,
+                                 kPageSize,
+                                 PROT_NONE,
+                                 MAP_SHARED | MAP_FIXED,
+                                 fd,
+                                 // Map a different offset just to
+                                 // better test real-world conditions.
+                                 kPageSize));
+  ASSERT_TRUE(inside_mapping);
+
+  // Now check that LinuxDumper interpreted the mappings properly.
+  LinuxDumper dumper(getpid());
+  ASSERT_TRUE(dumper.Init());
+  int mapping_count = 0;
+  for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
+    const MappingInfo& mapping = *dumper.mappings()[i];
+    if (strcmp(mapping.name, helper_path.c_str()) == 0) {
+      // This mapping should encompass the entire original mapped
+      // range.
+      EXPECT_EQ(kMappingAddress, mapping.start_addr);
+      EXPECT_EQ(kMappingSize, mapping.size);
+      EXPECT_EQ(0, mapping.offset);
+      mapping_count++;
+    }
+  }
+  EXPECT_EQ(1, mapping_count);
+}
+
+// Comment out this test due to crosbug.com/6757.  Only seems to
 // fail on heavily loaded buildbots and is written with timing
 // assumptions.
 #if 0
@@ -92,26 +179,26 @@
   char kNumberOfThreadsArgument[2];
   sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
 
+  int fds[2];
+  ASSERT_NE(-1, pipe(fds));
+
   pid_t child_pid = fork();
   if (child_pid == 0) {
-    // Locate helper binary next to the current binary.
-    char self_path[PATH_MAX];
-    if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
-      FAIL() << "readlink failed: " << strerror(errno);
-      exit(1);
-    }
-    string helper_path(self_path);
-    size_t pos = helper_path.rfind('/');
-    if (pos == string::npos) {
-      FAIL() << "no trailing slash in path: " << helper_path;
-      exit(1);
-    }
-    helper_path.erase(pos + 1);
-    helper_path += "linux_dumper_unittest_helper";
+    // In child process.
+    close(fds[0]);
 
-    // Set the number of threads
+    string helper_path(GetHelperBinary());
+    if (helper_path.empty()) {
+      FAIL() << "Couldn't find helper binary";
+      exit(1);
+    }
+
+    // Pass the pipe fd and the number of threads as arguments.
+    char pipe_fd_string[8];
+    sprintf(pipe_fd_string, "%d", fds[1]);
     execl(helper_path.c_str(),
           "linux_dumper_unittest_helper",
+          pipe_fd_string,
           kNumberOfThreadsArgument,
           NULL);
     // Kill if we get here.
@@ -119,9 +206,21 @@
     FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
     exit(0);
   }
-  // The sleep is flaky, but prevents us from reading
-  // the child process before all threads have been created.
-  sleep(1);
+  close(fds[1]);
+  // Wait for the child process to signal that it's ready.
+  struct pollfd pfd;
+  memset(&pfd, 0, sizeof(pfd));
+  pfd.fd = fds[0];
+  pfd.events = POLLIN | POLLERR;
+
+  const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
+  ASSERT_EQ(1, r);
+  ASSERT_TRUE(pfd.revents & POLLIN);
+  uint8_t junk;
+  read(fds[0], &junk, sizeof(junk));
+  close(fds[0]);
+
+  // Child is ready now.
   LinuxDumper dumper(child_pid);
   ASSERT_TRUE(dumper.Init());
   EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
@@ -182,6 +281,7 @@
 }
 
 #if !defined(__ARM_EABI__)
+// Ensure that the linux-gate VDSO is included in the mapping list.
 TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
   LinuxDumper dumper(getpid());
   ASSERT_TRUE(dumper.Init());
@@ -203,6 +303,80 @@
   EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
   EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
 }
+
+// Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
+TEST(LinuxDumperTest, LinuxGateMappingID) {
+  LinuxDumper dumper(getpid());
+  ASSERT_TRUE(dumper.Init());
+
+  bool found_linux_gate = false;
+  const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
+  unsigned index = 0;
+  for (unsigned i = 0; i < mappings.size(); ++i) {
+    if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
+      found_linux_gate = true;
+      index = i;
+      break;
+    }
+  }
+  ASSERT_TRUE(found_linux_gate);
+
+  uint8_t identifier[sizeof(MDGUID)];
+  ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
+                                                 true,
+                                                 index,
+                                                 identifier));
+  uint8_t empty_identifier[sizeof(MDGUID)];
+  memset(empty_identifier, 0, sizeof(empty_identifier));
+  EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
+}
+
+// Ensure that the linux-gate VDSO can generate a non-zeroed File ID
+// from a child process.
+TEST(LinuxDumperTest, LinuxGateMappingIDChild) {
+  int fds[2];
+  ASSERT_NE(-1, pipe(fds));
+
+  // Fork a child so ptrace works.
+  const pid_t child = fork();
+  if (child == 0) {
+    close(fds[1]);
+    // Now wait forever for the parent.
+    char b;
+    HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
+    close(fds[0]);
+    syscall(__NR_exit);
+  }
+  close(fds[0]);
+
+  LinuxDumper dumper(child);
+  ASSERT_TRUE(dumper.Init());
+
+  bool found_linux_gate = false;
+  const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
+  unsigned index = 0;
+  for (unsigned i = 0; i < mappings.size(); ++i) {
+    if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
+      found_linux_gate = true;
+      index = i;
+      break;
+    }
+  }
+  ASSERT_TRUE(found_linux_gate);
+
+  // Need to suspend the child so ptrace actually works.
+  ASSERT_TRUE(dumper.ThreadsSuspend());
+  uint8_t identifier[sizeof(MDGUID)];
+  ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
+                                                 true,
+                                                 index,
+                                                 identifier));
+  uint8_t empty_identifier[sizeof(MDGUID)];
+  memset(empty_identifier, 0, sizeof(empty_identifier));
+  EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
+  EXPECT_TRUE(dumper.ThreadsResume());
+  close(fds[1]);
+}
 #endif
 
 TEST(LinuxDumperTest, FileIDsMatch) {
@@ -217,11 +391,11 @@
   int fds[2];
   ASSERT_NE(-1, pipe(fds));
 
-  // fork a child so we can ptrace it
+  // Fork a child so ptrace works.
   const pid_t child = fork();
   if (child == 0) {
     close(fds[1]);
-    // now wait forever for the parent
+    // Now wait forever for the parent.
     char b;
     HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
     close(fds[0]);
@@ -245,7 +419,8 @@
 
   uint8_t identifier1[sizeof(MDGUID)];
   uint8_t identifier2[sizeof(MDGUID)];
-  EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(i, identifier1));
+  EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
+                                                 identifier1));
   FileID fileid(exe_name);
   EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
   char identifier_string1[37];