277 lines
8.5 KiB
C++
277 lines
8.5 KiB
C++
// Copyright 2018 The Crashpad Authors
|
|
//
|
|
// 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/linux/pac_helper.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) {
|
|
auto word = StripPACBits(words[index]);
|
|
if (word >= low_ && word < 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,
|
|
std::unique_ptr<const std::vector<std::string>> allowed_annotations,
|
|
std::unique_ptr<const std::vector<std::pair<VMAddress, VMAddress>>>
|
|
allowed_memory_ranges,
|
|
VMAddress target_module_address,
|
|
bool sanitize_stacks) {
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|
snapshot_ = snapshot;
|
|
allowed_annotations_ = std::move(allowed_annotations);
|
|
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 (allowed_annotations_) {
|
|
for (const auto module : snapshot_->Modules()) {
|
|
modules_.emplace_back(std::make_unique<internal::ModuleSnapshotSanitized>(
|
|
module, allowed_annotations_.get()));
|
|
}
|
|
}
|
|
|
|
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_));
|
|
}
|
|
}
|
|
|
|
process_memory_.Initialize(snapshot_->Memory(), allowed_memory_ranges.get());
|
|
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
return true;
|
|
}
|
|
|
|
crashpad::ProcessID ProcessSnapshotSanitized::ProcessID() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return snapshot_->ProcessID();
|
|
}
|
|
|
|
crashpad::ProcessID 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 (!allowed_annotations_) {
|
|
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();
|
|
}
|
|
|
|
const ProcessMemory* ProcessSnapshotSanitized::Memory() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return &process_memory_;
|
|
}
|
|
|
|
} // namespace crashpad
|