Commit 9d57abf2 authored by Vlad Tsyrklevich's avatar Vlad Tsyrklevich Committed by Commit Bot

GWP-ASan: Add sanitization support

Android WebView has a stricter privacy model because it runs in
embedders memory spaces. To ensure crash dumps don't include information
from the embedding process, additional sanitization is performed on the
crash dump.

To make GWP-ASan work under sanitization, white list GWP-ASan memory
regions that are required by the crash handler. Furthermore, add support
for testing under sanitization on Linux/Android (the only platforms that
currently support sanitization.)

CQ-DEPEND=chromium:1756705

Bug: 973167
Change-Id: I6a48b17bd128f44ffd62fe0f180db79c175a561b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1757160Reviewed-by: default avatarMark Mentovai <mark@chromium.org>
Reviewed-by: default avatarVitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Vlad Tsyrklevich <vtsyrklevich@chromium.org>
Cr-Commit-Position: refs/heads/master@{#688159}
parent cf20cf4d
...@@ -45,6 +45,10 @@ component("client") { ...@@ -45,6 +45,10 @@ component("client") {
"//components/crash/core/common:crash_key", "//components/crash/core/common:crash_key",
"//components/gwp_asan/common", "//components/gwp_asan/common",
] ]
if (is_android) {
deps += [ "//components/crash/content/app" ]
}
} }
source_set("unit_tests") { source_set("unit_tests") {
......
include_rules = [ include_rules = [
"+components/crash/core/common/crash_key.h", "+components/crash/core/common/crash_key.h",
"+components/crash/content/app/crashpad.h",
] ]
...@@ -21,6 +21,10 @@ ...@@ -21,6 +21,10 @@
#include "components/gwp_asan/common/crash_key_name.h" #include "components/gwp_asan/common/crash_key_name.h"
#include "components/gwp_asan/common/pack_stack_trace.h" #include "components/gwp_asan/common/pack_stack_trace.h"
#if defined(OS_ANDROID)
#include "components/crash/content/app/crashpad.h" // nogncheck
#endif
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
#include <pthread.h> #include <pthread.h>
#endif #endif
...@@ -166,6 +170,27 @@ void GuardedPageAllocator::Init(size_t max_alloced_pages, ...@@ -166,6 +170,27 @@ void GuardedPageAllocator::Init(size_t max_alloced_pages,
metadata_ = metadata_ =
std::make_unique<AllocatorState::SlotMetadata[]>(state_.num_metadata); std::make_unique<AllocatorState::SlotMetadata[]>(state_.num_metadata);
state_.metadata_addr = reinterpret_cast<uintptr_t>(metadata_.get()); state_.metadata_addr = reinterpret_cast<uintptr_t>(metadata_.get());
#if defined(OS_ANDROID)
// Explicitly whitelist memory ranges the crash_handler needs to reads. This
// is required for WebView because it has a stricter set of privacy
// constraints on what it reads from the crashing process.
for (auto& region : GetInternalMemoryRegions())
crash_reporter::WhitelistMemoryRange(region.first, region.second);
#endif
}
std::vector<std::pair<void*, size_t>>
GuardedPageAllocator::GetInternalMemoryRegions() {
std::vector<std::pair<void*, size_t>> regions;
regions.push_back(std::make_pair(&state_, sizeof(state_)));
regions.push_back(std::make_pair(
metadata_.get(),
sizeof(AllocatorState::SlotMetadata) * state_.num_metadata));
regions.push_back(
std::make_pair(slot_to_metadata_idx_.data(),
sizeof(AllocatorState::MetadataIdx) * state_.total_pages));
return regions;
} }
GuardedPageAllocator::~GuardedPageAllocator() { GuardedPageAllocator::~GuardedPageAllocator() {
......
...@@ -84,6 +84,10 @@ class GWP_ASAN_EXPORT GuardedPageAllocator { ...@@ -84,6 +84,10 @@ class GWP_ASAN_EXPORT GuardedPageAllocator {
// crash handler. // crash handler.
std::string GetCrashKey() const; std::string GetCrashKey() const;
// Returns internal memory used by the allocator (required for sanitization
// on supported platforms.)
std::vector<std::pair<void*, size_t>> GetInternalMemoryRegions();
// Returns true if ptr points to memory managed by this class. // Returns true if ptr points to memory managed by this class.
inline bool PointerIsMine(const void* ptr) const { inline bool PointerIsMine(const void* ptr) const {
return state_.PointerIsMine(reinterpret_cast<uintptr_t>(ptr)); return state_.PointerIsMine(reinterpret_cast<uintptr_t>(ptr));
......
...@@ -34,6 +34,10 @@ ...@@ -34,6 +34,10 @@
#include "third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h" #include "third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h"
#include "third_party/crashpad/crashpad/tools/tool_support.h" #include "third_party/crashpad/crashpad/tools/tool_support.h"
#if defined(OS_LINUX) || defined(OS_ANDROID)
#include "third_party/crashpad/crashpad/snapshot/sanitized/sanitization_information.h"
#endif
namespace gwp_asan { namespace gwp_asan {
namespace internal { namespace internal {
...@@ -118,18 +122,43 @@ MULTIPROCESS_TEST_MAIN(CrashingProcess) { ...@@ -118,18 +122,43 @@ MULTIPROCESS_TEST_MAIN(CrashingProcess) {
std::map<std::string, std::string> annotations; std::map<std::string, std::string> annotations;
std::vector<std::string> arguments; std::vector<std::string> arguments;
crashpad::CrashpadClient* client = new crashpad::CrashpadClient(); #if defined(OS_LINUX) || defined(OS_ANDROID)
static crashpad::SanitizationInformation sanitization_info = {};
static crashpad::SanitizationMemoryRangeWhitelist memory_whitelist;
if (cmd_line->HasSwitch("sanitize")) {
auto memory_ranges = gpa->GetInternalMemoryRegions();
auto* range_array =
new crashpad::SanitizationMemoryRangeWhitelist::Range[memory_ranges
.size()];
for (size_t i = 0; i < memory_ranges.size(); i++) {
range_array[i].base =
reinterpret_cast<crashpad::VMAddress>(memory_ranges[i].first);
range_array[i].length = memory_ranges[i].second;
}
memory_whitelist.size = memory_ranges.size();
memory_whitelist.entries =
reinterpret_cast<crashpad::VMAddress>(range_array);
sanitization_info.memory_range_whitelist_address =
reinterpret_cast<crashpad::VMAddress>(&memory_whitelist);
arguments.push_back(base::StringPrintf("--sanitization-information=%p",
&sanitization_info));
}
#endif
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
arguments.push_back("--test-child-process=CrashpadHandler"); arguments.push_back("--test-child-process=CrashpadHandler");
bool handler = client->StartHandler(/* handler */ cmd_line->GetProgram(), #endif
/* database */ directory,
/* metrics_dir */ metrics_dir, crashpad::CrashpadClient* client = new crashpad::CrashpadClient();
/* url */ "", #if defined(OS_LINUX)
/* annotations */ annotations, bool handler =
/* arguments */ arguments, client->StartHandlerAtCrash(/* handler */ cmd_line->GetProgram(),
/* restartable */ false, /* database */ directory,
/* asynchronous_start */ false); /* metrics_dir */ metrics_dir,
#else /* url */ "",
/* annotations */ annotations,
/* arguments */ arguments);
#elif defined(OS_ANDROID)
// TODO: Once the minSdkVersion is >= Q define a CrashpadHandlerMain() and // TODO: Once the minSdkVersion is >= Q define a CrashpadHandlerMain() and
// use the /system/bin/linker approach instead of using // use the /system/bin/linker approach instead of using
// libchrome_crashpad_handler.so // libchrome_crashpad_handler.so
...@@ -149,7 +178,17 @@ MULTIPROCESS_TEST_MAIN(CrashingProcess) { ...@@ -149,7 +178,17 @@ MULTIPROCESS_TEST_MAIN(CrashingProcess) {
bool handler = client->StartHandlerAtCrash( bool handler = client->StartHandlerAtCrash(
executable_path, directory, metrics_dir, "", annotations, arguments); executable_path, directory, metrics_dir, "", annotations, arguments);
#else
bool handler = client->StartHandler(/* handler */ cmd_line->GetProgram(),
/* database */ directory,
/* metrics_dir */ metrics_dir,
/* url */ "",
/* annotations */ annotations,
/* arguments */ arguments,
/* restartable */ false,
/* asynchronous_start */ false);
#endif #endif
if (!handler) { if (!handler) {
LOG(ERROR) << "Crash handler failed to launch"; LOG(ERROR) << "Crash handler failed to launch";
return kSuccess; return kSuccess;
...@@ -204,10 +243,18 @@ MULTIPROCESS_TEST_MAIN(CrashingProcess) { ...@@ -204,10 +243,18 @@ MULTIPROCESS_TEST_MAIN(CrashingProcess) {
return kSuccess; return kSuccess;
} }
struct TestParams {
TestParams(const char* allocator, bool sanitize)
: allocator(allocator), sanitize(sanitize) {}
const char* allocator;
bool sanitize;
};
class CrashHandlerTest : public base::MultiProcessTest, class CrashHandlerTest : public base::MultiProcessTest,
public testing::WithParamInterface<const char*> { public testing::WithParamInterface<TestParams> {
protected: protected:
CrashHandlerTest() : allocator_(GetParam()) {} CrashHandlerTest() : params_(GetParam()) {}
// Launch a child process and wait for it to crash. Set |gwp_asan_found_| if a // Launch a child process and wait for it to crash. Set |gwp_asan_found_| if a
// GWP-ASan data was found and if so, read it into |proto_|. // GWP-ASan data was found and if so, read it into |proto_|.
...@@ -239,7 +286,10 @@ class CrashHandlerTest : public base::MultiProcessTest, ...@@ -239,7 +286,10 @@ class CrashHandlerTest : public base::MultiProcessTest,
base::GetMultiProcessTestChildBaseCommandLine(); base::GetMultiProcessTestChildBaseCommandLine();
cmd_line.AppendSwitchPath("directory", database_dir); cmd_line.AppendSwitchPath("directory", database_dir);
cmd_line.AppendSwitchASCII("test-name", test_name); cmd_line.AppendSwitchASCII("test-name", test_name);
cmd_line.AppendSwitchASCII("allocator", allocator_); cmd_line.AppendSwitchASCII("allocator", params_.allocator);
if (params_.sanitize)
cmd_line.AppendSwitch("sanitize");
base::LaunchOptions options; base::LaunchOptions options;
#if defined(OS_WIN) #if defined(OS_WIN)
...@@ -339,16 +389,16 @@ class CrashHandlerTest : public base::MultiProcessTest, ...@@ -339,16 +389,16 @@ class CrashHandlerTest : public base::MultiProcessTest,
EXPECT_FALSE(proto_.missing_metadata()); EXPECT_FALSE(proto_.missing_metadata());
EXPECT_TRUE(proto_.has_allocator()); EXPECT_TRUE(proto_.has_allocator());
if (allocator_ == "malloc") if (!strcmp(params_.allocator, "malloc"))
EXPECT_EQ(proto_.allocator(), Crash_Allocator_MALLOC); EXPECT_EQ(proto_.allocator(), Crash_Allocator_MALLOC);
else if (allocator_ == "partitionalloc") else if (!strcmp(params_.allocator, "partitionalloc"))
EXPECT_EQ(proto_.allocator(), Crash_Allocator_PARTITIONALLOC); EXPECT_EQ(proto_.allocator(), Crash_Allocator_PARTITIONALLOC);
else else
ASSERT_TRUE(false) << "Unknown allocator name"; ASSERT_TRUE(false) << "Unknown allocator name";
} }
gwp_asan::Crash proto_; gwp_asan::Crash proto_;
std::string allocator_; TestParams params_;
bool gwp_asan_found_; bool gwp_asan_found_;
}; };
...@@ -407,7 +457,12 @@ TEST_P(CrashHandlerTest, MAYBE_DISABLED(UnrelatedException)) { ...@@ -407,7 +457,12 @@ TEST_P(CrashHandlerTest, MAYBE_DISABLED(UnrelatedException)) {
INSTANTIATE_TEST_SUITE_P(VaryAllocator, INSTANTIATE_TEST_SUITE_P(VaryAllocator,
CrashHandlerTest, CrashHandlerTest,
testing::Values("malloc", "partitionalloc")); testing::Values(
#if defined(OS_LINUX) || defined(OS_ANDROID)
TestParams("malloc", true),
#endif
TestParams("malloc", false),
TestParams("partitionalloc", false)));
} // namespace } // namespace
......
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