[PATCH v2 2/2] arm: traps: add irq_stack checking for underflow detection
From: Maninder Singh
Date: Mon Apr 27 2026 - 01:52:41 EST
with IRQSTACKS fp can point outside of thread's stack.
And when there is crash reported from IRQ side, it always report
"frame pointer underflow", which is not real underflow.
Call trace: frame pointer underflow
handle_irq_desc from generic_handle_domain_irq+0x18/0x1c
generic_handle_domain_irq from gic_handle_irq+0x78/0x8c
gic_handle_irq from generic_handle_arch_irq+0x3c/0x4c
..
..
Adding fp checking for IRQSTACK also to avoid this false
reporting. Although it is valid only in case of current as of now,
but added generic code to make it look simpler and for future usable.
Signed-off-by: Maninder Singh <maninder1.s@xxxxxxxxxxx>
---
https://lkml.org/lkml/2026/4/24/230
v1 -> v2: rather than removing fp underflow checking, added IRQSTACK checking
arch/arm/include/asm/irq.h | 13 +++++++++++++
arch/arm/kernel/traps.c | 15 ++++++++++++++-
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h
index 0d1f3d2404df..41861c8c2396 100644
--- a/arch/arm/include/asm/irq.h
+++ b/arch/arm/include/asm/irq.h
@@ -43,6 +43,19 @@ static inline int nr_legacy_irqs(void)
#ifdef CONFIG_IRQSTACKS
DECLARE_PER_CPU_READ_MOSTLY(u8 *, irq_stack_ptr);
+
+static inline bool on_irq_stack(int cpu, unsigned long sp)
+{
+ unsigned long high = (unsigned long)per_cpu(irq_stack_ptr, cpu);
+ unsigned long low = high - THREAD_SIZE;
+
+ return (low <= sp && sp <= high);
+}
+#else
+static inline bool on_irq_stack(int cpu, unsigned long sp)
+{
+ return false;
+}
#endif
#endif
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index dd0f31941b04..41d2812136ad 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -130,6 +130,19 @@ static int verify_stack(unsigned long sp)
return 0;
}
+
+static bool check_underflow(unsigned long fp, struct task_struct *tsk)
+{
+ int cpu = task_cpu(tsk);
+
+ if (on_irq_stack(cpu, fp))
+ return false;
+
+ if (fp < (unsigned long)end_of_stack(tsk))
+ return true;
+
+ return false;
+}
#endif
/*
@@ -243,7 +256,7 @@ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
} else if (verify_stack(fp)) {
pr_cont("invalid frame pointer 0x%08x", fp);
ok = 0;
- } else if (fp < (unsigned long)end_of_stack(tsk))
+ } else if (check_underflow(fp, tsk))
pr_cont("frame pointer underflow");
pr_cont("\n");
--
2.34.1