arm64: strip PACs from link register values

Pointer authentication codes are used to validate pointers against
accidental or malicious modification by storing a hash of the address
and a secret value in the pointer's unused, upper bits. The exact
bits used may vary by implementation and depend on the size of the
virtual address space of the target system, and whether other tagged
pointer features are in use.

Apple has implemented PACs in the Apple A12.
https://developer.apple.com/documentation/security/preparing_your_app_to_work_with_pointer_authentication

The documented method of stripping PACs from a pointer is to call
ptrauth_strip(), which ultimately emits an `xpaci` instruction, but
this option isn't available to the Breakpad processor not running on
the device. Instead, this patch selects likely address bits from
link register values by examining the address range of loaded modules.

Change-Id: I054bd1a03605719937fc85dcc8d8b9fe927f44be
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/1713650
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2019-07-22 13:26:19 -07:00
parent 4a6d7c70cc
commit cfad51e954
2 changed files with 33 additions and 1 deletions

View file

@ -54,8 +54,29 @@ StackwalkerARM64::StackwalkerARM64(const SystemInfo* system_info,
StackFrameSymbolizer* resolver_helper) StackFrameSymbolizer* resolver_helper)
: Stackwalker(system_info, memory, modules, resolver_helper), : Stackwalker(system_info, memory, modules, resolver_helper),
context_(context), context_(context),
context_frame_validity_(StackFrameARM64::CONTEXT_VALID_ALL) { } context_frame_validity_(StackFrameARM64::CONTEXT_VALID_ALL),
address_range_mask_(0xffffffffffffffff) {
if (modules && modules->module_count() > 0) {
// ARM64 supports storing pointer authentication codes in the upper bits of
// a pointer. Make a best guess at the range of valid addresses based on the
// range of loaded modules.
const CodeModule *high_module =
modules->GetModuleAtSequence(modules->module_count() - 1);
uint64_t mask = high_module->base_address() + high_module->size();
mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
mask |= mask >> 16;
mask |= mask >> 32;
address_range_mask_ = mask;
}
}
uint64_t StackwalkerARM64::PtrauthStrip(uint64_t ptr) {
uint64_t stripped = ptr & address_range_mask_;
return modules_ && modules_->GetModuleForAddress(stripped) ? stripped : ptr;
}
StackFrame* StackwalkerARM64::GetContextFrame() { StackFrame* StackwalkerARM64::GetContextFrame() {
if (!context_) { if (!context_) {
@ -71,6 +92,8 @@ StackFrame* StackwalkerARM64::GetContextFrame() {
frame->context_validity = context_frame_validity_; frame->context_validity = context_frame_validity_;
frame->trust = StackFrame::FRAME_TRUST_CONTEXT; frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM64_REG_PC]; frame->instruction = frame->context.iregs[MD_CONTEXT_ARM64_REG_PC];
frame->context.iregs[MD_CONTEXT_ARM64_REG_LR] =
PtrauthStrip(frame->context.iregs[MD_CONTEXT_ARM64_REG_LR]);
return frame; return frame;
} }
@ -202,6 +225,8 @@ StackFrameARM64* StackwalkerARM64::GetCallerByFramePointer(
return NULL; return NULL;
} }
caller_lr = PtrauthStrip(caller_lr);
uint64_t caller_sp = last_fp ? last_fp + 16 : uint64_t caller_sp = last_fp ? last_fp + 16 :
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP]; last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP];

View file

@ -68,6 +68,9 @@ class StackwalkerARM64 : public Stackwalker {
} }
private: private:
// Strip pointer authentication codes from an address.
uint64_t PtrauthStrip(uint64_t ptr);
// Implementation of Stackwalker, using arm64 context and stack conventions. // Implementation of Stackwalker, using arm64 context and stack conventions.
virtual StackFrame* GetContextFrame(); virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack* stack, virtual StackFrame* GetCallerFrame(const CallStack* stack,
@ -95,6 +98,10 @@ class StackwalkerARM64 : public Stackwalker {
// CONTEXT_VALID_ALL in real use; it is only changeable for the sake of // CONTEXT_VALID_ALL in real use; it is only changeable for the sake of
// unit tests. // unit tests.
uint64_t context_frame_validity_; uint64_t context_frame_validity_;
// A mask of the valid address bits, determined from the address range of
// modules_.
uint64_t address_range_mask_;
}; };