Add ProcessSnapshotSanitized

A ProcessSnapshotSanitized enables filtering possibly sensitive
information from a snapshot.

WebView has different privacy constraints than Chrome and needs to
avoid collecting data in annotations or from stack memory that may
contain PII. This CL enables:

1. Filtering annotations by name using a whitelist.
2. Filtering for crashes which reference a particular module.
3. Redacting non-essential information from stack memory.

This CL does not provide a client interface to enable sanitization.

Bug: crashpad:30
Change-Id: I8944c70fdcca6d6d4b7955d983320909bf871254
Reviewed-on: https://chromium-review.googlesource.com/1070472
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Scott Graham <scottmg@chromium.org>
This commit is contained in:
Joshua Peraza 2018-06-11 09:38:24 -07:00 committed by Commit Bot
parent 9a97daff39
commit a42b5269b4
20 changed files with 1425 additions and 7 deletions

View File

@ -122,6 +122,14 @@ static_library("snapshot") {
"linux/system_snapshot_linux.h",
"linux/thread_snapshot_linux.cc",
"linux/thread_snapshot_linux.h",
"sanitized/memory_snapshot_sanitized.cc",
"sanitized/memory_snapshot_sanitized.h",
"sanitized/module_snapshot_sanitized.cc",
"sanitized/module_snapshot_sanitized.h",
"sanitized/process_snapshot_sanitized.cc",
"sanitized/process_snapshot_sanitized.h",
"sanitized/thread_snapshot_sanitized.cc",
"sanitized/thread_snapshot_sanitized.h",
]
}
@ -329,6 +337,7 @@ source_set("snapshot_test") {
"linux/exception_snapshot_linux_test.cc",
"linux/process_reader_linux_test.cc",
"linux/system_snapshot_linux_test.cc",
"sanitized/process_snapshot_sanitized_test.cc",
]
} else {
sources += [ "crashpad_info_client_options_test.cc" ]

View File

@ -190,4 +190,18 @@ uint64_t CPUContext::StackPointer() const {
}
}
bool CPUContext::Is64Bit() const {
switch (architecture) {
case kCPUArchitectureX86_64:
case kCPUArchitectureARM64:
return true;
case kCPUArchitectureX86:
case kCPUArchitectureARM:
return false;
default:
NOTREACHED();
return false;
}
}
} // namespace crashpad

View File

