Re: [RFC PATCH v2 0/1] arm64: Implement stack trace termination record

From: Madhavan T. Venkataraman
Date: Mon Apr 19 2021 - 14:18:21 EST


Sorry. Forgot to include link to v2. Here it is:

https://lore.kernel.org/linux-arm-kernel/20210402032404.47239-1-madvenka@xxxxxxxxxxxxxxxxxxx/

Thanks!

Madhavan

On 4/19/21 1:16 PM, Madhavan T. Venkataraman wrote:
> CCing Pavel Tatashin <pasha.tatashin@xxxxxxxxxx> on request.
>
> Pasha,
>
> This is v2. v1 is here:
>
> https://lore.kernel.org/linux-arm-kernel/20210324184607.120948-1-madvenka@xxxxxxxxxxxxxxxxxxx/
>
> Thanks!
>
> Madhavan
>
> On 4/1/21 10:24 PM, madvenka@xxxxxxxxxxxxxxxxxxx wrote:
>> From: "Madhavan T. Venkataraman" <madvenka@xxxxxxxxxxxxxxxxxxx>
>>
>> Reliable stacktracing requires that we identify when a stacktrace is
>> terminated early. We can do this by ensuring all tasks have a final
>> frame record at a known location on their task stack, and checking
>> that this is the final frame record in the chain.
>>
>> All tasks have a pt_regs structure right after the task stack in the stack
>> page. The pt_regs structure contains a stackframe field. Make this stackframe
>> field the final frame in the task stack so all stack traces end at a fixed
>> stack offset.
>>
>> For kernel tasks, this is simple to understand. For user tasks, there is
>> some extra detail. User tasks get created via fork() et al. Once they return
>> from fork, they enter the kernel only on an EL0 exception. In arm64,
>> system calls are also EL0 exceptions.
>>
>> The EL0 exception handler uses the task pt_regs mentioned above to save
>> register state and call different exception functions. All stack traces
>> from EL0 exception code must end at the pt_regs. So, make pt_regs->stackframe
>> the final frame in the EL0 exception stack.
>>
>> To summarize, task_pt_regs(task)->stackframe will always be the final frame
>> in a stack trace.
>>
>> Sample stack traces
>> ===================
>>
>> The final frame for the idle tasks is different from v1. The rest of the
>> stack traces are the same.
>>
>> Primary CPU's idle task (changed from v1)
>> =======================
>>
>> [ 0.022365] arch_stack_walk+0x0/0xd0
>> [ 0.022376] callfd_stack+0x30/0x60
>> [ 0.022387] rest_init+0xd8/0xf8
>> [ 0.022397] arch_call_rest_init+0x18/0x24
>> [ 0.022411] start_kernel+0x5b8/0x5f4
>> [ 0.022424] __primary_switched+0xa8/0xac
>>
>> Secondary CPU's idle task (changed from v1)
>> =========================
>>
>> [ 0.022484] arch_stack_walk+0x0/0xd0
>> [ 0.022494] callfd_stack+0x30/0x60
>> [ 0.022502] secondary_start_kernel+0x188/0x1e0
>> [ 0.022513] __secondary_switched+0x80/0x84
>>
>> ---
>> Changelog:
>>
>> v1
>> - Set up task_pt_regs(current)->stackframe as the final frame
>> when a new task is initialized in copy_thread().
>>
>> - Create pt_regs for the idle tasks and set up pt_regs->stackframe
>> as the final frame for the idle tasks.
>>
>> - Set up task_pt_regs(current)->stackframe as the final frame in
>> the EL0 exception handler so the EL0 exception stack trace ends
>> there.
>>
>> - Terminate the stack trace successfully in unwind_frame() when
>> the FP reaches task_pt_regs(current)->stackframe.
>>
>> - The stack traces (above) in the kernel will terminate at the
>> correct place. Debuggers may show an extra record 0x0 at the end
>> for pt_regs->stackframe. That said, I did not see that extra frame
>> when I did stack traces using gdb.
>> v2
>> - Changed some wordings as suggested by Mark Rutland.
>>
>> - Removed the synthetic return PC for idle tasks. Changed the
>> branches to start_kernel() and secondary_start_kernel() to
>> calls so that they will have a proper return PC.
>>
>> Madhavan T. Venkataraman (1):
>> arm64: Implement stack trace termination record
>>
>> arch/arm64/kernel/entry.S | 8 +++++---
>> arch/arm64/kernel/head.S | 29 +++++++++++++++++++++++------
>> arch/arm64/kernel/process.c | 5 +++++
>> arch/arm64/kernel/stacktrace.c | 10 +++++-----
>> 4 files changed, 38 insertions(+), 14 deletions(-)
>>
>>
>> base-commit: 0d02ec6b3136c73c09e7859f0d0e4e2c4c07b49b
>>