elf: adjust small DT_STRTAB addresses by load bias

Change-Id: I6671bf5b8ec9922402b44c6d30c4804115f07a17
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2229114
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
GitOrigin-RevId: 294d233ca09e33e55e6d2419f58199d3a4ab6c32
diff --git a/snapshot/elf/elf_image_reader.cc b/snapshot/elf/elf_image_reader.cc
index 7f6d7c7..1f361ac 100644
--- a/snapshot/elf/elf_image_reader.cc
+++ b/snapshot/elf/elf_image_reader.cc
@@ -589,6 +589,14 @@
     return false;
   }
 
+  // GNU ld.so doesn't adjust the vdso's dynamic array entries by the load bias.
+  // If the address is too small to point into the loaded module range and is
+  // small enough to be an offset from the base of the module, adjust it now.
+  if (string_table_address < memory_.Base() &&
+      string_table_address < memory_.Size()) {
+    string_table_address += GetLoadBias();
+  }
+
   if (!memory_.ReadCStringSizeLimited(
           string_table_address + offset, string_table_size - offset, string)) {
     LOG(ERROR) << "missing nul-terminator";
diff --git a/snapshot/linux/process_reader_linux_test.cc b/snapshot/linux/process_reader_linux_test.cc
index 0f53393..e5710ac 100644
--- a/snapshot/linux/process_reader_linux_test.cc
+++ b/snapshot/linux/process_reader_linux_test.cc
@@ -535,7 +535,8 @@
 #endif  // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21
 }
 
-bool WriteTestModule(const base::FilePath& module_path) {
+bool WriteTestModule(const base::FilePath& module_path,
+                     const std::string& soname) {
 #if defined(ARCH_CPU_64_BITS)
   using Ehdr = Elf64_Ehdr;
   using Phdr = Elf64_Phdr;
@@ -565,6 +566,7 @@
       Dyn symtab;
       Dyn strsz;
       Dyn syment;
+      Dyn soname;
       Dyn null;
     } dynamic_array;
     struct {
@@ -573,8 +575,7 @@
       Elf32_Word bucket;
       Elf32_Word chain;
     } hash_table;
-    struct {
-    } string_table;
+    char string_table[32];
     struct {
     } section_header_string_table;
     struct {
@@ -671,6 +672,9 @@
   module.dynamic_array.strsz.d_un.d_val = sizeof(module.string_table);
   module.dynamic_array.syment.d_tag = DT_SYMENT;
   module.dynamic_array.syment.d_un.d_val = sizeof(Sym);
+  constexpr size_t kSonameOffset = 1;
+  module.dynamic_array.soname.d_tag = DT_SONAME;
+  module.dynamic_array.soname.d_un.d_val = kSonameOffset;
 
   module.dynamic_array.null.d_tag = DT_NULL;
 
@@ -679,6 +683,10 @@
   module.hash_table.bucket = 0;
   module.hash_table.chain = 0;
 
+  CHECK_GE(sizeof(module.string_table), soname.size() + 2);
+  module.string_table[0] = '\0';
+  memcpy(&module.string_table[kSonameOffset], soname.c_str(), soname.size());
+
   module.shdr_table.null.sh_type = SHT_NULL;
 
   module.shdr_table.dynamic.sh_name = 0;
@@ -716,11 +724,12 @@
   return true;
 }
 
-ScopedModuleHandle LoadTestModule(const std::string& module_name) {
+ScopedModuleHandle LoadTestModule(const std::string& module_name,
+                                  const std::string& module_soname) {
   base::FilePath module_path(
       TestPaths::Executable().DirName().Append(module_name));
 
-  if (!WriteTestModule(module_path)) {
+  if (!WriteTestModule(module_path, module_soname)) {
     return ScopedModuleHandle(nullptr);
   }
   EXPECT_TRUE(IsRegularFile(module_path));
@@ -756,7 +765,9 @@
 
 TEST(ProcessReaderLinux, SelfModules) {
   const std::string module_name = "test_module.so";
-  ScopedModuleHandle empty_test_module(LoadTestModule(module_name));
+  const std::string module_soname = "test_module_soname";
+  ScopedModuleHandle empty_test_module(
+      LoadTestModule(module_name, module_soname));
   ASSERT_TRUE(empty_test_module.valid());
 
   FakePtraceConnection connection;
@@ -766,12 +777,12 @@
   ASSERT_TRUE(process_reader.Initialize(&connection));
 
   ExpectModulesFromSelf(process_reader.Modules());
-  ExpectTestModule(&process_reader, module_name);
+  ExpectTestModule(&process_reader, module_soname);
 }
 
 class ChildModuleTest : public Multiprocess {
  public:
-  ChildModuleTest() : Multiprocess(), module_name_("test_module.so") {}
+  ChildModuleTest() : Multiprocess(), module_soname_("test_module_soname") {}
   ~ChildModuleTest() = default;
 
  private:
@@ -786,11 +797,12 @@
     ASSERT_TRUE(process_reader.Initialize(&connection));
 
     ExpectModulesFromSelf(process_reader.Modules());
-    ExpectTestModule(&process_reader, module_name_);
+    ExpectTestModule(&process_reader, module_soname_);
   }
 
   void MultiprocessChild() override {
-    ScopedModuleHandle empty_test_module(LoadTestModule(module_name_));
+    ScopedModuleHandle empty_test_module(
+        LoadTestModule("test_module.so", module_soname_));
     ASSERT_TRUE(empty_test_module.valid());
 
     char c = 0;
@@ -799,7 +811,7 @@
     CheckedReadFileAtEOF(ReadPipeHandle());
   }
 
-  const std::string module_name_;
+  const std::string module_soname_;
 
   DISALLOW_COPY_AND_ASSIGN(ChildModuleTest);
 };