@ -323,6 +323,9 @@ struct CPUContext {
//! context structure.
uint64_t StackPointer() const;
//! \brief Returns `true` if this context is for a 64-bit architecture.
bool Is64Bit() const;
//! \brief The CPU architecture of a context structure. This field controls
//! the expression of the union.
CPUArchitecture architecture;

View File

@ -0,0 +1,107 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/sanitized/memory_snapshot_sanitized.h"
#include <string.h>
namespace crashpad {
namespace internal {
namespace {
class MemorySanitizer : public MemorySnapshot::Delegate {
public:
MemorySanitizer(MemorySnapshot::Delegate* delegate,
RangeSet* ranges,
VMAddress address,
bool is_64_bit)
: delegate_(delegate),
ranges_(ranges),
address_(address),
is_64_bit_(is_64_bit) {}
~MemorySanitizer() = default;
bool MemorySnapshotDelegateRead(void* data, size_t size) override {
if (is_64_bit_) {
Sanitize<uint64_t>(data, size);
} else {
Sanitize<uint32_t>(data, size);
}
return delegate_->MemorySnapshotDelegateRead(data, size);
}
private:
template <typename Pointer>
void Sanitize(void* data, size_t size) {
const Pointer defaced =
static_cast<Pointer>(MemorySnapshotSanitized::kDefaced);
// Sanitize up to a word-aligned address.
const size_t aligned_offset =
((address_ + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1)) - address_;
memcpy(data, &defaced, aligned_offset);
// Sanitize words that aren't small and don't look like pointers.
size_t word_count = (size - aligned_offset) / sizeof(Pointer);
auto words =
reinterpret_cast<Pointer*>(static_cast<char*>(data) + aligned_offset);
for (size_t index = 0; index < word_count; ++index) {
if (words[index] > MemorySnapshotSanitized::kSmallWordMax &&
!ranges_->Contains(words[index])) {
words[index] = defaced;
}
}
// Sanitize trailing bytes beyond the word-sized items.
const size_t sanitized_bytes =
aligned_offset + word_count * sizeof(Pointer);
memcpy(static_cast<char*>(data) + sanitized_bytes,
&defaced,
size - sanitized_bytes);
}
MemorySnapshot::Delegate* delegate_;
RangeSet* ranges_;
VMAddress address_;
bool is_64_bit_;
DISALLOW_COPY_AND_ASSIGN(MemorySanitizer);
};
} // namespace
MemorySnapshotSanitized::MemorySnapshotSanitized(const MemorySnapshot* snapshot,
RangeSet* ranges,
bool is_64_bit)
: snapshot_(snapshot), ranges_(ranges), is_64_bit_(is_64_bit) {}
MemorySnapshotSanitized::~MemorySnapshotSanitized() = default;
uint64_t MemorySnapshotSanitized::Address() const {
return snapshot_->Address();
}
size_t MemorySnapshotSanitized::Size() const {
return snapshot_->Size();
}
bool MemorySnapshotSanitized::Read(Delegate* delegate) const {
MemorySanitizer sanitizer(delegate, ranges_, Address(), is_64_bit_);
return snapshot_->Read(&sanitizer);
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,74 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_
#define CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_
#include <stdint.h>
#include "snapshot/memory_snapshot.h"
#include "util/misc/range_set.h"
namespace crashpad {
namespace internal {
//! \brief A MemorySnapshot which wraps and filters sensitive information from
//! another MemorySnapshot.
//!
//! This class redacts all data from the wrapped MemorySnapshot unless:
//! 1. The data is pointer aligned and points into a whitelisted address range.
//! 2. The data is pointer aligned and is a small integer.
class MemorySnapshotSanitized final : public MemorySnapshot {
public:
//! \brief Redacted data is replaced with this value, casted to the
//! appropriate size for a pointer in the target process.
static constexpr uint64_t kDefaced = 0x0defaced0defaced;
//! \brief Pointer-aligned data smaller than this value is not redacted.
static constexpr uint64_t kSmallWordMax = 4096;
//! \brief Constructs this object.
//!
//! \param[in] snapshot The MemorySnapshot to sanitize.
//! \param[in] ranges A set of whitelisted address ranges.
//! \param[in] is_64_bit `true` if this memory is for a 64-bit process.
MemorySnapshotSanitized(const MemorySnapshot* snapshot,
RangeSet* ranges,
bool is_64_bit);
~MemorySnapshotSanitized() override;
// MemorySnapshot:
uint64_t Address() const override;
size_t Size() const override;
bool Read(Delegate* delegate) const override;
const MemorySnapshot* MergeWithOtherSnapshot(
const MemorySnapshot* other) const override {
return nullptr;
}
private:
const MemorySnapshot* snapshot_;
RangeSet* ranges_;
bool is_64_bit_;
DISALLOW_COPY_AND_ASSIGN(MemorySnapshotSanitized);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_

View File

@ -0,0 +1,132 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/sanitized/module_snapshot_sanitized.h"
namespace crashpad {
namespace internal {
namespace {
bool KeyIsInWhitelist(const std::string& name,
const std::vector<std::string>& whitelist) {
for (const auto& key : whitelist) {
if (name == key) {
return true;
}
}
return false;
}
} // namespace
ModuleSnapshotSanitized::ModuleSnapshotSanitized(
const ModuleSnapshot* snapshot,
const std::vector<std::string>* annotations_whitelist)
: snapshot_(snapshot), annotations_whitelist_(annotations_whitelist) {}
ModuleSnapshotSanitized::~ModuleSnapshotSanitized() = default;
std::string ModuleSnapshotSanitized::Name() const {
return snapshot_->Name();
}
uint64_t ModuleSnapshotSanitized::Address() const {
return snapshot_->Address();
}
uint64_t ModuleSnapshotSanitized::Size() const {
return snapshot_->Size();
}
time_t ModuleSnapshotSanitized::Timestamp() const {
return snapshot_->Timestamp();
}
void ModuleSnapshotSanitized::FileVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const {
snapshot_->FileVersion(version_0, version_1, version_2, version_3);
}
void ModuleSnapshotSanitized::SourceVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const {
snapshot_->SourceVersion(version_0, version_1, version_2, version_3);
}
ModuleSnapshot::ModuleType ModuleSnapshotSanitized::GetModuleType() const {
return snapshot_->GetModuleType();
}
void ModuleSnapshotSanitized::UUIDAndAge(crashpad::UUID* uuid,
uint32_t* age) const {
snapshot_->UUIDAndAge(uuid, age);
}
std::string ModuleSnapshotSanitized::DebugFileName() const {
return snapshot_->DebugFileName();
}
std::vector<std::string> ModuleSnapshotSanitized::AnnotationsVector() const {
// TODO(jperaza): If/when AnnotationsVector() begins to be used, determine
// whether and how the content should be sanitized.
DCHECK(snapshot_->AnnotationsVector().empty());
return std::vector<std::string>();
}
std::map<std::string, std::string>
ModuleSnapshotSanitized::AnnotationsSimpleMap() const {
std::map<std::string, std::string> annotations =
snapshot_->AnnotationsSimpleMap();
if (annotations_whitelist_) {
for (auto kv = annotations.begin(); kv != annotations.end(); ++kv) {
if (!KeyIsInWhitelist(kv->first, *annotations_whitelist_)) {
annotations.erase(kv);
}
}
}
return annotations;
}
std::vector<AnnotationSnapshot> ModuleSnapshotSanitized::AnnotationObjects()
const {
std::vector<AnnotationSnapshot> annotations = snapshot_->AnnotationObjects();
if (annotations_whitelist_) {
std::vector<AnnotationSnapshot> whitelisted;
for (const auto& anno : annotations) {
if (KeyIsInWhitelist(anno.name, *annotations_whitelist_)) {
whitelisted.push_back(anno);
}
}
annotations.swap(whitelisted);
}
return annotations;
}
std::set<CheckedRange<uint64_t>> ModuleSnapshotSanitized::ExtraMemoryRanges()
const {
DCHECK(snapshot_->ExtraMemoryRanges().empty());
return std::set<CheckedRange<uint64_t>>();
}
std::vector<const UserMinidumpStream*>
ModuleSnapshotSanitized::CustomMinidumpStreams() const {
return snapshot_->CustomMinidumpStreams();
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,75 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_
#define CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_
#include <string>
#include <vector>
#include "base/macros.h"
#include "snapshot/module_snapshot.h"
namespace crashpad {
namespace internal {
//! \brief A ModuleSnapshot which wraps and filters sensitive information from
//! another ModuleSnapshot.
class ModuleSnapshotSanitized final : public ModuleSnapshot {
public:
//! \brief Constructs this object.
//!
//! \param[in] snapshot The ModuleSnapshot to sanitize.
//! \param[in] annotations_whitelist A list of annotation names to allow to be
//! returned by AnnotationsSimpleMap() or AnnotationObjects(). If
//! `nullptr`, all annotations will be returned.
ModuleSnapshotSanitized(
const ModuleSnapshot* snapshot,
const std::vector<std::string>* annotations_whitelist);
~ModuleSnapshotSanitized() override;
// ModuleSnapshot:
std::string Name() const override;
uint64_t Address() const override;
uint64_t Size() const override;
time_t Timestamp() const override;
void FileVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const override;
void SourceVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const override;
ModuleType GetModuleType() const override;
void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
std::string DebugFileName() const override;
std::vector<std::string> AnnotationsVector() const override;
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
std::vector<AnnotationSnapshot> AnnotationObjects() const override;
std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;
std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
private:
const ModuleSnapshot* snapshot_;
const std::vector<std::string>* annotations_whitelist_;
DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotSanitized);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_

View File

@ -0,0 +1,265 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/sanitized/process_snapshot_sanitized.h"
#include <stdint.h>
#include "snapshot/cpu_context.h"
#include "util/numeric/safe_assignment.h"
namespace crashpad {
namespace {
class StackReferencesAddressRange : public MemorySnapshot::Delegate {
public:
// Returns true if stack contains a pointer aligned word in the range [low,
// high). The search starts at the first pointer aligned address greater than
// stack_pointer.
bool CheckStack(const MemorySnapshot* stack,
VMAddress stack_pointer,
VMAddress low,
VMAddress high,
bool is_64_bit) {
stack_ = stack;
stack_pointer_ = stack_pointer;
low_ = low;
high_ = high;
is_64_bit_ = is_64_bit;
return stack_->Read(this);
}
// MemorySnapshot::Delegate
bool MemorySnapshotDelegateRead(void* data, size_t size) {
return is_64_bit_ ? ScanStackForPointers<uint64_t>(data, size)
: ScanStackForPointers<uint32_t>(data, size);
}
private:
template <typename Pointer>
bool ScanStackForPointers(void* data, size_t size) {
size_t sp_offset;
if (!AssignIfInRange(&sp_offset, stack_pointer_ - stack_->Address())) {
return false;
}
const size_t aligned_sp_offset =
(sp_offset + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1);
auto words = reinterpret_cast<Pointer*>(static_cast<char*>(data) +
aligned_sp_offset);
size_t word_count = (size - aligned_sp_offset) / sizeof(Pointer);
for (size_t index = 0; index < word_count; ++index) {
if (words[index] >= low_ && words[index] < high_) {
return true;
}
}
return false;
}
VMAddress stack_pointer_;
VMAddress low_;
VMAddress high_;
const MemorySnapshot* stack_;
bool is_64_bit_;
};
} // namespace
ProcessSnapshotSanitized::ProcessSnapshotSanitized() = default;
ProcessSnapshotSanitized::~ProcessSnapshotSanitized() = default;
bool ProcessSnapshotSanitized::Initialize(
const ProcessSnapshot* snapshot,
const std::vector<std::string>* annotations_whitelist,
VMAddress target_module_address,
bool sanitize_stacks) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
snapshot_ = snapshot;
annotations_whitelist_ = annotations_whitelist;
sanitize_stacks_ = sanitize_stacks;
if (target_module_address) {
const ExceptionSnapshot* exception = snapshot_->Exception();
if (!exception) {
return false;
}
const ThreadSnapshot* exc_thread = nullptr;
for (const auto thread : snapshot_->Threads()) {
if (thread->ThreadID() == exception->ThreadID()) {
exc_thread = thread;
break;
}
}
if (!exc_thread) {
return false;
}
const ModuleSnapshot* target_module = nullptr;
for (const auto module : snapshot_->Modules()) {
if (target_module_address >= module->Address() &&
target_module_address < module->Address() + module->Size()) {
target_module = module;
break;
}
}
if (!target_module) {
return false;
}
// Only allow the snapshot if the program counter or some address on the
// stack points into the target module.
VMAddress pc = exception->Context()->InstructionPointer();
VMAddress module_address_low = target_module->Address();
VMAddress module_address_high = module_address_low + target_module->Size();
if ((pc < module_address_low || pc >= module_address_high) &&
!StackReferencesAddressRange().CheckStack(
exc_thread->Stack(),
exception->Context()->StackPointer(),
module_address_low,
module_address_high,
exception->Context()->Is64Bit())) {
return false;
}
}
if (annotations_whitelist_) {
for (const auto module : snapshot_->Modules()) {
modules_.emplace_back(std::make_unique<internal::ModuleSnapshotSanitized>(
module, annotations_whitelist_));
}
}
if (sanitize_stacks_) {
for (const auto module : snapshot_->Modules()) {
address_ranges_.Insert(module->Address(), module->Size());
}
for (const auto thread : snapshot_->Threads()) {
address_ranges_.Insert(thread->Stack()->Address(),
thread->Stack()->Size());
threads_.emplace_back(std::make_unique<internal::ThreadSnapshotSanitized>(
thread, &address_ranges_));
}
}
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
pid_t ProcessSnapshotSanitized::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->ProcessID();
}
pid_t ProcessSnapshotSanitized::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->ParentProcessID();
}
void ProcessSnapshotSanitized::SnapshotTime(timeval* snapshot_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
snapshot_->SnapshotTime(snapshot_time);
}
void ProcessSnapshotSanitized::ProcessStartTime(timeval* start_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
snapshot_->ProcessStartTime(start_time);
}
void ProcessSnapshotSanitized::ProcessCPUTimes(timeval* user_time,
timeval* system_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
snapshot_->ProcessCPUTimes(user_time, system_time);
}
void ProcessSnapshotSanitized::ReportID(UUID* report_id) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
snapshot_->ReportID(report_id);
}
void ProcessSnapshotSanitized::ClientID(UUID* client_id) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
snapshot_->ClientID(client_id);
}
const std::map<std::string, std::string>&
ProcessSnapshotSanitized::AnnotationsSimpleMap() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->AnnotationsSimpleMap();
}
const SystemSnapshot* ProcessSnapshotSanitized::System() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->System();
}
std::vector<const ThreadSnapshot*> ProcessSnapshotSanitized::Threads() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!sanitize_stacks_) {
return snapshot_->Threads();
}
std::vector<const ThreadSnapshot*> threads;
for (const auto& thread : threads_) {
threads.push_back(thread.get());
}
return threads;
}
std::vector<const ModuleSnapshot*> ProcessSnapshotSanitized::Modules() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!annotations_whitelist_) {
return snapshot_->Modules();
}
std::vector<const ModuleSnapshot*> modules;
for (const auto& module : modules_) {
modules.push_back(module.get());
}
return modules;
}
std::vector<UnloadedModuleSnapshot> ProcessSnapshotSanitized::UnloadedModules()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->UnloadedModules();
}
const ExceptionSnapshot* ProcessSnapshotSanitized::Exception() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->Exception();
}
std::vector<const MemoryMapRegionSnapshot*>
ProcessSnapshotSanitized::MemoryMap() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->MemoryMap();
}
std::vector<HandleSnapshot> ProcessSnapshotSanitized::Handles() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->Handles();
}
std::vector<const MemorySnapshot*> ProcessSnapshotSanitized::ExtraMemory()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->ExtraMemory();
}
} // namespace crashpad

