[PATCH] x86: Fixup from the removed INT3 if it is unhandled

From: Masami Hiramatsu (Google)
Date: Sun Jan 21 2024 - 03:16:50 EST


INT3 is used not only for software breakpoint, but also self modifying
code on x86 in the kernel. For example, jump_label, function tracer etc.
Those may not handle INT3 after removing it but not waiting for
synchronizing CPUs enough. Since such 'ghost' INT3 is not handled by
anyone because they think it has been removed already.
Recheck there is INT3 on the exception address and if not, ignore it.

Note that previously kprobes does the same thing by itself, but that is
not a good location to do that because INT3 is commonly used. Do it at
the common place so that it can handle all 'ghost' INT3.

Reported-by: Matthieu Baerts <matttbe@xxxxxxxxxx>
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@xxxxxxxxxx>
Fixes: 8e791f7eba4c ("x86/kprobes: Drop removed INT3 handling code")
---
arch/x86/kernel/traps.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index c876f1d36a81..f3e7a99c21fe 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -720,6 +720,25 @@ static bool do_int3(struct pt_regs *regs)
}
NOKPROBE_SYMBOL(do_int3);

+static bool fixup_int3(struct pt_regs *regs)
+{
+ unsigned long addr = instruction_pointer(regs) - INT3_INSN_SIZE;
+
+ if (*(u8 *)addr != INT3_INSN_OPCODE) {
+ /*
+ * The breakpoint instruction was removed right
+ * after we hit it. Another cpu has removed it
+ * from this address. In this case, no further
+ * handling of this interrupt is appropriate.
+ * Back up over the (now missing) int3 and run
+ * the original instruction.
+ */
+ instruction_pointer_set(regs, (unsigned long)addr);
+ return true;
+ }
+ return false;
+}
+
static void do_int3_user(struct pt_regs *regs)
{
if (do_int3(regs))
@@ -757,7 +776,7 @@ DEFINE_IDTENTRY_RAW(exc_int3)
irqentry_state_t irq_state = irqentry_nmi_enter(regs);

instrumentation_begin();
- if (!do_int3(regs))
+ if (!do_int3(regs) && !fixup_int3(regs))
die("int3", regs, 0);
instrumentation_end();
irqentry_nmi_exit(regs, irq_state);
--
2.34.1



--
Masami Hiramatsu (Google) <mhiramat@xxxxxxxxxx>