2015-07-22 20:46:22 +00:00
|
|
|
From: Andy Lutomirski <luto@kernel.org>
|
|
|
|
Date: Wed, 15 Jul 2015 10:29:37 -0700
|
|
|
|
Subject: [8/9] x86/nmi/64: Reorder nested NMI checks
|
|
|
|
Origin: https://git.kernel.org/linus/a27507ca2d796cfa8d907de31ad730359c8a6d06
|
|
|
|
|
|
|
|
Check the repeat_nmi .. end_repeat_nmi special case first. The
|
|
|
|
next patch will rework the RSP check and, as a side effect, the
|
|
|
|
RSP check will no longer detect repeat_nmi .. end_repeat_nmi, so
|
|
|
|
we'll need this ordering of the checks.
|
|
|
|
|
|
|
|
Note: this is more subtle than it appears. The check for
|
|
|
|
repeat_nmi .. end_repeat_nmi jumps straight out of the NMI code
|
|
|
|
instead of adjusting the "iret" frame to force a repeat. This
|
|
|
|
is necessary, because the code between repeat_nmi and
|
|
|
|
end_repeat_nmi sets "NMI executing" and then writes to the
|
|
|
|
"iret" frame itself. If a nested NMI comes in and modifies the
|
|
|
|
"iret" frame while repeat_nmi is also modifying it, we'll end up
|
|
|
|
with garbage. The old code got this right, as does the new
|
|
|
|
code, but the new code is a bit more explicit.
|
|
|
|
|
|
|
|
If we were to move the check right after the "NMI executing"
|
|
|
|
check, then we'd get it wrong and have random crashes.
|
|
|
|
|
|
|
|
( Because the "NMI executing" check would jump to the code that would
|
|
|
|
modify the "iret" frame without checking if the interrupted NMI was
|
|
|
|
currently modifying it. )
|
|
|
|
|
|
|
|
Signed-off-by: Andy Lutomirski <luto@kernel.org>
|
|
|
|
Reviewed-by: Steven Rostedt <rostedt@goodmis.org>
|
|
|
|
Cc: Borislav Petkov <bp@suse.de>
|
|
|
|
Cc: Linus Torvalds <torvalds@linux-foundation.org>
|
|
|
|
Cc: Peter Zijlstra <peterz@infradead.org>
|
|
|
|
Cc: Thomas Gleixner <tglx@linutronix.de>
|
|
|
|
Cc: stable@vger.kernel.org
|
|
|
|
Signed-off-by: Ingo Molnar <mingo@kernel.org>
|
2015-07-23 01:24:14 +00:00
|
|
|
[bwh: Backported to 4.1: adjust filename, spacing]
|
2015-07-22 20:46:22 +00:00
|
|
|
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
|
|
|
|
---
|
|
|
|
arch/x86/kernel/entry_64.S | 34 ++++++++++++++++++----------------
|
|
|
|
1 file changed, 18 insertions(+), 16 deletions(-)
|
|
|
|
|
|
|
|
--- a/arch/x86/kernel/entry_64.S
|
|
|
|
+++ b/arch/x86/kernel/entry_64.S
|
2015-07-23 01:24:14 +00:00
|
|
|
@@ -1554,7 +1554,24 @@ ENTRY(nmi)
|
2015-07-22 20:46:22 +00:00
|
|
|
/*
|
|
|
|
* Determine whether we're a nested NMI.
|
|
|
|
*
|
|
|
|
- * First check "NMI executing". If it's set, then we're nested.
|
|
|
|
+ * If we interrupted kernel code between repeat_nmi and
|
|
|
|
+ * end_repeat_nmi, then we are a nested NMI. We must not
|
|
|
|
+ * modify the "iret" frame because it's being written by
|
|
|
|
+ * the outer NMI. That's okay; the outer NMI handler is
|
|
|
|
+ * about to about to call do_nmi anyway, so we can just
|
|
|
|
+ * resume the outer NMI.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ movq $repeat_nmi, %rdx
|
|
|
|
+ cmpq 8(%rsp), %rdx
|
|
|
|
+ ja 1f
|
|
|
|
+ movq $end_repeat_nmi, %rdx
|
|
|
|
+ cmpq 8(%rsp), %rdx
|
|
|
|
+ ja nested_nmi_out
|
|
|
|
+1:
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Now check "NMI executing". If it's set, then we're nested.
|
|
|
|
* This will not detect if we interrupted an outer NMI just
|
|
|
|
* before IRET.
|
|
|
|
*/
|
2015-07-23 01:24:14 +00:00
|
|
|
@@ -1581,21 +1598,6 @@ ENTRY(nmi)
|
2015-07-22 20:46:22 +00:00
|
|
|
|
|
|
|
nested_nmi:
|
|
|
|
/*
|
|
|
|
- * If we interrupted an NMI that is between repeat_nmi and
|
|
|
|
- * end_repeat_nmi, then we must not modify the "iret" frame
|
|
|
|
- * because it's being written by the outer NMI. That's okay;
|
|
|
|
- * the outer NMI handler is about to call do_nmi anyway,
|
|
|
|
- * so we can just resume the outer NMI.
|
|
|
|
- */
|
|
|
|
- movq $repeat_nmi, %rdx
|
|
|
|
- cmpq 8(%rsp), %rdx
|
|
|
|
- ja 1f
|
|
|
|
- movq $end_repeat_nmi, %rdx
|
|
|
|
- cmpq 8(%rsp), %rdx
|
|
|
|
- ja nested_nmi_out
|
|
|
|
-
|
|
|
|
-1:
|
|
|
|
- /*
|
|
|
|
* Modify the "iret" frame to point to repeat_nmi, forcing another
|
|
|
|
* iteration of NMI handling.
|
|
|
|
*/
|