View File

@ -0,0 +1,105 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_
#define CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
#include "snapshot/exception_snapshot.h"
#include "snapshot/process_snapshot.h"
#include "snapshot/sanitized/module_snapshot_sanitized.h"
#include "snapshot/sanitized/thread_snapshot_sanitized.h"
#include "snapshot/thread_snapshot.h"
#include "snapshot/unloaded_module_snapshot.h"
#include "util/misc/address_types.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/misc/range_set.h"
namespace crashpad {
//! \brief A ProcessSnapshot which wraps and filters sensitive information from
//! another ProcessSnapshot.
class ProcessSnapshotSanitized final : public ProcessSnapshot {
public:
ProcessSnapshotSanitized();
~ProcessSnapshotSanitized() override;
//! \brief Initializes this object.
//!
//! This method must be successfully called before calling any other method on
//! this object.
//!
//! \param[in] snapshot The ProcessSnapshot to sanitize.
//! \param[in] annotations_whitelist A list of annotations names to allow to
//! be returned by AnnotationsSimpleMap() or from this object's module
//! snapshots. If `nullptr`, all annotations will be returned.
//! \param[in] target_module_address An address in the target process'
//! address space within the bounds of a module to target. If the
//! crashing thread's context and stack do not contain any pointers into
//! this module's address range, this method will return `false`. If this
//! value is 0, this method will not check the context or stack for
//! references to any particular module.
//! \param[in] sanitize_stacks If `true`, the MemorySnapshots for each
//! thread's stack will be filtered using an
//! internal::StackSnapshotSanitized.
//! \return `false` if \a snapshot does not meet sanitization requirements and
//! should be filtered entirely. Otherwise `true`.
bool Initialize(const ProcessSnapshot* snapshot,
const std::vector<std::string>* annotations_whitelist,
VMAddress target_module_address,
bool sanitize_stacks);
// ProcessSnapshot:
pid_t ProcessID() const override;
pid_t ParentProcessID() const override;
void SnapshotTime(timeval* snapshot_time) const override;
void ProcessStartTime(timeval* start_time) const override;
void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
void ReportID(UUID* report_id) const override;
void ClientID(UUID* client_id) const override;
const std::map<std::string, std::string>& AnnotationsSimpleMap()
const override;
const SystemSnapshot* System() const override;
std::vector<const ThreadSnapshot*> Threads() const override;
std::vector<const ModuleSnapshot*> Modules() const override;
std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
const ExceptionSnapshot* Exception() const override;
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
private:
// Only used when annotations_whitelist_ != nullptr.
std::vector<std::unique_ptr<internal::ModuleSnapshotSanitized>> modules_;
// Only used when sanitize_stacks_ == true.
std::vector<std::unique_ptr<internal::ThreadSnapshotSanitized>> threads_;
RangeSet address_ranges_;
const ProcessSnapshot* snapshot_;
const std::vector<std::string>* annotations_whitelist_;
bool sanitize_stacks_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotSanitized);
};
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_

