1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <linux/export.h> 3 #include <linux/kprobes.h> 4 #include <linux/sched.h> 5 #include <linux/sched/debug.h> 6 #include <linux/stacktrace.h> 7 8 #include <asm/sections.h> 9 #include <asm/stacktrace.h> 10 #include <asm/traps.h> 11 12 #include "reboot.h" 13 14 #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) 15 /* 16 * Unwind the current stack frame and store the new register values in the 17 * structure passed as argument. Unwinding is equivalent to a function return, 18 * hence the new PC value rather than LR should be used for backtrace. 19 * 20 * With framepointer enabled, a simple function prologue looks like this: 21 * mov ip, sp 22 * stmdb sp!, {fp, ip, lr, pc} 23 * sub fp, ip, #4 24 * 25 * A simple function epilogue looks like this: 26 * ldm sp, {fp, sp, pc} 27 * 28 * When compiled with clang, pc and sp are not pushed. A simple function 29 * prologue looks like this when built with clang: 30 * 31 * stmdb {..., fp, lr} 32 * add fp, sp, #x 33 * sub sp, sp, #y 34 * 35 * A simple function epilogue looks like this when built with clang: 36 * 37 * sub sp, fp, #x 38 * ldm {..., fp, pc} 39 * 40 * 41 * Note that with framepointer enabled, even the leaf functions have the same 42 * prologue and epilogue, therefore we can ignore the LR value in this case. 43 */ 44 45 extern unsigned long call_with_stack_end; 46 47 static int frame_pointer_check(struct stackframe *frame) 48 { 49 unsigned long high, low; 50 unsigned long fp = frame->fp; 51 unsigned long pc = frame->pc; 52 53 /* 54 * call_with_stack() is the only place we allow SP to jump from one 55 * stack to another, with FP and SP pointing to different stacks, 56 * skipping the FP boundary check at this point. 57 */ 58 if (pc >= (unsigned long)&call_with_stack && 59 pc < (unsigned long)&call_with_stack_end) 60 return 0; 61 62 /* only go to a higher address on the stack */ 63 low = frame->sp; 64 high = ALIGN(low, THREAD_SIZE); 65 66 /* check current frame pointer is within bounds */ 67 #ifdef CONFIG_CC_IS_CLANG 68 if (fp < low + 4 || fp > high - 4) 69 return -EINVAL; 70 #else 71 if (fp < low + 12 || fp > high - 4) 72 return -EINVAL; 73 #endif 74 75 return 0; 76 } 77 78 int notrace unwind_frame(struct stackframe *frame) 79 { 80 unsigned long fp = frame->fp; 81 82 if (frame_pointer_check(frame)) 83 return -EINVAL; 84 85 /* 86 * When we unwind through an exception stack, include the saved PC 87 * value into the stack trace. 88 */ 89 if (frame->ex_frame) { 90 struct pt_regs *regs = (struct pt_regs *)frame->sp; 91 92 /* 93 * We check that 'regs + sizeof(struct pt_regs)' (that is, 94 * ®s[1]) does not exceed the bottom of the stack to avoid 95 * accessing data outside the task's stack. This may happen 96 * when frame->ex_frame is a false positive. 97 */ 98 if ((unsigned long)®s[1] > ALIGN(frame->sp, THREAD_SIZE)) 99 return -EINVAL; 100 101 frame->pc = regs->ARM_pc; 102 frame->ex_frame = false; 103 return 0; 104 } 105 106 /* restore the registers from the stack frame */ 107 #ifdef CONFIG_CC_IS_CLANG 108 frame->sp = frame->fp; 109 frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp)); 110 frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 4)); 111 #else 112 frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 12)); 113 frame->sp = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 8)); 114 frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 4)); 115 #endif 116 #ifdef CONFIG_KRETPROBES 117 if (is_kretprobe_trampoline(frame->pc)) 118 frame->pc = kretprobe_find_ret_addr(frame->tsk, 119 (void *)frame->fp, &frame->kr_cur); 120 #endif 121 122 if (in_entry_text(frame->pc)) 123 frame->ex_frame = true; 124 125 return 0; 126 } 127 #endif 128 129 void notrace walk_stackframe(struct stackframe *frame, 130 bool (*fn)(void *, unsigned long), void *data) 131 { 132 while (1) { 133 int ret; 134 135 if (!fn(data, frame->pc)) 136 break; 137 ret = unwind_frame(frame); 138 if (ret < 0) 139 break; 140 } 141 } 142 EXPORT_SYMBOL(walk_stackframe); 143 144 #ifdef CONFIG_STACKTRACE 145 struct stack_trace_data { 146 struct stack_trace *trace; 147 unsigned int no_sched_functions; 148 unsigned int skip; 149 }; 150 151 static bool save_trace(void *d, unsigned long addr) 152 { 153 struct stack_trace_data *data = d; 154 struct stack_trace *trace = data->trace; 155 156 if (data->no_sched_functions && in_sched_functions(addr)) 157 return true; 158 if (data->skip) { 159 data->skip--; 160 return true; 161 } 162 163 trace->entries[trace->nr_entries++] = addr; 164 return trace->nr_entries < trace->max_entries; 165 } 166 167 /* This must be noinline to so that our skip calculation works correctly */ 168 static noinline void __save_stack_trace(struct task_struct *tsk, 169 struct stack_trace *trace, unsigned int nosched) 170 { 171 struct stack_trace_data data; 172 struct stackframe frame; 173 174 data.trace = trace; 175 data.skip = trace->skip; 176 data.no_sched_functions = nosched; 177 178 if (tsk != current) { 179 #ifdef CONFIG_SMP 180 /* 181 * What guarantees do we have here that 'tsk' is not 182 * running on another CPU? For now, ignore it as we 183 * can't guarantee we won't explode. 184 */ 185 return; 186 #else 187 frame.fp = thread_saved_fp(tsk); 188 frame.sp = thread_saved_sp(tsk); 189 frame.lr = 0; /* recovered from the stack */ 190 frame.pc = thread_saved_pc(tsk); 191 #endif 192 } else { 193 /* We don't want this function nor the caller */ 194 data.skip += 2; 195 frame.fp = (unsigned long)__builtin_frame_address(0); 196 frame.sp = current_stack_pointer; 197 frame.lr = (unsigned long)__builtin_return_address(0); 198 here: 199 frame.pc = (unsigned long)&&here; 200 } 201 #ifdef CONFIG_KRETPROBES 202 frame.kr_cur = NULL; 203 frame.tsk = tsk; 204 #endif 205 #ifdef CONFIG_UNWINDER_FRAME_POINTER 206 frame.ex_frame = false; 207 #endif 208 209 walk_stackframe(&frame, save_trace, &data); 210 } 211 212 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) 213 { 214 struct stack_trace_data data; 215 struct stackframe frame; 216 217 data.trace = trace; 218 data.skip = trace->skip; 219 data.no_sched_functions = 0; 220 221 frame.fp = regs->ARM_fp; 222 frame.sp = regs->ARM_sp; 223 frame.lr = regs->ARM_lr; 224 frame.pc = regs->ARM_pc; 225 #ifdef CONFIG_KRETPROBES 226 frame.kr_cur = NULL; 227 frame.tsk = current; 228 #endif 229 #ifdef CONFIG_UNWINDER_FRAME_POINTER 230 frame.ex_frame = in_entry_text(frame.pc); 231 #endif 232 233 walk_stackframe(&frame, save_trace, &data); 234 } 235 236 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 237 { 238 __save_stack_trace(tsk, trace, 1); 239 } 240 EXPORT_SYMBOL(save_stack_trace_tsk); 241 242 void save_stack_trace(struct stack_trace *trace) 243 { 244 __save_stack_trace(current, trace, 0); 245 } 246 EXPORT_SYMBOL_GPL(save_stack_trace); 247 #endif 248