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

Dave Anderson anderson at redhat.com
Tue Apr 21 20:25:08 UTC 2020


----- Original Message -----
> This value is used to mask the PAC bits and generate correct backtrace
> and symbol name.
> (amit.kachhap at arm.com)
> ---
> 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/

Hi Amit,

I'm still a bit confused here -- please help me out...  

For the lack of a better term, in the following discussion, I'm going to refer
to the text values without the KERNELPACMASK applied as "obfuscated".

Now, as I understand it, when a running function calls another function and 
leaves its text return address on the stack, the processor obfuscates the text
return value before it pushes it on the stack.  

But here's where I'm confused: when an in-kernel exception frame occurs, and the
processor lays down the full register set on the stack, are both the PC and LR (regs[30])
text values written on the stack as obfuscated values?

Here's why I'm asking...

When looking for the starting stack hooks in a dumpfile, your patch takes the PC from
the in-kernel exception frame unmodified:
  
  static int
  arm64_get_dumpfile_stackframe(struct bt_info *bt, struct arm64_stackframe *frame)
  {
          struct machine_specific *ms = machdep->machspec;
          struct arm64_pt_regs *ptregs;
  
          if (!ms->panic_task_regs ||
              (!ms->panic_task_regs[bt->tc->processor].sp &&
               !ms->panic_task_regs[bt->tc->processor].pc)) {
                  bt->flags |= BT_REGS_NOT_FOUND;
                  return FALSE;
          }
  
          ptregs = &ms->panic_task_regs[bt->tc->processor];
=======>  frame->pc = ptregs->pc;
          ...

That PC value comes from an exception frame.  Will that ptregs->pc value 
be obfuscated?

When it gathers the starting hooks for non-active tasks, it does this:

  static int
  arm64_get_stackframe(struct bt_info *bt, struct arm64_stackframe *frame)
  {
          if (!fill_task_struct(bt->task))
                  return FALSE;
  
          frame->sp = ULONG(tt->task_struct + OFFSET(task_struct_thread_context_sp));
          frame->pc = ULONG(tt->task_struct + OFFSET(task_struct_thread_context_pc));
          frame->fp = ULONG(tt->task_struct + OFFSET(task_struct_thread_context_fp));
  
          return TRUE;
  }
  
When a task is put to sleep, is the PC text address in the task's thread_struct.cpu_context
obfuscated?

And then when trying to determine whether the current stack pointer is
pointing to an in-kernel exception frame, the possible regs->pc and regs[30]
values are both transformed with the mask, so it seems that both of them
will have been obfuscated by the processor when creating the frame on
the stack:
  
  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 | ms->CONFIG_ARM64_KERNELPACMASK) &&
========>     is_kernel_text(regs->regs[30] | ms->CONFIG_ARM64_KERNELPACMASK)) {
                  switch (regs->pstate & PSR_MODE_MASK)
                  {
                  case PSR_MODE_EL1t:
                  case PSR_MODE_EL1h:
                  case PSR_MODE_EL2t:
                  case PSR_MODE_EL2h:
                          return TRUE;
                  }
          }
  
          return FALSE;
  }
  

But here when when displaying an exception frame, the LR is masked
if it "make sense", but the unmodified PC value is checked without
transforming it:

  static void
  arm64_print_exception_frame(struct bt_info *bt, ulong pt_regs, int mode, FILE *ofp)
  {
  ...
                  LR = regs->regs[30];
=======>          if (is_kernel_text (LR | ms->CONFIG_ARM64_KERNELPACMASK))
                          LR |= ms->CONFIG_ARM64_KERNELPACMASK;
  ...

          case KERNEL_MODE:
                  fprintf(ofp, "     PC: %016lx  ", (ulong)regs->pc);
=======>          if (is_kernel_text(regs->pc) &&
                      (sp = value_search(regs->pc, &offset))) {
                          fprintf(ofp, "[%s", sp->name);
                          if (offset)
                                  fprintf(ofp, (*gdb_output_radix == 16) ?
                                      "+0x%lx" : "+%ld",
                                          offset);
                          fprintf(ofp, "]\n");
                  } else
                          fprintf(ofp, "[unknown or invalid address]\n");
  
                  fprintf(ofp, "     LR: %016lx  ", LR);
=======>          if (is_kernel_text(LR) &&
                      (sp = value_search(LR, &offset))) {
                          fprintf(ofp, "[%s", sp->name);
                          if (offset)
                                  fprintf(ofp, (*gdb_output_radix == 16) ?
                                      "+0x%lx" : "+%ld",
                                          offset);
                          fprintf(ofp, "]\n");
                  } else
                          fprintf(ofp, "[unknown or invalid address]\n");
  
So that would lead me to believe that the exception frame PC is
in NOT obfuscated.  Hence my confusion...

Thanks,
  Dave























> 
>  arm64.c | 52 ++++++++++++++++++++++++++++++++++++++++++----------
>  defs.h  |  1 +
>  2 files changed, 43 insertions(+), 10 deletions(-)
> 
> diff --git a/arm64.c b/arm64.c
> index 7662d71..326b8e0 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->pc | ms->CONFIG_ARM64_KERNELPACMASK) &&
> +	    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