Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit de5cd8cc authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Fix global finding logic.

Recently, the maps for an elf in memory might show up looking like:

  f0000-f1000 0 r-- /system/lib/libc.so
  f1000-f2000 0 ---
  f2000-f3000 1000 r-x /system/lib/libc.so
  f3000-f4000 2000 rw- /system/lib/libc.so

That empty map was confusing the logic when looking for a global
variable. Now this case is handled properly.

New unit test added for this case.

Bug: 147910661

Test: Ran unit tests.
Test: Ran original failing test 137-cfi.
Change-Id: Ida2e96d1da5e1bf61f41646949fe5a2d405c0d61
parent a78d0cb7
Loading
Loading
Loading
Loading
+18 −20
Original line number Diff line number Diff line
@@ -70,12 +70,16 @@ void Global::FindAndReadVariable(Maps* maps, const char* var_str) {
  // This also works:
  //   f0000-f2000 0 r-- /system/lib/libc.so
  //   f2000-f3000 2000 rw- /system/lib/libc.so
  MapInfo* map_start = nullptr;
  // It is also possible to see empty maps after the read-only like so:
  //   f0000-f1000 0 r-- /system/lib/libc.so
  //   f1000-f2000 0 ---
  //   f2000-f3000 1000 r-x /system/lib/libc.so
  //   f3000-f4000 2000 rw- /system/lib/libc.so
  MapInfo* map_zero = nullptr;
  for (const auto& info : *maps) {
    if (map_start != nullptr && map_start->name == info->name) {
      if (info->offset != 0 &&
          (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
        Elf* elf = map_start->GetElf(memory_, arch());
    if (info->offset != 0 && (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
        map_zero != nullptr && Searchable(info->name) && info->name == map_zero->name) {
      Elf* elf = map_zero->GetElf(memory_, arch());
      uint64_t ptr;
      if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
        uint64_t offset_end = info->offset + info->end - info->start;
@@ -86,14 +90,8 @@ void Global::FindAndReadVariable(Maps* maps, const char* var_str) {
          }
        }
      }
        map_start = nullptr;
      }
    } else {
      map_start = nullptr;
    }
    if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
        Searchable(info->name)) {
      map_start = info.get();
    } else if (info->offset == 0 && !info->name.empty()) {
      map_zero = info.get();
    }
  }
}
+26 −1
Original line number Diff line number Diff line
@@ -64,7 +64,11 @@ class DexFilesTest : public ::testing::Test {
                       "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
                       "100000-110000 rw-p 00f1000 00:00 0 /fake/elf3\n"
                       "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
                       "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"));
                       "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"
                       "500000-501000 r--p 0000000 00:00 0 /fake/elf4\n"
                       "501000-502000 ---p 0000000 00:00 0\n"
                       "503000-510000 rw-p 0003000 00:00 0 /fake/elf4\n"
                       "510000-520000 rw-p 0010000 00:00 0 /fake/elf4\n"));
    ASSERT_TRUE(maps_->Parse());

    // Global variable in a section that is not readable.
@@ -81,6 +85,11 @@ class DexFilesTest : public ::testing::Test {
    map_info = maps_->Get(kMapGlobal);
    ASSERT_TRUE(map_info != nullptr);
    CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);

    // Global variable set in this map, but there is an empty map before rw map.
    map_info = maps_->Get(kMapGlobalAfterEmpty);
    ASSERT_TRUE(map_info != nullptr);
    CreateFakeElf(map_info, 0x3800, 0x3000, 0x3000, 0xd000);
  }

  void SetUp() override {
@@ -102,6 +111,8 @@ class DexFilesTest : public ::testing::Test {
  static constexpr size_t kMapGlobalRw = 6;
  static constexpr size_t kMapDexFileEntries = 7;
  static constexpr size_t kMapDexFiles = 8;
  static constexpr size_t kMapGlobalAfterEmpty = 9;
  static constexpr size_t kMapDexFilesAfterEmpty = 12;

  std::shared_ptr<Memory> process_memory_;
  MemoryFake* memory_;
@@ -328,4 +339,18 @@ TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
  EXPECT_EQ(0x123U, method_offset);
}

TEST_F(DexFilesTest, get_method_information_with_empty_map) {
  std::string method_name = "nothing";
  uint64_t method_offset = 0x124;
  MapInfo* info = maps_->Get(kMapDexFilesAfterEmpty);

  WriteDescriptor32(0x503800, 0x506000);
  WriteEntry32(0x506000, 0, 0, 0x510000);
  WriteDex(0x510000);

  dex_files_->GetMethodInformation(maps_.get(), info, 0x510100, &method_name, &method_offset);
  EXPECT_EQ("Main.<init>", method_name);
  EXPECT_EQ(0U, method_offset);
}

}  // namespace unwindstack