清除PAC在地址中添加的额外bit
参考
Android中
void OfflineUnwinder::CollectMetaInfo(std::unordered_map<std::string, std::string>* info_map
__attribute__((unused))) {
#if defined(__aarch64__)
// Find pac_mask for ARMv8.3-A Pointer Authentication by below steps:
// 1. Create a 64 bit value with every bit set, but clear bit 55. Because linux user space uses
// TTBR0.
// 2. Use XPACLRI to clear auth code bits.
// 3. Flip every bit to get pac_mask, excluding bit 55.
// We can also use ptrace(PTRACE_GETREGSET, pid, NT_ARM_PAC_MASK). But it needs a tracee.
register uint64_t x30 __asm("x30") = ~(1ULL << 55);
// This is XPACLRI on ARMv8.3-A, and nop on prev ARMv8.3-A.
asm("hint 0x7" : "+r"(x30));
uint64_t pac_mask = ~x30 & ~(1ULL << 55);
if (pac_mask != 0) {
(*info_map)[META_KEY_ARM64_PAC_MASK] = android::base::StringPrintf("0x%" PRIx64, pac_mask);
}
#endif
}
static uint64_t strip_pac(uint64_t pc, uint64_t mask) {
// If the target is aarch64 then the return address may have been
// signed using the Armv8.3-A Pointer Authentication extension. The
// original return address can be restored by stripping out the
// authentication code using a mask or xpaclri. xpaclri is a NOP on
// pre-Armv8.3-A architectures.
if (mask) {
pc &= ~mask;
} else {
#if defined(__BIONIC__)
pc = __bionic_clear_pac_bits(pc);
#endif
}
return pc;
}
inline uintptr_t __bionic_clear_pac_bits(uintptr_t ptr) {
#if defined(__aarch64__)
register uintptr_t x30 __asm("x30") = ptr;
// This is a NOP on pre-Armv8.3-A architectures.
asm("xpaclri" : "+r"(x30));
return x30;
#else
return ptr;
#endif
}
Linux内核
内核在生成VMCORE时,会将PAC占用的位域记录到VMCORE中:
void arch_crash_save_vmcoreinfo(void)
{
VMCOREINFO_NUMBER(VA_BITS);
/* Please note VMCOREINFO_NUMBER() uses "%d", not "%x" */
vmcoreinfo_append_str("NUMBER(MODULES_VADDR)=0x%lx\n", MODULES_VADDR);
vmcoreinfo_append_str("NUMBER(MODULES_END)=0x%lx\n", MODULES_END);
vmcoreinfo_append_str("NUMBER(VMALLOC_END)=0x%lx\n", VMALLOC_END);
vmcoreinfo_append_str("NUMBER(VMEMMAP_START)=0x%lx\n", VMEMMAP_START);
vmcoreinfo_append_str("NUMBER(VMEMMAP_END)=0x%lx\n", VMEMMAP_END);
vmcoreinfo_append_str("NUMBER(kimage_voffset)=0x%llx\n",
kimage_voffset);
vmcoreinfo_append_str("NUMBER(PHYS_OFFSET)=0x%llx\n",
PHYS_OFFSET);
vmcoreinfo_append_str("NUMBER(TCR_EL1_T1SZ)=0x%llx\n",
get_tcr_el1_t1sz());
vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset());
vmcoreinfo_append_str("NUMBER(KERNELPACMASK)=0x%llx\n",
system_supports_address_auth() ?
ptrauth_kernel_pac_mask() : 0);
}
上面调用了ptrauth_kernel_pac_mask
来得到内核态PAC占用的位域:
/*
* The EL0/EL1 pointer bits used by a pointer authentication code.
* This is dependent on TBI0/TBI1 being enabled, or bits 63:56 would also apply.
*/
#define ptrauth_user_pac_mask() GENMASK_ULL(54, vabits_actual)
#define ptrauth_kernel_pac_mask() GENMASK_ULL(63, vabits_actual)
假如va是39位的话,在内核态,位域就是[63:39],用户态就是[54:39].
内核提供了下面的方法来清除地址中的PAC位:
static inline unsigned long ptrauth_strip_insn_pac(unsigned long ptr)
{
return ptrauth_clear_pac(ptr);
}
/* Valid for EL0 TTBR0 and EL1 TTBR1 instruction pointers */
#define ptrauth_clear_pac(ptr) \
((ptr & BIT_ULL(55)) ? (ptr | ptrauth_kernel_pac_mask()) : \
(ptr & ~ptrauth_user_pac_mask()))
crash工具
是通过解析VMCORE得到KERNELPACMASK:
static void arm64_calc_KERNELPACMASK(void)
{
ulong value;
if (arm64_get_vmcoreinfo(&value, "NUMBER(KERNELPACMASK)", NUM_HEX)) {
machdep->machspec->CONFIG_ARM64_KERNELPACMASK = value;
if (CRASHDEBUG(1))
fprintf(fp, "CONFIG_ARM64_KERNELPACMASK: %lx\n", value);
}
}
在输出地址对应的符号时,会用到上面的值:
if (is_kernel_text(val | ms->CONFIG_ARM64_KERNELPACMASK)) {
val |= ms->CONFIG_ARM64_KERNELPACMASK;
name = closest_symbol(val);
完。
本文来自博客园,作者:摩斯电码,未经同意,禁止转载