[Crash-utility] [PATCH v3] Determine the ARM64 kernel's Pointer Authentication mask value by reading the new KERNELPACMASK vmcoreinfo entry.

Amit Daniel Kachhap amit.kachhap at arm.com
Fri Apr 24 12:28:00 UTC 2020


Pointer authentication support is added in the recent versions of the arm64
kernel. This basically add PAC bits to the top unused bits of the lr
register in the stack to prevent ROP kind of attack.

However the presence of PAC bits fails to match with the correct symbol
name. Hence a KERNELPACMASK field is added in the vmcoreinfo to help
in masking out this PAC details.

This patch fetches the KERNELPACMASK info and use it to mask the PAC bits
and generate correct backtrace and symbol name.
(amit.kachhap at arm.com)
---

Changes sice v2:
* Removed PAC mask check from arm64_is_kernel_exception_frame function
* More details in commit.

Changes since v1:
* Moved PAC mask code from arm64_print_stackframe_entry to
 arm64_unwind_frame.
* PAC mask check on all kernel text during complete stack parsing
  with bt -t <pid> command.
* dump_machdep_table now prints CONFIG_ARM64_KERNELPACMASK.

The kernel version for the corresponding vmcoreinfo entry is posted here[1].

[1]: https://lore.kernel.org/patchwork/patch/1211981/


 arm64.c | 50 +++++++++++++++++++++++++++++++++++++++++---------
 defs.h  |  1 +
 2 files changed, 42 insertions(+), 9 deletions(-)

diff --git a/arm64.c b/arm64.c
index 7662d71..e0a5cf2 100644
--- a/arm64.c
+++ b/arm64.c
@@ -84,6 +84,7 @@ static int arm64_get_kvaddr_ranges(struct vaddr_range *);
 static void arm64_get_crash_notes(void);
 static void arm64_calc_VA_BITS(void);
 static int arm64_is_uvaddr(ulong, struct task_context *);
+static void arm64_calc_KERNELPACMASK(void);
 
 
 /*
@@ -213,6 +214,7 @@ arm64_init(int when)
 		machdep->pagemask = ~((ulonglong)machdep->pageoffset);
 
 		arm64_calc_VA_BITS();
+		arm64_calc_KERNELPACMASK();
 		ms = machdep->machspec;
 		if (ms->VA_BITS_ACTUAL) {
 			ms->page_offset = ARM64_PAGE_OFFSET_ACTUAL;
@@ -472,6 +474,7 @@ arm64_init(int when)
 	case LOG_ONLY:
 		machdep->machspec = &arm64_machine_specific;
 		arm64_calc_VA_BITS();
+		arm64_calc_KERNELPACMASK();
 		arm64_calc_phys_offset();
 		machdep->machspec->page_offset = ARM64_PAGE_OFFSET;
 		break;
@@ -659,6 +662,11 @@ arm64_dump_machdep_table(ulong arg)
 		fprintf(fp, "%ld\n", ms->VA_BITS_ACTUAL);
 	else
 		fprintf(fp, "(unused)\n");
+	fprintf(fp, "CONFIG_ARM64_KERNELPACMASK: ");
+	if (ms->CONFIG_ARM64_KERNELPACMASK)
+		fprintf(fp, "%lx\n", ms->CONFIG_ARM64_KERNELPACMASK);
+	else
+		fprintf(fp, "(unused)\n");
 	fprintf(fp, "         userspace_top: %016lx\n", ms->userspace_top);
 	fprintf(fp, "           page_offset: %016lx\n", ms->page_offset);
 	fprintf(fp, "    vmalloc_start_addr: %016lx\n", ms->vmalloc_start_addr);
@@ -1774,13 +1782,14 @@ static int
 arm64_is_kernel_exception_frame(struct bt_info *bt, ulong stkptr)
 {
         struct arm64_pt_regs *regs;
+	struct machine_specific *ms = machdep->machspec;
 
         regs = (struct arm64_pt_regs *)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(stkptr))];
 
 	if (INSTACK(regs->sp, bt) && INSTACK(regs->regs[29], bt) && 
 	    !(regs->pstate & (0xffffffff00000000ULL | PSR_MODE32_BIT)) &&
 	    is_kernel_text(regs->pc) &&
-	    is_kernel_text(regs->regs[30])) {
+	    is_kernel_text(regs->regs[30] | ms->CONFIG_ARM64_KERNELPACMASK)) {
 		switch (regs->pstate & PSR_MODE_MASK)
 		{
 		case PSR_MODE_EL1t:
@@ -1924,6 +1933,7 @@ arm64_print_stackframe_entry(struct bt_info *bt, int level, struct arm64_stackfr
          * See, for example, "bl schedule" before ret_to_user().
          */
 	branch_pc = frame->pc - 4;
+
         name = closest_symbol(branch_pc);
         name_plus_offset = NULL;
 
@@ -2135,7 +2145,7 @@ arm64_unwind_frame(struct bt_info *bt, struct arm64_stackframe *frame)
 	unsigned long stack_mask;
 	unsigned long irq_stack_ptr, orig_sp;
 	struct arm64_pt_regs *ptregs;
-	struct machine_specific *ms;
+	struct machine_specific *ms = machdep->machspec;
 
 	stack_mask = (unsigned long)(ARM64_STACK_SIZE) - 1;
 	fp = frame->fp;
