Commit 67429c37 authored by Mike Wittman's avatar Mike Wittman Committed by Commit Bot

Validate ModuleCache modules against /proc/self/maps

Adds a test that checks module properties against the corresponding
information in /proc/self/maps.

Bug: 1004855
Change-Id: I1c5981dadaba2991ea1e50b46e77bf9ea027236d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2314689
Commit-Queue: Mike Wittman <wittman@chromium.org>
Reviewed-by: default avatarEtienne Pierre-Doray <etiennep@chromium.org>
Cr-Commit-Position: refs/heads/master@{#795233}
parent 0c3954a6
...@@ -2,16 +2,24 @@ ...@@ -2,16 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <iomanip>
#include <map>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <vector>
#include "base/callback.h" #include "base/callback.h"
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
#include "base/profiler/module_cache.h" #include "base/profiler/module_cache.h"
#include "base/strings/string_piece.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_LINUX) || defined(OS_ANDROID)
#include "base/debug/proc_maps_linux.h"
#endif
namespace base { namespace base {
namespace { namespace {
...@@ -296,5 +304,84 @@ MAYBE_TEST(ModuleCacheTest, InvalidModule) { ...@@ -296,5 +304,84 @@ MAYBE_TEST(ModuleCacheTest, InvalidModule) {
EXPECT_EQ(nullptr, cache.GetModuleForAddress(1)); EXPECT_EQ(nullptr, cache.GetModuleForAddress(1));
} }
// arm64 module support is not implemented.
#if defined(OS_LINUX) || (defined(OS_ANDROID) && !defined(ARCH_CPU_ARM64))
// Validates that, for the memory regions listed in /proc/self/maps, the modules
// found via ModuleCache are consistent with those regions' extents.
TEST(ModuleCacheTest, CheckAgainstProcMaps) {
std::string proc_maps;
debug::ReadProcMaps(&proc_maps);
std::vector<debug::MappedMemoryRegion> regions;
ASSERT_TRUE(debug::ParseProcMaps(proc_maps, &regions));
// Map distinct paths to lists of regions for the path in increasing memory
// order.
using RegionVector = std::vector<const debug::MappedMemoryRegion*>;
using PathRegionsMap = std::map<StringPiece, RegionVector>;
PathRegionsMap path_regions;
for (const debug::MappedMemoryRegion& region : regions)
path_regions[region.path].push_back(&region);
const auto find_last_executable_region = [](const RegionVector& regions) {
const auto rloc = std::find_if(
regions.rbegin(), regions.rend(),
[](const debug::MappedMemoryRegion* region) {
return static_cast<bool>(region->permissions &
debug::MappedMemoryRegion::EXECUTE);
});
return rloc == regions.rend() ? nullptr : *rloc;
};
int module_count = 0;
// Loop through each distinct path.
for (const auto& path_regions_pair : path_regions) {
// Regions that aren't associated with absolute paths are unlikely to be
// part of modules.
if (path_regions_pair.first.empty() || path_regions_pair.first[0] != '/')
continue;
const debug::MappedMemoryRegion* const last_executable_region =
find_last_executable_region(path_regions_pair.second);
// The region isn't part of a module if no executable regions are associated
// with the same path.
if (!last_executable_region)
continue;
// Loop through all the regions associated with the path, checking that
// modules created for addresses in each region have the expected extents.
const uintptr_t expected_base_address =
path_regions_pair.second.front()->start;
for (const auto* region : path_regions_pair.second) {
ModuleCache cache;
const ModuleCache::Module* module =
cache.GetModuleForAddress(region->start);
// Not all regions matching the prior conditions are necessarily modules;
// things like resources are also mmapped into memory from files. Ignore
// any region isn't part of a module.
if (!module)
continue;
++module_count;
EXPECT_EQ(expected_base_address, module->GetBaseAddress());
// This needs an inequality comparison because the module size is computed
// based on the ELF section's actual extent, while the |proc_maps| region
// is aligned to a larger boundary.
EXPECT_LE(module->GetSize(),
last_executable_region->end - expected_base_address)
<< "base address: " << std::hex << module->GetBaseAddress()
<< std::endl
<< "region start: " << std::hex << region->start << std::endl
<< "region end: " << std::hex << region->end << std::endl;
}
}
// Linux should have at least this module and ld-linux.so. Android should have
// at least this module and system libraries.
EXPECT_GE(module_count, 2);
}
#endif
} // namespace } // namespace
} // namespace base } // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment