1
2
3
4
5
6
7#include <linux/kernel.h>
8#include <linux/export.h>
9#include <linux/ftrace.h>
10#include <linux/kprobes.h>
11#include <linux/sched.h>
12#include <linux/sched/debug.h>
13#include <linux/sched/task_stack.h>
14#include <linux/stacktrace.h>
15
16#include <asm/irq.h>
17#include <asm/pointer_auth.h>
18#include <asm/stack_pointer.h>
19#include <asm/stacktrace.h>
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
43{
44 unsigned long fp = frame->fp;
45 struct stack_info info;
46
47 if (fp & 0xf)
48 return -EINVAL;
49
50 if (!tsk)
51 tsk = current;
52
53 if (!on_accessible_stack(tsk, fp, &info))
54 return -EINVAL;
55
56 if (test_bit(info.type, frame->stacks_done))
57 return -EINVAL;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 if (info.type == frame->prev_type) {
73 if (fp <= frame->prev_fp)
74 return -EINVAL;
75 } else {
76 set_bit(frame->prev_type, frame->stacks_done);
77 }
78
79
80
81
82
83 frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
84 frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 8));
85 frame->prev_fp = fp;
86 frame->prev_type = info.type;
87
88#ifdef CONFIG_FUNCTION_GRAPH_TRACER
89 if (tsk->ret_stack &&
90 (ptrauth_strip_insn_pac(frame->pc) == (unsigned long)return_to_handler)) {
91 struct ftrace_ret_stack *ret_stack;
92
93
94
95
96
97
98 ret_stack = ftrace_graph_get_ret_stack(tsk, frame->graph++);
99 if (WARN_ON_ONCE(!ret_stack))
100 return -EINVAL;
101 frame->pc = ret_stack->ret;
102 }
103#endif
104
105 frame->pc = ptrauth_strip_insn_pac(frame->pc);
106
107
108
109
110
111
112
113 if (!frame->fp && !frame->pc)
114 return -EINVAL;
115
116 return 0;
117}
118NOKPROBE_SYMBOL(unwind_frame);
119
120void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
121 bool (*fn)(void *, unsigned long), void *data)
122{
123 while (1) {
124 int ret;
125
126 if (!fn(data, frame->pc))
127 break;
128 ret = unwind_frame(tsk, frame);
129 if (ret < 0)
130 break;
131 }
132}
133NOKPROBE_SYMBOL(walk_stackframe);
134
135static void dump_backtrace_entry(unsigned long where, const char *loglvl)
136{
137 printk("%s %pS\n", loglvl, (void *)where);
138}
139
140void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
141 const char *loglvl)
142{
143 struct stackframe frame;
144 int skip = 0;
145
146 pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
147
148 if (regs) {
149 if (user_mode(regs))
150 return;
151 skip = 1;
152 }
153
154 if (!tsk)
155 tsk = current;
156
157 if (!try_get_task_stack(tsk))
158 return;
159
160 if (tsk == current) {
161 start_backtrace(&frame,
162 (unsigned long)__builtin_frame_address(0),
163 (unsigned long)dump_backtrace);
164 } else {
165
166
167
168 start_backtrace(&frame,
169 thread_saved_fp(tsk),
170 thread_saved_pc(tsk));
171 }
172
173 printk("%sCall trace:\n", loglvl);
174 do {
175
176 if (!skip) {
177 dump_backtrace_entry(frame.pc, loglvl);
178 } else if (frame.fp == regs->regs[29]) {
179 skip = 0;
180
181
182
183
184
185
186
187 dump_backtrace_entry(regs->pc, loglvl);
188 }
189 } while (!unwind_frame(tsk, &frame));
190
191 put_task_stack(tsk);
192}
193
194void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl)
195{
196 dump_backtrace(NULL, tsk, loglvl);
197 barrier();
198}
199
200#ifdef CONFIG_STACKTRACE
201
202void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
203 struct task_struct *task, struct pt_regs *regs)
204{
205 struct stackframe frame;
206
207 if (regs)
208 start_backtrace(&frame, regs->regs[29], regs->pc);
209 else if (task == current)
210 start_backtrace(&frame,
211 (unsigned long)__builtin_frame_address(0),
212 (unsigned long)arch_stack_walk);
213 else
214 start_backtrace(&frame, thread_saved_fp(task),
215 thread_saved_pc(task));
216
217 walk_stackframe(task, &frame, consume_entry, cookie);
218}
219
220#endif
221