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/system_snapshot_linux.h",
|
||||||
"linux/thread_snapshot_linux.cc",
|
"linux/thread_snapshot_linux.cc",
|
||||||
"linux/thread_snapshot_linux.h",
|
"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/exception_snapshot_linux_test.cc",
|
||||||
"linux/process_reader_linux_test.cc",
|
"linux/process_reader_linux_test.cc",
|
||||||
"linux/system_snapshot_linux_test.cc",
|
"linux/system_snapshot_linux_test.cc",
|
||||||
|
"sanitized/process_snapshot_sanitized_test.cc",
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
sources += [ "crashpad_info_client_options_test.cc" ]
|
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
|
} // namespace crashpad
|
||||||
|
@ -323,6 +323,9 @@ struct CPUContext {
|
|||||||
//! context structure.
|
//! context structure.
|
||||||
uint64_t StackPointer() const;
|
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
|
//! \brief The CPU architecture of a context structure. This field controls
|
||||||
//! the expression of the union.
|
//! the expression of the union.
|
||||||
CPUArchitecture architecture;
|
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.cc',
|
||||||
'posix/timezone.h',
|
'posix/timezone.h',
|
||||||
'process_snapshot.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',
|
'snapshot_constants.h',
|
||||||
'system_snapshot.h',
|
'system_snapshot.h',
|
||||||
'thread_snapshot.h',
|
'thread_snapshot.h',
|
||||||
@ -176,6 +184,7 @@
|
|||||||
'sources/': [
|
'sources/': [
|
||||||
['exclude', '^elf/'],
|
['exclude', '^elf/'],
|
||||||
['exclude', '^crashpad_types/'],
|
['exclude', '^crashpad_types/'],
|
||||||
|
['exclude', '^sanitized/'],
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
['target_arch!="ia32" and target_arch!="x64"', {
|
['target_arch!="ia32" and target_arch!="x64"', {
|
||||||
|
@ -92,6 +92,7 @@
|
|||||||
'mac/system_snapshot_mac_test.cc',
|
'mac/system_snapshot_mac_test.cc',
|
||||||
'minidump/process_snapshot_minidump_test.cc',
|
'minidump/process_snapshot_minidump_test.cc',
|
||||||
'posix/timezone_test.cc',
|
'posix/timezone_test.cc',
|
||||||
|
'sanitized/process_snapshot_sanitized_test.cc',
|
||||||
'win/cpu_context_win_test.cc',
|
'win/cpu_context_win_test.cc',
|
||||||
'win/exception_snapshot_win_test.cc',
|
'win/exception_snapshot_win_test.cc',
|
||||||
'win/extra_memory_ranges_test.cc',
|
'win/extra_memory_ranges_test.cc',
|
||||||
@ -145,6 +146,7 @@
|
|||||||
'sources/': [
|
'sources/': [
|
||||||
['exclude', '^elf/'],
|
['exclude', '^elf/'],
|
||||||
['exclude', '^crashpad_types/'],
|
['exclude', '^crashpad_types/'],
|
||||||
|
['exclude', '^sanitized/'],
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
|
@ -104,6 +104,8 @@ static_library("util") {
|
|||||||
"misc/pdb_structures.h",
|
"misc/pdb_structures.h",
|
||||||
"misc/random_string.cc",
|
"misc/random_string.cc",
|
||||||
"misc/random_string.h",
|
"misc/random_string.h",
|
||||||
|
"misc/range_set.cc",
|
||||||
|
"misc/range_set.h",
|
||||||
"misc/reinterpret_bytes.cc",
|
"misc/reinterpret_bytes.cc",
|
||||||
"misc/reinterpret_bytes.h",
|
"misc/reinterpret_bytes.h",
|
||||||
"misc/scoped_forbid_return.cc",
|
"misc/scoped_forbid_return.cc",
|
||||||
@ -256,9 +258,7 @@ static_library("util") {
|
|||||||
defines = [ "CRASHPAD_USE_BORINGSSL" ]
|
defines = [ "CRASHPAD_USE_BORINGSSL" ]
|
||||||
|
|
||||||
if (crashpad_is_in_fuchsia) {
|
if (crashpad_is_in_fuchsia) {
|
||||||
deps += [
|
deps += [ "//third_party/boringssl" ]
|
||||||
"//third_party/boringssl"
|
|
||||||
]
|
|
||||||
} else {
|
} else {
|
||||||
libs = [
|
libs = [
|
||||||
"crypto",
|
"crypto",
|
||||||
@ -490,13 +490,13 @@ if (!crashpad_is_android) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (use_boringssl_for_http_transport_socket) {
|
if (use_boringssl_for_http_transport_socket) {
|
||||||
data_deps = [ ":generate_test_server_key" ]
|
data_deps = [
|
||||||
|
":generate_test_server_key",
|
||||||
|
]
|
||||||
defines = [ "CRASHPAD_USE_BORINGSSL" ]
|
defines = [ "CRASHPAD_USE_BORINGSSL" ]
|
||||||
|
|
||||||
if (crashpad_is_in_fuchsia) {
|
if (crashpad_is_in_fuchsia) {
|
||||||
deps += [
|
deps += [ "//third_party/boringssl" ]
|
||||||
"//third_party/boringssl"
|
|
||||||
]
|
|
||||||
} else {
|
} else {
|
||||||
libs = [
|
libs = [
|
||||||
"crypto",
|
"crypto",
|
||||||
@ -526,6 +526,7 @@ source_set("util_test") {
|
|||||||
"misc/initialization_state_test.cc",
|
"misc/initialization_state_test.cc",
|
||||||
"misc/paths_test.cc",
|
"misc/paths_test.cc",
|
||||||
"misc/random_string_test.cc",
|
"misc/random_string_test.cc",
|
||||||
|
"misc/range_set_test.cc",
|
||||||
"misc/reinterpret_bytes_test.cc",
|
"misc/reinterpret_bytes_test.cc",
|
||||||
"misc/scoped_forbid_return_test.cc",
|
"misc/scoped_forbid_return_test.cc",
|
||||||
"misc/time_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/pdb_structures.h',
|
||||||
'misc/random_string.cc',
|
'misc/random_string.cc',
|
||||||
'misc/random_string.h',
|
'misc/random_string.h',
|
||||||
|
'misc/range_set.cc',
|
||||||
|
'misc/range_set.h',
|
||||||
'misc/reinterpret_bytes.cc',
|
'misc/reinterpret_bytes.cc',
|
||||||
'misc/reinterpret_bytes.h',
|
'misc/reinterpret_bytes.h',
|
||||||
'misc/scoped_forbid_return.cc',
|
'misc/scoped_forbid_return.cc',
|
||||||
|
@ -79,6 +79,7 @@
|
|||||||
'misc/paths_test.cc',
|
'misc/paths_test.cc',
|
||||||
'misc/scoped_forbid_return_test.cc',
|
'misc/scoped_forbid_return_test.cc',
|
||||||
'misc/random_string_test.cc',
|
'misc/random_string_test.cc',
|
||||||
|
'misc/range_set_test.cc',
|
||||||
'misc/reinterpret_bytes_test.cc',
|
'misc/reinterpret_bytes_test.cc',
|
||||||
'misc/time_test.cc',
|
'misc/time_test.cc',
|
||||||
'misc/uuid_test.cc',
|
'misc/uuid_test.cc',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user