aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2015-07-15 13:29:38 -0400
committerIngo Molnar <mingo@kernel.org>2015-07-17 06:50:12 -0400
commit810bc075f78ff2c221536eb3008eac6a492dba2d (patch)
treed558081a62bb49f80e733c755201fe8cbc4cfe39
parenta27507ca2d796cfa8d907de31ad730359c8a6d06 (diff)
x86/nmi/64: Use DF to avoid userspace RSP confusing nested NMI detection
We have a tricky bug in the nested NMI code: if we see RSP pointing to the NMI stack on NMI entry from kernel mode, we assume that we are executing a nested NMI. This isn't quite true. A malicious userspace program can point RSP at the NMI stack, issue SYSCALL, and arrange for an NMI to happen while RSP is still pointing at the NMI stack. Fix it with a sneaky trick. Set DF in the region of code that the RSP check is intended to detect. IRET will clear DF atomically. ( Note: other than paravirt, there's little need for all this complexity. We could check RIP instead of RSP. ) 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>
-rw-r--r--arch/x86/entry/entry_64.S29
1 files changed, 25 insertions, 4 deletions
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index 5c4ab384b84f..d8ab2b201fa1 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -1388,7 +1388,14 @@ ENTRY(nmi)
1388 /* 1388 /*
1389 * Now test if the previous stack was an NMI stack. This covers 1389 * Now test if the previous stack was an NMI stack. This covers
1390 * the case where we interrupt an outer NMI after it clears 1390 * the case where we interrupt an outer NMI after it clears
1391 * "NMI executing" but before IRET. 1391 * "NMI executing" but before IRET. We need to be careful, though:
1392 * there is one case in which RSP could point to the NMI stack
1393 * despite there being no NMI active: naughty userspace controls
1394 * RSP at the very beginning of the SYSCALL targets. We can
1395 * pull a fast one on naughty userspace, though: we program
1396 * SYSCALL to mask DF, so userspace cannot cause DF to be set
1397 * if it controls the kernel's RSP. We set DF before we clear
1398 * "NMI executing".
1392 */ 1399 */
1393 lea 6*8(%rsp), %rdx 1400 lea 6*8(%rsp), %rdx
1394 /* Compare the NMI stack (rdx) with the stack we came from (4*8(%rsp)) */ 1401 /* Compare the NMI stack (rdx) with the stack we came from (4*8(%rsp)) */
@@ -1400,7 +1407,13 @@ ENTRY(nmi)
1400 cmpq %rdx, 4*8(%rsp) 1407 cmpq %rdx, 4*8(%rsp)
1401 /* If it is below the NMI stack, it is a normal NMI */ 1408 /* If it is below the NMI stack, it is a normal NMI */
1402 jb first_nmi 1409 jb first_nmi
1403 /* Ah, it is within the NMI stack, treat it as nested */ 1410
1411 /* Ah, it is within the NMI stack. */
1412
1413 testb $(X86_EFLAGS_DF >> 8), (3*8 + 1)(%rsp)
1414 jz first_nmi /* RSP was user controlled. */
1415
1416 /* This is a nested NMI. */
1404 1417
1405nested_nmi: 1418nested_nmi:
1406 /* 1419 /*
@@ -1506,8 +1519,16 @@ nmi_restore:
1506 /* Point RSP at the "iret" frame. */ 1519 /* Point RSP at the "iret" frame. */
1507 REMOVE_PT_GPREGS_FROM_STACK 6*8 1520 REMOVE_PT_GPREGS_FROM_STACK 6*8
1508 1521
1509 /* Clear "NMI executing". */ 1522 /*
1510 movq $0, 5*8(%rsp) 1523 * Clear "NMI executing". Set DF first so that we can easily
1524 * distinguish the remaining code between here and IRET from
1525 * the SYSCALL entry and exit paths. On a native kernel, we
1526 * could just inspect RIP, but, on paravirt kernels,
1527 * INTERRUPT_RETURN can translate into a jump into a
1528 * hypercall page.
1529 */
1530 std
1531 movq $0, 5*8(%rsp) /* clear "NMI executing" */
1511 1532
1512 /* 1533 /*
1513 * INTERRUPT_RETURN reads the "iret" frame and exits the NMI 1534 * INTERRUPT_RETURN reads the "iret" frame and exits the NMI