@@ -20,6 +20,14 @@
#include <asm/stack_pointer.h>
#include <asm/stacktrace.h>
+enum kunwind_source {
+ KUNWIND_SOURCE_UNKNOWN,
+ KUNWIND_SOURCE_FRAME,
+ KUNWIND_SOURCE_CALLER,
+ KUNWIND_SOURCE_TASK,
+ KUNWIND_SOURCE_REGS_PC,
+};
+
/*
* Kernel unwind state
*
@@ -37,6 +45,7 @@ struct kunwind_state {
#ifdef CONFIG_KRETPROBES
struct llist_node *kr_cur;
#endif
+ enum kunwind_source source;
};
static __always_inline void
@@ -45,6 +54,7 @@ kunwind_init(struct kunwind_state *state,
{
unwind_init_common(&state->common);
state->task = task;
+ state->source = KUNWIND_SOURCE_UNKNOWN;
}
/*
@@ -62,6 +72,7 @@ kunwind_init_from_regs(struct kunwind_state *state,
state->common.fp = regs->regs[29];
state->common.pc = regs->pc;
+ state->source = KUNWIND_SOURCE_REGS_PC;
}
/*
@@ -79,6 +90,7 @@ kunwind_init_from_caller(struct kunwind_state *state)
state->common.fp = (unsigned long)__builtin_frame_address(1);
state->common.pc = (unsigned long)__builtin_return_address(0);
+ state->source = KUNWIND_SOURCE_CALLER;
}
/*
@@ -99,6 +111,7 @@ kunwind_init_from_task(struct kunwind_state *state,
state->common.fp = thread_saved_fp(task);
state->common.pc = thread_saved_pc(task);
+ state->source = KUNWIND_SOURCE_TASK;
}
static __always_inline int
@@ -148,9 +161,19 @@ kunwind_next(struct kunwind_state *state)
if (fp == (unsigned long)&task_pt_regs(tsk)->stackframe)
return -ENOENT;
- err = unwind_next_frame_record(&state->common);
- if (err)
- return err;
+ switch (state->source) {
+ case KUNWIND_SOURCE_FRAME:
+ case KUNWIND_SOURCE_CALLER:
+ case KUNWIND_SOURCE_TASK:
+ case KUNWIND_SOURCE_REGS_PC:
+ err = unwind_next_frame_record(&state->common);
+ if (err)
+ return err;
+ state->source = KUNWIND_SOURCE_FRAME;
+ break;
+ default:
+ return -EINVAL;
+ }
state->common.pc = ptrauth_strip_kernel_insn_pac(state->common.pc);
@@ -294,10 +317,26 @@ noinline noinstr void arch_bpf_stack_walk(bool (*consume_entry)(void *cookie, u6
kunwind_stack_walk(arch_bpf_unwind_consume_entry, &data, current, NULL);
}
+static const char *state_source_string(const struct kunwind_state *state)
+{
+ switch (state->source) {
+ case KUNWIND_SOURCE_FRAME: return NULL;
+ case KUNWIND_SOURCE_CALLER: return "C";
+ case KUNWIND_SOURCE_TASK: return "T";
+ case KUNWIND_SOURCE_REGS_PC: return "P";
+ default: return "U";
+ }
+}
+
static bool dump_backtrace_entry(const struct kunwind_state *state, void *arg)
{
+ const char *source = state_source_string(state);
char *loglvl = arg;
- printk("%s %pSb\n", loglvl, (void *)state->common.pc);
+ printk("%s %pSb%s%s%s\n", loglvl,
+ (void *)state->common.pc,
+ source ? " (" : "",
+ source ? source : "",
+ source ? ")" : "");
return true;
}