Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: Change the on_*stack functions to take a size argument

unwind_frame() was previously implicitly checking that the frame
record is in bounds of the stack by enforcing that FP is both aligned
to 16 and in bounds of the stack. Once the FP alignment requirement
is relaxed to 8 this will not be sufficient because it does not
account for the case where FP points to 8 bytes before the end of the
stack.

Make the check explicit by changing the on_*stack functions to take a
size argument and adjusting the callers to pass the appropriate sizes.

Signed-off-by: Peter Collingbourne <pcc@google.com>
Link: https://linux-review.googlesource.com/id/Ib7a3eb3eea41b0687ffaba045ceb2012d077d8b4
Reviewed-by: Mark Rutland <mark.rutland@arm.com>
Tested-by: Mark Rutland <mark.rutland@arm.com>
Link: https://lore.kernel.org/r/20210526174927.2477847-1-pcc@google.com
Signed-off-by: Will Deacon <will@kernel.org>

authored by

Peter Collingbourne and committed by
Will Deacon
76734d26 7d7b720a

+37 -34
+6 -6
arch/arm64/include/asm/processor.h
··· 329 329 * of header definitions for the use of task_stack_page. 330 330 */ 331 331 332 - #define current_top_of_stack() \ 333 - ({ \ 334 - struct stack_info _info; \ 335 - BUG_ON(!on_accessible_stack(current, current_stack_pointer, &_info)); \ 336 - _info.high; \ 332 + #define current_top_of_stack() \ 333 + ({ \ 334 + struct stack_info _info; \ 335 + BUG_ON(!on_accessible_stack(current, current_stack_pointer, 1, &_info)); \ 336 + _info.high; \ 337 337 }) 338 - #define on_thread_stack() (on_task_stack(current, current_stack_pointer, NULL)) 338 + #define on_thread_stack() (on_task_stack(current, current_stack_pointer, 1, NULL)) 339 339 340 340 #endif /* __ASSEMBLY__ */ 341 341 #endif /* __ASM_PROCESSOR_H */
+4 -3
arch/arm64/include/asm/sdei.h
··· 42 42 43 43 struct stack_info; 44 44 45 - bool _on_sdei_stack(unsigned long sp, struct stack_info *info); 46 - static inline bool on_sdei_stack(unsigned long sp, 45 + bool _on_sdei_stack(unsigned long sp, unsigned long size, 46 + struct stack_info *info); 47 + static inline bool on_sdei_stack(unsigned long sp, unsigned long size, 47 48 struct stack_info *info) 48 49 { 49 50 if (!IS_ENABLED(CONFIG_VMAP_STACK)) ··· 52 51 if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE)) 53 52 return false; 54 53 if (in_nmi()) 55 - return _on_sdei_stack(sp, info); 54 + return _on_sdei_stack(sp, size, info); 56 55 57 56 return false; 58 57 }
+16 -16
arch/arm64/include/asm/stacktrace.h
··· 69 69 70 70 DECLARE_PER_CPU(unsigned long *, irq_stack_ptr); 71 71 72 - static inline bool on_stack(unsigned long sp, unsigned long low, 73 - unsigned long high, enum stack_type type, 74 - struct stack_info *info) 72 + static inline bool on_stack(unsigned long sp, unsigned long size, 73 + unsigned long low, unsigned long high, 74 + enum stack_type type, struct stack_info *info) 75 75 { 76 76 if (!low) 77 77 return false; 78 78 79 - if (sp < low || sp >= high) 79 + if (sp < low || sp + size < sp || sp + size > high) 80 80 return false; 81 81 82 82 if (info) { ··· 87 87 return true; 88 88 } 89 89 90 - static inline bool on_irq_stack(unsigned long sp, 90 + static inline bool on_irq_stack(unsigned long sp, unsigned long size, 91 91 struct stack_info *info) 92 92 { 93 93 unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr); 94 94 unsigned long high = low + IRQ_STACK_SIZE; 95 95 96 - return on_stack(sp, low, high, STACK_TYPE_IRQ, info); 96 + return on_stack(sp, size, low, high, STACK_TYPE_IRQ, info); 97 97 } 98 98 99 99 static inline bool on_task_stack(const struct task_struct *tsk, 100 - unsigned long sp, 100 + unsigned long sp, unsigned long size, 101 101 struct stack_info *info) 102 102 { 103 103 unsigned long low = (unsigned long)task_stack_page(tsk); 104 104 unsigned long high = low + THREAD_SIZE; 105 105 106 - return on_stack(sp, low, high, STACK_TYPE_TASK, info); 106 + return on_stack(sp, size, low, high, STACK_TYPE_TASK, info); 107 107 } 108 108 109 109 #ifdef CONFIG_VMAP_STACK 110 110 DECLARE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack); 111 111 112 - static inline bool on_overflow_stack(unsigned long sp, 112 + static inline bool on_overflow_stack(unsigned long sp, unsigned long size, 113 113 struct stack_info *info) 114 114 { 115 115 unsigned long low = (unsigned long)raw_cpu_ptr(overflow_stack); 116 116 unsigned long high = low + OVERFLOW_STACK_SIZE; 117 117 118 - return on_stack(sp, low, high, STACK_TYPE_OVERFLOW, info); 118 + return on_stack(sp, size, low, high, STACK_TYPE_OVERFLOW, info); 119 119 } 120 120 #else 121 - static inline bool on_overflow_stack(unsigned long sp, 121 + static inline bool on_overflow_stack(unsigned long sp, unsigned long size, 122 122 struct stack_info *info) { return false; } 123 123 #endif 124 124 ··· 128 128 * context. 129 129 */ 130 130 static inline bool on_accessible_stack(const struct task_struct *tsk, 131 - unsigned long sp, 131 + unsigned long sp, unsigned long size, 132 132 struct stack_info *info) 133 133 { 134 134 if (info) 135 135 info->type = STACK_TYPE_UNKNOWN; 136 136 137 - if (on_task_stack(tsk, sp, info)) 137 + if (on_task_stack(tsk, sp, size, info)) 138 138 return true; 139 139 if (tsk != current || preemptible()) 140 140 return false; 141 - if (on_irq_stack(sp, info)) 141 + if (on_irq_stack(sp, size, info)) 142 142 return true; 143 - if (on_overflow_stack(sp, info)) 143 + if (on_overflow_stack(sp, size, info)) 144 144 return true; 145 - if (on_sdei_stack(sp, info)) 145 + if (on_sdei_stack(sp, size, info)) 146 146 return true; 147 147 148 148 return false;
+1 -1
arch/arm64/kernel/ptrace.c
··· 122 122 { 123 123 return ((addr & ~(THREAD_SIZE - 1)) == 124 124 (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) || 125 - on_irq_stack(addr, NULL); 125 + on_irq_stack(addr, sizeof(unsigned long), NULL); 126 126 } 127 127 128 128 /**
+9 -7
arch/arm64/kernel/sdei.c
··· 162 162 return err; 163 163 } 164 164 165 - static bool on_sdei_normal_stack(unsigned long sp, struct stack_info *info) 165 + static bool on_sdei_normal_stack(unsigned long sp, unsigned long size, 166 + struct stack_info *info) 166 167 { 167 168 unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_normal_ptr); 168 169 unsigned long high = low + SDEI_STACK_SIZE; 169 170 170 - return on_stack(sp, low, high, STACK_TYPE_SDEI_NORMAL, info); 171 + return on_stack(sp, size, low, high, STACK_TYPE_SDEI_NORMAL, info); 171 172 } 172 173 173 - static bool on_sdei_critical_stack(unsigned long sp, struct stack_info *info) 174 + static bool on_sdei_critical_stack(unsigned long sp, unsigned long size, 175 + struct stack_info *info) 174 176 { 175 177 unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_critical_ptr); 176 178 unsigned long high = low + SDEI_STACK_SIZE; 177 179 178 - return on_stack(sp, low, high, STACK_TYPE_SDEI_CRITICAL, info); 180 + return on_stack(sp, size, low, high, STACK_TYPE_SDEI_CRITICAL, info); 179 181 } 180 182 181 - bool _on_sdei_stack(unsigned long sp, struct stack_info *info) 183 + bool _on_sdei_stack(unsigned long sp, unsigned long size, struct stack_info *info) 182 184 { 183 185 if (!IS_ENABLED(CONFIG_VMAP_STACK)) 184 186 return false; 185 187 186 - if (on_sdei_critical_stack(sp, info)) 188 + if (on_sdei_critical_stack(sp, size, info)) 187 189 return true; 188 190 189 - if (on_sdei_normal_stack(sp, info)) 191 + if (on_sdei_normal_stack(sp, size, info)) 190 192 return true; 191 193 192 194 return false;
+1 -1
arch/arm64/kernel/stacktrace.c
··· 78 78 if (fp & 0xf) 79 79 return -EINVAL; 80 80 81 - if (!on_accessible_stack(tsk, fp, &info)) 81 + if (!on_accessible_stack(tsk, fp, 16, &info)) 82 82 return -EINVAL; 83 83 84 84 if (test_bit(info.type, frame->stacks_done))