]> git.hungrycats.org Git - linux/commitdiff
x86/nmi/64: Reorder nested NMI checks
authorAndy Lutomirski <luto@kernel.org>
Wed, 15 Jul 2015 17:29:37 +0000 (10:29 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 17 Aug 2015 03:52:15 +0000 (20:52 -0700)
commit a27507ca2d796cfa8d907de31ad730359c8a6d06 upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/x86/kernel/entry_64.S

index 0cc6af62f34dce85abc51010f88c6f2b9460cd91..3824867c89933f4166d3e444615b34e3a6be8e53 100644 (file)
@@ -1535,7 +1535,24 @@ ENTRY(nmi)
        /*
         * 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.
         */
@@ -1561,21 +1578,6 @@ ENTRY(nmi)
        CFI_REMEMBER_STATE
 
 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.