crashpad/snapshot/sanitized/process_snapshot_sanitized.cc
avvall 4773a37f0a Crashpad: Adding PAC bit stripping to stack sanitization.
Pointer Authentication works by adding a signature to the top bits of
an instruction or data pointer (only instruction pointers on the stack
are currently signed in Chromium). This can confuse range checks,
because they need to strip the top bits. Masking these bits during sanitization range checks prevents confusion.


Test: Testing was done manually on a device with pointer authentication enabled.
Bug: crashpad:364
Bug: 919548
Change-Id: I2e739cadb2844cfaf73a75596d664135aeb5faac
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4387271
Commit-Queue: Adam Walls <avvall@google.com>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Ben Hamilton <benhamilton@google.com>
2023-04-04 23:59:29 +00:00

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