x86/nmi: Prevent instruction breakpoints in NMIs
Now that hw_breakpoint tracks kernel instruction breakpoints for us,
this is easy and will have very little performance impact if there
are no kernel instruction breakpoints.
Signed-off-by: Andy Lutomirski <luto@kernel.org>
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index 18bc9b5..bd7da73 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -492,6 +492,9 @@
dotraplinkage notrace void
do_nmi(struct pt_regs *regs, long error_code)
{
+ bool restore_dr7 = false;
+ unsigned long old_dr7;
+
if (this_cpu_read(nmi_state) != NMI_NOT_RUNNING) {
this_cpu_write(nmi_state, NMI_LATCHED);
return;
@@ -500,6 +503,18 @@
this_cpu_write(nmi_cr2, read_cr2());
nmi_restart:
+ /*
+ * Prevent instruction breakpoints from tripping inside NMI code
+ * that isn't protected by NOKPROBE_SYMBOL. We need this
+ * because we cannot return from an instruction breakpoint
+ * without using IRET.
+ */
+ if (this_cpu_read(cpu_nmi_kernel_insn_breakpoint_mask)) {
+ get_debugreg(old_dr7, 7);
+ set_debugreg(0, 7);
+ restore_dr7 = true;
+ }
+
#ifdef CONFIG_X86_64
/*
* If we interrupted a breakpoint, it is possible that
@@ -529,6 +544,9 @@
}
#endif
+ if (restore_dr7)
+ set_debugreg(old_dr7, 7);
+
if (unlikely(this_cpu_read(nmi_cr2) != read_cr2()))
write_cr2(this_cpu_read(nmi_cr2));
if (this_cpu_dec_return(nmi_state))