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 static void start_stack_trace(struct stackframe *frame, struct task_struct *task, 146 unsigned long fp, unsigned long sp, 147 unsigned long lr, unsigned long pc) 148 { 149 frame->fp = fp; 150 frame->sp = sp; 151 frame->lr = lr; 152 frame->pc = pc; 153 #ifdef CONFIG_KRETPROBES 154 frame->kr_cur = NULL; 155 frame->tsk = task; 156 #endif 157 #ifdef CONFIG_UNWINDER_FRAME_POINTER 158 frame->ex_frame = in_entry_text(frame->pc); 159 #endif 160 } 161 162 void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, 163 struct task_struct *task, struct pt_regs *regs) 164 { 165 struct stackframe frame; 166 167 if (regs) { 168 start_stack_trace(&frame, NULL, regs->ARM_fp, regs->ARM_sp, 169 regs->ARM_lr, regs->ARM_pc); 170 } else if (task != current) { 171 #ifdef CONFIG_SMP 172 /* 173 * What guarantees do we have here that 'tsk' is not 174 * running on another CPU? For now, ignore it as we 175 * can't guarantee we won't explode. 176 */ 177 return; 178 #else 179 start_stack_trace(&frame, task, thread_saved_fp(task), 180 thread_saved_sp(task), 0, 181 thread_saved_pc(task)); 182 #endif 183 } else { 184 here: 185 start_stack_trace(&frame, task, 186 (unsigned long)__builtin_frame_address(0), 187 current_stack_pointer, 188 (unsigned long)__builtin_return_address(0), 189 (unsigned long)&&here); 190 /* skip this function */ 191 if (unwind_frame(&frame)) 192 return; 193 } 194 195 walk_stackframe(&frame, consume_entry, cookie); 196 } 197 #endif 198
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.