mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
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:
parent
9a97daff39
commit
a42b5269b4
@ -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" ]
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
107
snapshot/sanitized/memory_snapshot_sanitized.cc
Normal file
107
snapshot/sanitized/memory_snapshot_sanitized.cc
Normal 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
|
74
snapshot/sanitized/memory_snapshot_sanitized.h
Normal file
74
snapshot/sanitized/memory_snapshot_sanitized.h
Normal 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_
|
132
snapshot/sanitized/module_snapshot_sanitized.cc
Normal file
132
snapshot/sanitized/module_snapshot_sanitized.cc
Normal 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
|
75
snapshot/sanitized/module_snapshot_sanitized.h
Normal file
75
snapshot/sanitized/module_snapshot_sanitized.h
Normal 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_
|
265
snapshot/sanitized/process_snapshot_sanitized.cc
Normal file
265
snapshot/sanitized/process_snapshot_sanitized.cc
Normal 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
|
105
snapshot/sanitized/process_snapshot_sanitized.h
Normal file
105
snapshot/sanitized/process_snapshot_sanitized.h
Normal 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_
|
275
snapshot/sanitized/process_snapshot_sanitized_test.cc
Normal file
275
snapshot/sanitized/process_snapshot_sanitized_test.cc
Normal 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
|
63
snapshot/sanitized/thread_snapshot_sanitized.cc
Normal file
63
snapshot/sanitized/thread_snapshot_sanitized.cc
Normal 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
|
59
snapshot/sanitized/thread_snapshot_sanitized.h
Normal file
59
snapshot/sanitized/thread_snapshot_sanitized.h
Normal 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_
|
@ -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"', {
|
||||
|
@ -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/'],
|
||||
],
|
||||
}],
|
||||
],
|
||||
|
@ -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
55
util/misc/range_set.cc
Normal 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
51
util/misc/range_set.h
Normal 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
116
util/misc/range_set_test.cc
Normal 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
|
@ -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',
|
||||
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user