aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2015-09-20 19:32:04 -0400
committerThomas Gleixner <tglx@linutronix.de>2015-09-22 16:40:28 -0400
commitfc57a7c68020dcf954428869eafd934c0ab1536f (patch)
tree8d558a7d8c7f7872ea34759795d03235a3b12f35
parent1f93e4a96c9109378204c147b3eec0d0e8100fde (diff)
x86/paravirt: Replace the paravirt nop with a bona fide empty function
PARAVIRT_ADJUST_EXCEPTION_FRAME generates this code (using nmi as an example, trimmed for readability): ff 15 00 00 00 00 callq *0x0(%rip) # 2796 <nmi+0x6> 2792: R_X86_64_PC32 pv_irq_ops+0x2c That's a call through a function pointer to regular C function that does nothing on native boots, but that function isn't protected against kprobes, isn't marked notrace, and is certainly not guaranteed to preserve any registers if the compiler is feeling perverse. This is bad news for a CLBR_NONE operation. Of course, if everything works correctly, once paravirt ops are patched, it gets nopped out, but what if we hit this code before paravirt ops are patched in? This can potentially cause breakage that is very difficult to debug. A more subtle failure is possible here, too: if _paravirt_nop uses the stack at all (even just to push RBP), it will overwrite the "NMI executing" variable if it's called in the NMI prologue. The Xen case, perhaps surprisingly, is fine, because it's already written in asm. Fix all of the cases that default to paravirt_nop (including adjust_exception_frame) with a big hammer: replace paravirt_nop with an asm function that is just a ret instruction. The Xen case may have other problems, so document them. This is part of a fix for some random crashes that Sasha saw. Reported-and-tested-by: Sasha Levin <sasha.levin@oracle.com> Signed-off-by: Andy Lutomirski <luto@kernel.org> Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/8f5d2ba295f9d73751c33d97fda03e0495d9ade0.1442791737.git.luto@kernel.org Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--arch/x86/entry/entry_64.S11
-rw-r--r--arch/x86/kernel/paravirt.c16
2 files changed, 23 insertions, 4 deletions
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index d3033183ed70..404ca97c4715 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -1128,7 +1128,18 @@ END(error_exit)
1128 1128
1129/* Runs on exception stack */ 1129/* Runs on exception stack */
1130ENTRY(nmi) 1130ENTRY(nmi)
1131 /*
1132 * Fix up the exception frame if we're on Xen.
1133 * PARAVIRT_ADJUST_EXCEPTION_FRAME is guaranteed to push at most
1134 * one value to the stack on native, so it may clobber the rdx
1135 * scratch slot, but it won't clobber any of the important
1136 * slots past it.
1137 *
1138 * Xen is a different story, because the Xen frame itself overlaps
1139 * the "NMI executing" variable.
1140 */
1131 PARAVIRT_ADJUST_EXCEPTION_FRAME 1141 PARAVIRT_ADJUST_EXCEPTION_FRAME
1142
1132 /* 1143 /*
1133 * We allow breakpoints in NMIs. If a breakpoint occurs, then 1144 * We allow breakpoints in NMIs. If a breakpoint occurs, then
1134 * the iretq it performs will take us out of NMI context. 1145 * the iretq it performs will take us out of NMI context.
diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c
index f68e48f5f6c2..c2130aef3f9d 100644
--- a/arch/x86/kernel/paravirt.c
+++ b/arch/x86/kernel/paravirt.c
@@ -41,10 +41,18 @@
41#include <asm/timer.h> 41#include <asm/timer.h>
42#include <asm/special_insns.h> 42#include <asm/special_insns.h>
43 43
44/* nop stub */ 44/*
45void _paravirt_nop(void) 45 * nop stub, which must not clobber anything *including the stack* to
46{ 46 * avoid confusing the entry prologues.
47} 47 */
48extern void _paravirt_nop(void);
49asm (".pushsection .entry.text, \"ax\"\n"
50 ".global _paravirt_nop\n"
51 "_paravirt_nop:\n\t"
52 "ret\n\t"
53 ".size _paravirt_nop, . - _paravirt_nop\n\t"
54 ".type _paravirt_nop, @function\n\t"
55 ".popsection");
48 56
49/* identity function, which can be inlined */ 57/* identity function, which can be inlined */
50u32 _paravirt_ident_32(u32 x) 58u32 _paravirt_ident_32(u32 x)