summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/unwind.h1
-rw-r--r--arch/x86/kernel/unwind_frame.c36
2 files changed, 28 insertions, 9 deletions
diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h
index 5663425f812f..9b10dcd51716 100644
--- a/arch/x86/include/asm/unwind.h
+++ b/arch/x86/include/asm/unwind.h
@@ -12,6 +12,7 @@ struct unwind_state {
struct task_struct *task;
int graph_idx;
#ifdef CONFIG_FRAME_POINTER
+ bool got_irq;
unsigned long *bp, *orig_sp;
struct pt_regs *regs;
unsigned long ip;
diff --git a/arch/x86/kernel/unwind_frame.c b/arch/x86/kernel/unwind_frame.c
index c67a059c6655..c4611359b617 100644
--- a/arch/x86/kernel/unwind_frame.c
+++ b/arch/x86/kernel/unwind_frame.c
@@ -1,6 +1,8 @@
#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
+#include <linux/interrupt.h>
+#include <asm/sections.h>
#include <asm/ptrace.h>
#include <asm/bitops.h>
#include <asm/stacktrace.h>
@@ -72,6 +74,21 @@ static size_t regs_size(struct pt_regs *regs)
return sizeof(*regs);
}
+static bool in_entry_code(unsigned long ip)
+{
+ char *addr = (char *)ip;
+
+ if (addr >= __entry_text_start && addr < __entry_text_end)
+ return true;
+
+#if defined(CONFIG_FUNCTION_GRAPH_TRACER) || defined(CONFIG_KASAN)
+ if (addr >= __irqentry_text_start && addr < __irqentry_text_end)
+ return true;
+#endif
+
+ return false;
+}
+
#ifdef CONFIG_X86_32
#define GCC_REALIGN_WORDS 3
#else
@@ -144,6 +161,7 @@ static bool update_stack_state(struct unwind_state *state,
if (regs) {
frame = (unsigned long *)regs;
len = regs_size(regs);
+ state->got_irq = true;
} else {
frame = next_bp;
len = FRAME_HEADER_SIZE;
@@ -239,16 +257,8 @@ bool unwind_next_frame(struct unwind_state *state)
next_bp = (unsigned long *)READ_ONCE_TASK_STACK(state->task, *state->bp);
/* Move to the next frame if it's safe: */
- if (!update_stack_state(state, next_bp)) {
- /*
- * Don't warn on bad regs->bp. An interrupt in entry code
- * might cause a false positive warning.
- */
- if (state->regs)
- goto the_end;
-
+ if (!update_stack_state(state, next_bp))
goto bad_address;
- }
return true;
@@ -263,6 +273,13 @@ bad_address:
if (state->task != current)
goto the_end;
+ /*
+ * Don't warn if the unwinder got lost due to an interrupt in entry
+ * code before the stack was set up:
+ */
+ if (state->got_irq && in_entry_code(state->ip))
+ goto the_end;
+
if (state->regs) {
printk_deferred_once(KERN_WARNING
"WARNING: kernel stack regs at %p in %s:%d has bad 'bp' value %p\n",
@@ -289,6 +306,7 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
memset(state, 0, sizeof(*state));
state->task = task;
+ state->got_irq = (regs);
/* Don't even attempt to start from user mode regs: */
if (regs && user_mode(regs)) {