View File

@ -0,0 +1,275 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/sanitized/process_snapshot_sanitized.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/multiprocess_exec.h"
#include "util/file/file_io.h"
#include "util/numeric/safe_assignment.h"
#if defined(OS_LINUX) || defined(OS_ANDROID)
#include <sys/syscall.h>
#include "snapshot/linux/process_snapshot_linux.h"
#include "util/linux/direct_ptrace_connection.h"
#include "util/linux/exception_information.h"
#include "util/posix/signals.h"
#endif
namespace crashpad {
namespace test {
namespace {
class ExceptionGenerator {
public:
static ExceptionGenerator* Get() {
static ExceptionGenerator* instance = new ExceptionGenerator();
return instance;
}
bool Initialize(FileHandle in, FileHandle out) {
in_ = in;
out_ = out;
return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr);
}
private:
ExceptionGenerator() = default;
~ExceptionGenerator() = delete;
static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
auto state = Get();
ExceptionInformation info = {};
info.siginfo_address = FromPointerCast<VMAddress>(siginfo);
info.context_address = FromPointerCast<VMAddress>(context);
info.thread_id = syscall(SYS_gettid);
auto info_addr = FromPointerCast<VMAddress>(&info);
ASSERT_TRUE(LoggingWriteFile(state->out_, &info_addr, sizeof(info_addr)));
CheckedReadFileAtEOF(state->in_);
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
}
FileHandle in_;
FileHandle out_;
DISALLOW_COPY_AND_ASSIGN(ExceptionGenerator);
};
constexpr char kWhitelistedAnnotationName[] = "name_of_whitelisted_anno";
constexpr char kWhitelistedAnnotationValue[] = "some_value";
constexpr char kNonWhitelistedAnnotationName[] = "non_whitelisted_anno";
constexpr char kNonWhitelistedAnnotationValue[] = "private_annotation";
constexpr char kSensitiveStackData[] = "sensitive_stack_data";
struct ChildTestAddresses {
VMAddress string_address;
VMAddress module_address;
VMAddress non_module_address;
VMAddress code_pointer_address;
VMAddress code_pointer_value;
};
void ChildTestFunction() {
FileHandle in = StdioFileHandle(StdioStream::kStandardInput);
FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
static StringAnnotation<32> whitelisted_annotation(
kWhitelistedAnnotationName);
whitelisted_annotation.Set(kWhitelistedAnnotationValue);
static StringAnnotation<32> non_whitelisted_annotation(
kNonWhitelistedAnnotationName);
non_whitelisted_annotation.Set(kNonWhitelistedAnnotationValue);
char string_data[strlen(kSensitiveStackData) + 1];
strcpy(string_data, kSensitiveStackData);
void (*code_pointer)(void) = ChildTestFunction;
ChildTestAddresses addrs = {};
addrs.string_address = FromPointerCast<VMAddress>(string_data);
addrs.module_address = FromPointerCast<VMAddress>(ChildTestFunction);
addrs.non_module_address = FromPointerCast<VMAddress>(&addrs);
addrs.code_pointer_address = FromPointerCast<VMAddress>(&code_pointer);
addrs.code_pointer_value = FromPointerCast<VMAddress>(code_pointer);
ASSERT_TRUE(LoggingWriteFile(out, &addrs, sizeof(addrs)));
auto gen = ExceptionGenerator::Get();
ASSERT_TRUE(gen->Initialize(in, out));
__builtin_trap();
}
CRASHPAD_CHILD_TEST_MAIN(ChildToBeSanitized) {
ChildTestFunction();
NOTREACHED();
return EXIT_SUCCESS;
}
void ExpectAnnotations(ProcessSnapshot* snapshot, bool sanitized) {
bool found_whitelisted = false;
bool found_non_whitelisted = false;
for (auto module : snapshot->Modules()) {
for (const auto& anno : module->AnnotationObjects()) {
if (anno.name == kWhitelistedAnnotationName) {
found_whitelisted = true;
} else if (anno.name == kNonWhitelistedAnnotationName) {
found_non_whitelisted = true;
}
}
}
EXPECT_TRUE(found_whitelisted);
if (sanitized) {
EXPECT_FALSE(found_non_whitelisted);
} else {
EXPECT_TRUE(found_non_whitelisted);
}
}
class StackSanitizationChecker : public MemorySnapshot::Delegate {
public:
StackSanitizationChecker() = default;
~StackSanitizationChecker() = default;
void CheckStack(const MemorySnapshot* stack,
const ChildTestAddresses& addrs,
bool is_64_bit,
bool sanitized) {
stack_ = stack;
addrs_ = addrs;
is_64_bit_ = is_64_bit;
sanitized_ = sanitized;
EXPECT_TRUE(stack_->Read(this));
}
// MemorySnapshot::Delegate
bool MemorySnapshotDelegateRead(void* data, size_t size) override {
size_t pointer_offset;
if (!AssignIfInRange(&pointer_offset,
addrs_.code_pointer_address - stack_->Address())) {
ADD_FAILURE();
return false;
}
const auto data_c = static_cast<char*>(data);
VMAddress pointer_value;
if (is_64_bit_) {
pointer_value = *reinterpret_cast<uint64_t*>(data_c + pointer_offset);
} else {
pointer_value = *reinterpret_cast<uint32_t*>(data_c + pointer_offset);
}
EXPECT_EQ(pointer_value, addrs_.code_pointer_value);
size_t string_offset;
if (!AssignIfInRange(&string_offset,
addrs_.string_address - stack_->Address())) {
ADD_FAILURE();
return false;
}
auto string = data_c + string_offset;
if (sanitized_) {
EXPECT_STRNE(string, kSensitiveStackData);
} else {
EXPECT_STREQ(string, kSensitiveStackData);
}
return true;
}
private:
const MemorySnapshot* stack_;
ChildTestAddresses addrs_;
bool is_64_bit_;
bool sanitized_;
};
void ExpectStackData(ProcessSnapshot* snapshot,
const ChildTestAddresses& addrs,
bool sanitized) {
const ThreadSnapshot* crasher = nullptr;
for (const auto thread : snapshot->Threads()) {
if (thread->ThreadID() == snapshot->Exception()->ThreadID()) {
crasher = thread;
break;
}
}
ASSERT_TRUE(crasher);
const MemorySnapshot* stack = crasher->Stack();
StackSanitizationChecker().CheckStack(
stack, addrs, crasher->Context()->Is64Bit(), sanitized);
}
class SanitizeTest : public MultiprocessExec {
public:
SanitizeTest() : MultiprocessExec() {
SetChildTestMainFunction("ChildToBeSanitized");
SetExpectedChildTerminationBuiltinTrap();
}
~SanitizeTest() = default;
private:
void MultiprocessParent() {
ChildTestAddresses addrs = {};
ASSERT_TRUE(
LoggingReadFileExactly(ReadPipeHandle(), &addrs, sizeof(addrs)));
VMAddress exception_info_addr;
ASSERT_TRUE(LoggingReadFileExactly(
ReadPipeHandle(), &exception_info_addr, sizeof(exception_info_addr)));
DirectPtraceConnection connection;
ASSERT_TRUE(connection.Initialize(ChildProcess()));
ProcessSnapshotLinux snapshot;
ASSERT_TRUE(snapshot.Initialize(&connection));
ASSERT_TRUE(snapshot.InitializeException(exception_info_addr));
ExpectAnnotations(&snapshot, /* sanitized= */ false);
ExpectStackData(&snapshot, addrs, /* sanitized= */ false);
std::vector<std::string> whitelist;
whitelist.push_back(kWhitelistedAnnotationName);
ProcessSnapshotSanitized sanitized;
ASSERT_TRUE(sanitized.Initialize(
&snapshot, &whitelist, addrs.module_address, true));
ExpectAnnotations(&sanitized, /* sanitized= */ true);
ExpectStackData(&sanitized, addrs, /* sanitized= */ true);
ProcessSnapshotSanitized screened_snapshot;
EXPECT_FALSE(screened_snapshot.Initialize(
&snapshot, nullptr, addrs.non_module_address, false));
}
DISALLOW_COPY_AND_ASSIGN(SanitizeTest);
};
TEST(ProcessSnapshotSanitized, Sanitize) {
SanitizeTest test;
test.Run();
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -0,0 +1,63 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/sanitized/thread_snapshot_sanitized.h"
#include "snapshot/cpu_context.h"
namespace crashpad {
namespace internal {
ThreadSnapshotSanitized::ThreadSnapshotSanitized(const ThreadSnapshot* snapshot,
RangeSet* ranges)
: ThreadSnapshot(),
snapshot_(snapshot),
stack_(snapshot_->Stack(), ranges, snapshot_->Context()->Is64Bit()) {}
ThreadSnapshotSanitized::~ThreadSnapshotSanitized() = default;
const CPUContext* ThreadSnapshotSanitized::Context() const {
return snapshot_->Context();
}
const MemorySnapshot* ThreadSnapshotSanitized::Stack() const {
return &stack_;
}
uint64_t ThreadSnapshotSanitized::ThreadID() const {
return snapshot_->ThreadID();
}
int ThreadSnapshotSanitized::SuspendCount() const {
return snapshot_->SuspendCount();
}
int ThreadSnapshotSanitized::Priority() const {
return snapshot_->Priority();
}
uint64_t ThreadSnapshotSanitized::ThreadSpecificDataAddress() const {
return snapshot_->ThreadSpecificDataAddress();
}
std::vector<const MemorySnapshot*> ThreadSnapshotSanitized::ExtraMemory()
const {
// TODO(jperaza): If/when ExtraMemory() is used, decide whether and how it
// should be sanitized.
DCHECK(snapshot_->ExtraMemory().empty());
return std::vector<const MemorySnapshot*>();
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,59 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_
#define CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_
#include "snapshot/thread_snapshot.h"
#include "snapshot/sanitized/memory_snapshot_sanitized.h"
#include "util/misc/range_set.h"
namespace crashpad {
namespace internal {
//! \brief A ThreadSnapshot which wraps and filters sensitive information from
//! another ThreadSnapshot.
class ThreadSnapshotSanitized final : public ThreadSnapshot {
public:
//! \brief Constructs this object.
//!
//! \param[in] snapshot The ThreadSnapshot to sanitize.
//! \param[in] ranges A set of address ranges with which to sanitize this
//! thread's stacks. \see internal::MemorySnapshotSanitized.
ThreadSnapshotSanitized(const ThreadSnapshot* snapshot, RangeSet* ranges);
~ThreadSnapshotSanitized() override;
// ThreadSnapshot:
const CPUContext* Context() const override;
const MemorySnapshot* Stack() const override;
uint64_t ThreadID() const override;
int SuspendCount() const override;
int Priority() const override;
uint64_t ThreadSpecificDataAddress() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
private:
const ThreadSnapshot* snapshot_;
MemorySnapshotSanitized stack_;
DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotSanitized);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_

View File

@ -123,6 +123,14 @@
'posix/timezone.cc',
'posix/timezone.h',
'process_snapshot.h',
'sanitized/memory_snapshot_sanitized.cc',
'sanitized/memory_snapshot_sanitized.h',
'sanitized/module_snapshot_sanitized.cc',
'sanitized/module_snapshot_sanitized.h',
'sanitized/process_snapshot_sanitized.cc',
'sanitized/process_snapshot_sanitized.h',
'sanitized/thread_snapshot_sanitized.cc',
'sanitized/thread_snapshot_sanitized.h',
'snapshot_constants.h',
'system_snapshot.h',
'thread_snapshot.h',
@ -176,6 +184,7 @@
'sources/': [
['exclude', '^elf/'],
['exclude', '^crashpad_types/'],
['exclude', '^sanitized/'],
],
}],
['target_arch!="ia32" and target_arch!="x64"', {

View File

@ -92,6 +92,7 @@
'mac/system_snapshot_mac_test.cc',
'minidump/process_snapshot_minidump_test.cc',
'posix/timezone_test.cc',
'sanitized/process_snapshot_sanitized_test.cc',
'win/cpu_context_win_test.cc',
'win/exception_snapshot_win_test.cc',
'win/extra_memory_ranges_test.cc',
@ -145,6 +146,7 @@
'sources/': [
['exclude', '^elf/'],
['exclude', '^crashpad_types/'],
['exclude', '^sanitized/'],
],
}],
],

View File

@ -104,6 +104,8 @@ static_library("util") {
"misc/pdb_structures.h",
"misc/random_string.cc",
"misc/random_string.h",
"misc/range_set.cc",
"misc/range_set.h",
"misc/reinterpret_bytes.cc",
"misc/reinterpret_bytes.h",
"misc/scoped_forbid_return.cc",
@ -256,9 +258,7 @@ static_library("util") {
defines = [ "CRASHPAD_USE_BORINGSSL" ]
if (crashpad_is_in_fuchsia) {
deps += [
"//third_party/boringssl"
]
deps += [ "//third_party/boringssl" ]
} else {
libs = [
"crypto",
@ -490,13 +490,13 @@ if (!crashpad_is_android) {
}
if (use_boringssl_for_http_transport_socket) {
data_deps = [ ":generate_test_server_key" ]
data_deps = [
":generate_test_server_key",
]
defines = [ "CRASHPAD_USE_BORINGSSL" ]
if (crashpad_is_in_fuchsia) {
deps += [
"//third_party/boringssl"
]
deps += [ "//third_party/boringssl" ]
} else {
libs = [
"crypto",
@ -526,6 +526,7 @@ source_set("util_test") {
"misc/initialization_state_test.cc",
"misc/paths_test.cc",
"misc/random_string_test.cc",
"misc/range_set_test.cc",
"misc/reinterpret_bytes_test.cc",
"misc/scoped_forbid_return_test.cc",
"misc/time_test.cc",

55
util/misc/range_set.cc Normal file
View File

@ -0,0 +1,55 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/misc/range_set.h"
#include <algorithm>
namespace crashpad {
RangeSet::RangeSet() = default;
RangeSet::~RangeSet() = default;
void RangeSet::Insert(VMAddress base, VMSize size) {
if (!size) {
return;
}
VMAddress last = base + size - 1;
auto overlapping_range = ranges_.lower_bound(base);
#define OVERLAPPING_RANGES_BASE overlapping_range->second
#define OVERLAPPING_RANGES_LAST overlapping_range->first
while (overlapping_range != ranges_.end() &&
OVERLAPPING_RANGES_BASE <= last) {
base = std::min(base, OVERLAPPING_RANGES_BASE);
last = std::max(last, OVERLAPPING_RANGES_LAST);
auto tmp = overlapping_range;
++overlapping_range;
ranges_.erase(tmp);
}
#undef OVERLAPPING_RANGES_BASE
#undef OVERLAPPING_RANGES_LAST
ranges_[last] = base;
}
bool RangeSet::Contains(VMAddress address) const {
auto range_above_address = ranges_.lower_bound(address);
return range_above_address != ranges_.end() &&
range_above_address->second <= address;
}
} // namespace crashpad

51
util/misc/range_set.h Normal file
View File

@ -0,0 +1,51 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_UTIL_MISC_RANGE_SET_H_
#define CRASHPAD_UTIL_MISC_RANGE_SET_H_
#include <map>
#include "base/macros.h"
#include "util/misc/address_types.h"
namespace crashpad {
//! \brief A set of VMAddress ranges.
class RangeSet {
public:
RangeSet();
~RangeSet();
//! \brief Inserts a range into the set.
//!
//! \param[in] base The low address of the range.
//! \param[in] size The size of the range.
void Insert(VMAddress base, VMSize size);
//! \brief Returns `true` if \a address falls within a range in this set.
bool Contains(VMAddress address) const;
private:
// Keys are the highest address in the range. Values are the base address of
// the range. Overlapping ranges are merged on insertion. Adjacent ranges may
// be merged.
std::map<VMAddress, VMAddress> ranges_;
DISALLOW_COPY_AND_ASSIGN(RangeSet);
};
} // namespace crashpad
#endif // CRASHPAD_UTIL_MISC_RANGE_SET_H_

116
util/misc/range_set_test.cc Normal file
View File

@ -0,0 +1,116 @@
// Copyright 2018 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/misc/range_set.h"
#include <sys/types.h>
#include <memory>
#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "util/misc/address_types.h"
#include "util/misc/from_pointer_cast.h"
namespace crashpad {
namespace test {
namespace {
void ExpectRangeIsContained(const RangeSet& ranges,
VMAddress base,
VMSize size) {
for (VMAddress addr = base; addr < base + size; ++addr) {
SCOPED_TRACE(base::StringPrintf("0x%" PRIx64 " in range 0x%" PRIx64
":0x%" PRIx64,
addr,
base,
base + size));
EXPECT_TRUE(ranges.Contains(addr));
}
}
TEST(RangeSet, Basic) {
RangeSet ranges;
auto base = FromPointerCast<VMAddress>(&ranges);
VMSize size = sizeof(ranges);
ranges.Insert(base, size);
ExpectRangeIsContained(ranges, base, size);
EXPECT_FALSE(ranges.Contains(base - 1));
EXPECT_FALSE(ranges.Contains(base + size));
}
TEST(RangeSet, ZeroSizedRange) {
RangeSet ranges;
auto addr = FromPointerCast<VMAddress>(&ranges);
ranges.Insert(addr, 0);
EXPECT_FALSE(ranges.Contains(addr));
}
TEST(RangeSet, DuplicateRanges) {
RangeSet ranges;
auto base = FromPointerCast<VMAddress>(&ranges);
VMSize size = sizeof(ranges);
ranges.Insert(base, size);
ranges.Insert(base, size);
ExpectRangeIsContained(ranges, base, size);
}
TEST(RangeSet, OverlappingRanges) {
RangeSet ranges;
ranges.Insert(37, 16);
ranges.Insert(9, 9);
ranges.Insert(17, 42);
EXPECT_TRUE(ranges.Contains(9));
EXPECT_TRUE(ranges.Contains(17));
EXPECT_TRUE(ranges.Contains(36));
EXPECT_TRUE(ranges.Contains(37));
EXPECT_TRUE(ranges.Contains(52));
EXPECT_TRUE(ranges.Contains(58));
}
TEST(RangeSet, SubRangeInLargeRange) {
constexpr size_t kBufferSize = 2 << 22;
auto buf = std::make_unique<char[]>(kBufferSize);
RangeSet ranges;
auto addr = FromPointerCast<VMAddress>(buf.get());
ranges.Insert(addr, kBufferSize);
EXPECT_TRUE(ranges.Contains(addr));
EXPECT_TRUE(ranges.Contains(addr + kBufferSize - 1));
ranges.Insert(addr, kBufferSize / 2);
EXPECT_TRUE(ranges.Contains(addr));
EXPECT_TRUE(ranges.Contains(addr + kBufferSize / 2 - 1));
EXPECT_TRUE(ranges.Contains(addr + kBufferSize - 1));
}
TEST(RangeSet, LargeOverlappingRanges) {
constexpr size_t kBufferSize = 2 << 23;
auto buf = std::make_unique<char[]>(kBufferSize);
RangeSet ranges;
auto addr = FromPointerCast<VMAddress>(buf.get());
ranges.Insert(addr, 3 * kBufferSize / 4);
ranges.Insert(addr + kBufferSize / 4, 3 * kBufferSize / 4);
EXPECT_TRUE(ranges.Contains(addr));
EXPECT_TRUE(ranges.Contains(addr + kBufferSize - 1));
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -153,6 +153,8 @@
'misc/pdb_structures.h',
'misc/random_string.cc',
'misc/random_string.h',
'misc/range_set.cc',
'misc/range_set.h',
'misc/reinterpret_bytes.cc',
'misc/reinterpret_bytes.h',
'misc/scoped_forbid_return.cc',

View File

@ -79,6 +79,7 @@
'misc/paths_test.cc',
'misc/scoped_forbid_return_test.cc',
'misc/random_string_test.cc',
'misc/range_set_test.cc',
'misc/reinterpret_bytes_test.cc',
'misc/time_test.cc',
'misc/uuid_test.cc',