清除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);

完。

posted @ 2024-09-19 15:42  摩斯电码  阅读(21)  评论(0编辑  收藏  举报