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

Amit Kachhap amit.kachhap at arm.com
Wed Apr 22 13:42:20 UTC 2020


Hi Dave,
On 4/22/20 1:55 AM, Dave Anderson wrote:
> 
> ----- 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".
ok
> 
> 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.

yes correct.

> 
> 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?
> 

In arm64 case arch/arm64/include/asm/kexec.h + crash_setup_regs() 
function sets up the kernel exception frame. As can be seen PC does not 
have obfuscated (PAC) values but LR can be obfuscated.

> 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?

I suppose no.

> 
> 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) &&

Yes good catch. Masking can be removed from here.

> ========>     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...

Yes agree with you that exception frame PC is not obfuscated.

Cheers,
Amit
> 
> 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