@@ -2149,6 +2159,8 @@ arm64_unwind_frame(struct bt_info *bt, struct arm64_stackframe *frame)
 	frame->sp = fp + 0x10;
 	frame->fp = GET_STACK_ULONG(fp);
 	frame->pc = GET_STACK_ULONG(fp + 8);
+	if (is_kernel_text(frame->pc | ms->CONFIG_ARM64_KERNELPACMASK))
+		frame->pc |= ms->CONFIG_ARM64_KERNELPACMASK;
 
 	if ((frame->fp == 0) && (frame->pc == 0))
 		return FALSE;
@@ -2200,7 +2212,6 @@ arm64_unwind_frame(struct bt_info *bt, struct arm64_stackframe *frame)
 	 *  irq_stack_ptr = IRQ_STACK_PTR(raw_smp_processor_id());
 	 *  orig_sp = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr);   (pt_regs pointer on process stack)
 	 */
-	ms = machdep->machspec;
 	irq_stack_ptr = ms->irq_stacks[bt->tc->processor] + ms->irq_stack_size - 16;
 
 	if (frame->sp == irq_stack_ptr) {
@@ -2802,6 +2813,8 @@ arm64_print_text_symbols(struct bt_info *bt, struct arm64_stackframe *frame, FIL
 	char buf2[BUFSIZE];
 	char *name;
 	ulong start;
+	ulong val;
+	struct machine_specific *ms = machdep->machspec;
 
 	if (bt->flags & BT_TEXT_SYMBOLS_ALL)
 		start = bt->stackbase;
@@ -2816,8 +2829,10 @@ arm64_print_text_symbols(struct bt_info *bt, struct arm64_stackframe *frame, FIL
 
 	for (i = (start - bt->stackbase)/sizeof(ulong); i < LONGS_PER_STACK; i++) {
 		up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]);
-		if (is_kernel_text(*up)) {
-			name = closest_symbol(*up);
+		val = *up;
+		if (is_kernel_text(val | ms->CONFIG_ARM64_KERNELPACMASK)) {
+			val |= ms->CONFIG_ARM64_KERNELPACMASK;
+			name = closest_symbol(val);
 			fprintf(ofp, "  %s[%s] %s at %lx",
 				bt->flags & BT_ERROR_MASK ?
 				"  " : "",
@@ -2826,13 +2841,13 @@ arm64_print_text_symbols(struct bt_info *bt, struct arm64_stackframe *frame, FIL
 				MKSTR(bt->stackbase + 
 				(i * sizeof(long)))),
 				bt->flags & BT_SYMBOL_OFFSET ?
-				value_to_symstr(*up, buf2, bt->radix) :
-				name, *up);
-			if (module_symbol(*up, NULL, &lm, NULL, 0))
+				value_to_symstr(val, buf2, bt->radix) :
+				name, val);
+			if (module_symbol(val, NULL, &lm, NULL, 0))
 				fprintf(ofp, " [%s]", lm->mod_name);
 			fprintf(ofp, "\n");
 			if (BT_REFERENCE_CHECK(bt))
-				arm64_do_bt_reference_check(bt, *up, name);
+				arm64_do_bt_reference_check(bt, val, name);
 		}
 	}
 }
@@ -3135,6 +3150,7 @@ arm64_print_exception_frame(struct bt_info *bt, ulong pt_regs, int mode, FILE *o
 	struct syment *sp;
 	ulong LR, SP, offset;
 	char buf[BUFSIZE];
+	struct machine_specific *ms = machdep->machspec;
 
 	if (CRASHDEBUG(1)) 
 		fprintf(ofp, "pt_regs: %lx\n", pt_regs);
@@ -3150,6 +3166,8 @@ arm64_print_exception_frame(struct bt_info *bt, ulong pt_regs, int mode, FILE *o
 		rows = 4;
 	} else {
 		LR = regs->regs[30];
+		if (is_kernel_text (LR | ms->CONFIG_ARM64_KERNELPACMASK))
+			LR |= ms->CONFIG_ARM64_KERNELPACMASK;
 		SP = regs->sp;
 		top_reg = 29;
 		is_64_bit = TRUE;
@@ -4058,6 +4076,20 @@ arm64_swp_offset(ulong pte)
 	return pte;
 }
 
+static void arm64_calc_KERNELPACMASK(void)
+{
+	ulong value;
+	char *string;
+
+	if ((string = pc->read_vmcoreinfo("NUMBER(KERNELPACMASK)"))) {
+		value = htol(string, QUIET, NULL);
+		free(string);
+		machdep->machspec->CONFIG_ARM64_KERNELPACMASK = value;
+		if (CRASHDEBUG(1))
+			fprintf(fp, "CONFIG_ARM64_KERNELPACMASK=%lx\n", value);
+	}
+}
+
 #endif  /* ARM64 */
 
 
diff --git a/defs.h b/defs.h
index ac24a5d..4c3e509 100644
--- a/defs.h
+++ b/defs.h
@@ -3263,6 +3263,7 @@ struct machine_specific {
 	ulong machine_kexec_end;
 	ulong VA_BITS_ACTUAL;
 	ulong CONFIG_ARM64_VA_BITS;
+	ulong CONFIG_ARM64_KERNELPACMASK;
 	ulong VA_START;
 };
 
-- 
2.17.1





More information about the Crash-utility mailing list