diff options
Diffstat (limited to 'arch/s390/kernel/stacktrace.c')
-rw-r--r-- | arch/s390/kernel/stacktrace.c | 77 |
1 files changed, 41 insertions, 36 deletions
diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c index f6a620f854e1..fc5419ac64c8 100644 --- a/arch/s390/kernel/stacktrace.c +++ b/arch/s390/kernel/stacktrace.c @@ -6,57 +6,62 @@ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> */ -#include <linux/sched.h> -#include <linux/sched/debug.h> #include <linux/stacktrace.h> -#include <linux/kallsyms.h> -#include <linux/export.h> #include <asm/stacktrace.h> #include <asm/unwind.h> +#include <asm/kprobes.h> -void save_stack_trace(struct stack_trace *trace) +void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, + struct task_struct *task, struct pt_regs *regs) { struct unwind_state state; + unsigned long addr; - unwind_for_each_frame(&state, current, NULL, 0) { - if (trace->nr_entries >= trace->max_entries) + unwind_for_each_frame(&state, task, regs, 0) { + addr = unwind_get_return_address(&state); + if (!addr || !consume_entry(cookie, addr, false)) break; - if (trace->skip > 0) - trace->skip--; - else - trace->entries[trace->nr_entries++] = state.ip; } } -EXPORT_SYMBOL_GPL(save_stack_trace); -void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) +/* + * This function returns an error if it detects any unreliable features of the + * stack. Otherwise it guarantees that the stack trace is reliable. + * + * If the task is not 'current', the caller *must* ensure the task is inactive. + */ +int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry, + void *cookie, struct task_struct *task) { struct unwind_state state; + unsigned long addr; - unwind_for_each_frame(&state, tsk, NULL, 0) { - if (trace->nr_entries >= trace->max_entries) - break; - if (in_sched_functions(state.ip)) - continue; - if (trace->skip > 0) - trace->skip--; - else - trace->entries[trace->nr_entries++] = state.ip; - } -} -EXPORT_SYMBOL_GPL(save_stack_trace_tsk); + unwind_for_each_frame(&state, task, NULL, 0) { + if (state.stack_info.type != STACK_TYPE_TASK) + return -EINVAL; -void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) -{ - struct unwind_state state; + if (state.regs) + return -EINVAL; - unwind_for_each_frame(&state, current, regs, 0) { - if (trace->nr_entries >= trace->max_entries) - break; - if (trace->skip > 0) - trace->skip--; - else - trace->entries[trace->nr_entries++] = state.ip; + addr = unwind_get_return_address(&state); + if (!addr) + return -EINVAL; + +#ifdef CONFIG_KPROBES + /* + * Mark stacktraces with kretprobed functions on them + * as unreliable. + */ + if (state.ip == (unsigned long)kretprobe_trampoline) + return -EINVAL; +#endif + + if (!consume_entry(cookie, addr, false)) + return -EINVAL; } + + /* Check for stack corruption */ + if (unwind_error(&state)) + return -EINVAL; + return 0; } -EXPORT_SYMBOL_GPL(save_stack_trace_regs); |