#ifndef __ASSEMBLER__
#include <linux/percpu.h>
+#include <linux/sched/task_stack.h>
#include <asm-generic/irq.h>
#include <asm/thread_info.h>
static inline bool on_irq_stack(unsigned long sp)
{
- /* variable names the same as kernel/stacktrace.c */
unsigned long low = (unsigned long)raw_cpu_ptr(irq_stack);
unsigned long high = low + IRQ_STACK_START_SP;
return (low <= sp && sp <= high);
}
+static inline bool on_task_stack(struct task_struct *tsk, unsigned long sp)
+{
+ unsigned long low = (unsigned long)task_stack_page(tsk);
+ unsigned long high = low + THREAD_SIZE;
+
+ return (low <= sp && sp < high);
+}
+
#endif /* !__ASSEMBLER__ */
#endif
*/
int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
{
- unsigned long high, low;
unsigned long fp = frame->fp;
- unsigned long irq_stack_ptr;
+
+ if (fp & 0xf)
+ return -EINVAL;
if (!tsk)
tsk = current;
* Switching between stacks is valid when tracing current and in
* non-preemptible context.
*/
- if (tsk == current && !preemptible())
- irq_stack_ptr = IRQ_STACK_PTR();
- else
- irq_stack_ptr = 0;
-
- low = frame->sp;
- /* irq stacks are not THREAD_SIZE aligned */
- if (on_irq_stack(frame->sp))
- high = irq_stack_ptr;
- else
- high = ALIGN(low, THREAD_SIZE) - 0x20;
-
- if (fp < low || fp > high || fp & 0xf)
+ if (!(tsk == current && !preemptible() && on_irq_stack(fp)) &&
+ !on_task_stack(tsk, fp))
return -EINVAL;
frame->sp = fp + 0x10;
* Check the frame->fp we read from the bottom of the irq_stack,
* and the original task stack pointer are both in current->stack.
*/
- if (frame->sp == irq_stack_ptr) {
+ if (frame->sp == IRQ_STACK_PTR()) {
struct pt_regs *irq_args;
- unsigned long orig_sp = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr);
+ unsigned long orig_sp = IRQ_STACK_TO_TASK_STACK(frame->sp);
if (object_is_on_stack((void *)orig_sp) &&
object_is_on_stack((void *)frame->fp)) {