x86/entry/32: Fix FIXUP_ESPFIX_STACK with user CR3
authorAndy Lutomirski <luto@kernel.org>
Sun, 24 Nov 2019 16:50:03 +0000 (08:50 -0800)
committerIngo Molnar <mingo@kernel.org>
Mon, 25 Nov 2019 08:36:47 +0000 (09:36 +0100)
UNWIND_ESPFIX_STACK needs to read the GDT, and the GDT mapping that
can be accessed via %fs is not mapped in the user pagetables.  Use
SGDT to find the cpu_entry_area mapping and read the espfix offset
from that instead.

Reported-and-tested-by: Borislav Petkov <bp@alien8.de>
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/entry/entry_32.S

index 0b8c93136650502647ea9b98ad4f0d2416c845ae..f07baf0388bcecd7ed84e92dba47d261228c952c 100644 (file)
 
 .macro CHECK_AND_APPLY_ESPFIX
 #ifdef CONFIG_X86_ESPFIX32
-#define GDT_ESPFIX_SS PER_CPU_VAR(gdt_page) + (GDT_ENTRY_ESPFIX_SS * 8)
+#define GDT_ESPFIX_OFFSET (GDT_ENTRY_ESPFIX_SS * 8)
+#define GDT_ESPFIX_SS PER_CPU_VAR(gdt_page) + GDT_ESPFIX_OFFSET
 
        ALTERNATIVE     "jmp .Lend_\@", "", X86_BUG_ESPFIX
 
@@ -1147,12 +1148,26 @@ ENDPROC(entry_INT80_32)
  * We can't call C functions using the ESPFIX stack. This code reads
  * the high word of the segment base from the GDT and swiches to the
  * normal stack and adjusts ESP with the matching offset.
+ *
+ * We might be on user CR3 here, so percpu data is not mapped and we can't
+ * access the GDT through the percpu segment.  Instead, use SGDT to find
+ * the cpu_entry_area alias of the GDT.
  */
 #ifdef CONFIG_X86_ESPFIX32
        /* fixup the stack */
-       mov     GDT_ESPFIX_SS + 4, %al /* bits 16..23 */
-       mov     GDT_ESPFIX_SS + 7, %ah /* bits 24..31 */
+       pushl   %ecx
+       subl    $2*4, %esp
+       sgdt    (%esp)
+       movl    2(%esp), %ecx                           /* GDT address */
+       /*
+        * Careful: ECX is a linear pointer, so we need to force base
+        * zero.  %cs is the only known-linear segment we have right now.
+        */
+       mov     %cs:GDT_ESPFIX_OFFSET + 4(%ecx), %al    /* bits 16..23 */
+       mov     %cs:GDT_ESPFIX_OFFSET + 7(%ecx), %ah    /* bits 24..31 */
        shl     $16, %eax
+       addl    $2*4, %esp
+       popl    %ecx
        addl    %esp, %eax                      /* the adjusted stack pointer */
        pushl   $__KERNEL_DS
        pushl   %eax