Skip to content

Commit bdf8eaf

Browse files
mrutland-armctmarinas
authored andcommitted
arm64: stacktrace: report source of unwind data
When analysing a stacktrace it can be useful to know where an unwound PC came from, as in some situations certain sources may be suspect or known to be unreliable. In future it would also be useful to track this so that certain unwind steps can be performed in a stateful manner. For example when unwinding across an exception boundary, we'd ideally unwind pt_regs::pc, then pt_regs::lr, then the next frame record. This patch adds an enumerated set of unwind sources, tracks this during the unwind, and updates dump_backtrace() to log these for interesting unwind steps. The interesting sources recorded are: "C" - the PC came from the caller of an unwind function. "T" - the PC came from thread_saved_pc() for a blocked task. "P" - the PC came from a pt_regs::pc. "U" - the PC came from an unknown source (indicates an unwinder error). ... with nothing recorded when the PC came from a frame_record::pc as this is the vastly common case and logging this would make it difficult to spot the more interesting cases. For example, when triggering a backtrace via magic-sysrq + L, the CPU handling the sysrq will have a backtrace whose first element is the caller (C) of dump_backtrace(): | Call trace: | show_stack+0x18/0x30 (C) | dump_stack_lvl+0x60/0x80 | dump_stack+0x18/0x24 | nmi_cpu_backtrace+0xfc/0x140 | ... ... and other CPUs will have a backtrace whose first element is their pt_regs::pc (P) at the instant the backtrace IPI was taken: | Call trace: | _raw_spin_unlock_irqrestore+0x8/0x50 (P) | wake_up_process+0x18/0x24 | process_timeout+0x14/0x20 | call_timer_fn.isra.0+0x24/0x80 | ... Signed-off-by: Mark Rutland <mark.rutland@arm.com> Reviewed-by: Mark Brown <broonie@kernel.org> Reviewed-by: Miroslav Benes <mbenes@suse.cz> Reviewed-by: Puranjay Mohan <puranjay12@gmail.com> Cc: Ard Biesheuvel <ardb@kernel.org> Cc: Josh Poimboeuf <jpoimboe@kernel.org> Cc: Kalesh Singh <kaleshsingh@google.com> Cc: Madhavan T. Venkataraman <madvenka@linux.microsoft.com> Cc: Marc Zyngier <maz@kernel.org> Cc: Will Deacon <will@kernel.org> Link: https://lore.kernel.org/r/20241017092538.1859841-8-mark.rutland@arm.com Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
1 parent b779479 commit bdf8eaf

File tree

1 file changed

+43
-4
lines changed

1 file changed

+43
-4
lines changed

arch/arm64/kernel/stacktrace.c

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@
2020
#include <asm/stack_pointer.h>
2121
#include <asm/stacktrace.h>
2222

23+
enum kunwind_source {
24+
KUNWIND_SOURCE_UNKNOWN,
25+
KUNWIND_SOURCE_FRAME,
26+
KUNWIND_SOURCE_CALLER,
27+
KUNWIND_SOURCE_TASK,
28+
KUNWIND_SOURCE_REGS_PC,
29+
};
30+
2331
/*
2432
* Kernel unwind state
2533
*
@@ -37,6 +45,7 @@ struct kunwind_state {
3745
#ifdef CONFIG_KRETPROBES
3846
struct llist_node *kr_cur;
3947
#endif
48+
enum kunwind_source source;
4049
};
4150

4251
static __always_inline void
@@ -45,6 +54,7 @@ kunwind_init(struct kunwind_state *state,
4554
{
4655
unwind_init_common(&state->common);
4756
state->task = task;
57+
state->source = KUNWIND_SOURCE_UNKNOWN;
4858
}
4959

5060
/*
@@ -62,6 +72,7 @@ kunwind_init_from_regs(struct kunwind_state *state,
6272

6373
state->common.fp = regs->regs[29];
6474
state->common.pc = regs->pc;
75+
state->source = KUNWIND_SOURCE_REGS_PC;
6576
}
6677

6778
/*
@@ -79,6 +90,7 @@ kunwind_init_from_caller(struct kunwind_state *state)
7990

8091
state->common.fp = (unsigned long)__builtin_frame_address(1);
8192
state->common.pc = (unsigned long)__builtin_return_address(0);
93+
state->source = KUNWIND_SOURCE_CALLER;
8294
}
8395

8496
/*
@@ -99,6 +111,7 @@ kunwind_init_from_task(struct kunwind_state *state,
99111

100112
state->common.fp = thread_saved_fp(task);
101113
state->common.pc = thread_saved_pc(task);
114+
state->source = KUNWIND_SOURCE_TASK;
102115
}
103116

104117
static __always_inline int
@@ -148,9 +161,19 @@ kunwind_next(struct kunwind_state *state)
148161
if (fp == (unsigned long)&task_pt_regs(tsk)->stackframe)
149162
return -ENOENT;
150163

151-
err = unwind_next_frame_record(&state->common);
152-
if (err)
153-
return err;
164+
switch (state->source) {
165+
case KUNWIND_SOURCE_FRAME:
166+
case KUNWIND_SOURCE_CALLER:
167+
case KUNWIND_SOURCE_TASK:
168+
case KUNWIND_SOURCE_REGS_PC:
169+
err = unwind_next_frame_record(&state->common);
170+
if (err)
171+
return err;
172+
state->source = KUNWIND_SOURCE_FRAME;
173+
break;
174+
default:
175+
return -EINVAL;
176+
}
154177

155178
state->common.pc = ptrauth_strip_kernel_insn_pac(state->common.pc);
156179

@@ -294,10 +317,26 @@ noinline noinstr void arch_bpf_stack_walk(bool (*consume_entry)(void *cookie, u6
294317
kunwind_stack_walk(arch_bpf_unwind_consume_entry, &data, current, NULL);
295318
}
296319

320+
static const char *state_source_string(const struct kunwind_state *state)
321+
{
322+
switch (state->source) {
323+
case KUNWIND_SOURCE_FRAME: return NULL;
324+
case KUNWIND_SOURCE_CALLER: return "C";
325+
case KUNWIND_SOURCE_TASK: return "T";
326+
case KUNWIND_SOURCE_REGS_PC: return "P";
327+
default: return "U";
328+
}
329+
}
330+
297331
static bool dump_backtrace_entry(const struct kunwind_state *state, void *arg)
298332
{
333+
const char *source = state_source_string(state);
299334
char *loglvl = arg;
300-
printk("%s %pSb\n", loglvl, (void *)state->common.pc);
335+
printk("%s %pSb%s%s%s\n", loglvl,
336+
(void *)state->common.pc,
337+
source ? " (" : "",
338+
source ? source : "",
339+
source ? ")" : "");
301340
return true;
302341
}
303342

0 commit comments

Comments
 (0)