[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