[tip:x86/pti] x86/entry/32: Add debug code to check entry/exit CR3

From: tip-bot for Joerg Roedel
Date: Thu Jul 19 2018 - 19:39:57 EST


Commit-ID: 97193702c6d353e12aefc0fb2f73a98ca421cd56
Gitweb: https://git.kernel.org/tip/97193702c6d353e12aefc0fb2f73a98ca421cd56
Author: Joerg Roedel <jroedel@xxxxxxx>
AuthorDate: Wed, 18 Jul 2018 11:41:16 +0200
Committer: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
CommitDate: Fri, 20 Jul 2018 01:11:49 +0200

x86/entry/32: Add debug code to check entry/exit CR3

Add code to check whether the kernel is entered and left with the correct
CR3 and make it depend on CONFIG_DEBUG_ENTRY. This is needed because there
is no NX protection of user-addresses in the kernel-CR3 on x86-32 and that
type of bug would not be detected otherwise.

Signed-off-by: Joerg Roedel <jroedel@xxxxxxx>
Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Tested-by: Pavel Machek <pavel@xxxxxx>
Cc: "H . Peter Anvin" <hpa@xxxxxxxxx>
Cc: linux-mm@xxxxxxxxx
Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
Cc: Andy Lutomirski <luto@xxxxxxxxxx>
Cc: Dave Hansen <dave.hansen@xxxxxxxxx>
Cc: Josh Poimboeuf <jpoimboe@xxxxxxxxxx>
Cc: Juergen Gross <jgross@xxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Borislav Petkov <bp@xxxxxxxxx>
Cc: Jiri Kosina <jkosina@xxxxxxx>
Cc: Boris Ostrovsky <boris.ostrovsky@xxxxxxxxxx>
Cc: Brian Gerst <brgerst@xxxxxxxxx>
Cc: David Laight <David.Laight@xxxxxxxxxx>
Cc: Denys Vlasenko <dvlasenk@xxxxxxxxxx>
Cc: Eduardo Valentin <eduval@xxxxxxxxxx>
Cc: Greg KH <gregkh@xxxxxxxxxxxxxxxxxxx>
Cc: Will Deacon <will.deacon@xxxxxxx>
Cc: aliguori@xxxxxxxxxx
Cc: daniel.gruss@xxxxxxxxxxxxxx
Cc: hughd@xxxxxxxxxx
Cc: keescook@xxxxxxxxxx
Cc: Andrea Arcangeli <aarcange@xxxxxxxxxx>
Cc: Waiman Long <llong@xxxxxxxxxx>
Cc: "David H . Gutteridge" <dhgutteridge@xxxxxxxxxxxx>
Cc: joro@xxxxxxxxxx
Link: https://lkml.kernel.org/r/1531906876-13451-40-git-send-email-joro@xxxxxxxxxx

---
arch/x86/entry/entry_32.S | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)

diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index b1541c74c71a..010cdb41e3c7 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -166,6 +166,24 @@
.Lend_\@:
.endm

+.macro BUG_IF_WRONG_CR3 no_user_check=0
+#ifdef CONFIG_DEBUG_ENTRY
+ ALTERNATIVE "jmp .Lend_\@", "", X86_FEATURE_PTI
+ .if \no_user_check == 0
+ /* coming from usermode? */
+ testl $SEGMENT_RPL_MASK, PT_CS(%esp)
+ jz .Lend_\@
+ .endif
+ /* On user-cr3? */
+ movl %cr3, %eax
+ testl $PTI_SWITCH_MASK, %eax
+ jnz .Lend_\@
+ /* From userspace with kernel cr3 - BUG */
+ ud2
+.Lend_\@:
+#endif
+.endm
+
/*
* Switch to kernel cr3 if not already loaded and return current cr3 in
* \scratch_reg
@@ -213,6 +231,8 @@
.macro SAVE_ALL_NMI cr3_reg:req
SAVE_ALL

+ BUG_IF_WRONG_CR3
+
/*
* Now switch the CR3 when PTI is enabled.
*
@@ -224,6 +244,7 @@

.Lend_\@:
.endm
+
/*
* This is a sneaky trick to help the unwinder find pt_regs on the stack. The
* frame pointer is replaced with an encoded pointer to pt_regs. The encoding
@@ -287,6 +308,8 @@

.Lswitched_\@:

+ BUG_IF_WRONG_CR3
+
RESTORE_REGS pop=\pop
.endm

@@ -357,6 +380,8 @@

ALTERNATIVE "", "jmp .Lend_\@", X86_FEATURE_XENPV

+ BUG_IF_WRONG_CR3
+
SWITCH_TO_KERNEL_CR3 scratch_reg=%eax

/*
@@ -799,6 +824,7 @@ ENTRY(entry_SYSENTER_32)
*/
pushfl
pushl %eax
+ BUG_IF_WRONG_CR3 no_user_check=1
SWITCH_TO_KERNEL_CR3 scratch_reg=%eax
popl %eax
popfl
@@ -893,6 +919,7 @@ ENTRY(entry_SYSENTER_32)
* whereas POPF does not.)
*/
btrl $X86_EFLAGS_IF_BIT, (%esp)
+ BUG_IF_WRONG_CR3 no_user_check=1
popfl
popl %eax

@@ -970,6 +997,8 @@ restore_all:
/* Switch back to user CR3 */
SWITCH_TO_USER_CR3 scratch_reg=%eax

+ BUG_IF_WRONG_CR3
+
/* Restore user state */
RESTORE_REGS pop=4 # skip orig_eax/error_code
.Lirq_return:
@@ -983,6 +1012,7 @@ restore_all:
restore_all_kernel:
TRACE_IRQS_IRET
PARANOID_EXIT_TO_KERNEL_MODE
+ BUG_IF_WRONG_CR3
RESTORE_REGS 4
jmp .Lirq_return

@@ -990,6 +1020,19 @@ restore_all_kernel:
ENTRY(iret_exc )
pushl $0 # no error code
pushl $do_iret_error
+
+#ifdef CONFIG_DEBUG_ENTRY
+ /*
+ * The stack-frame here is the one that iret faulted on, so its a
+ * return-to-user frame. We are on kernel-cr3 because we come here from
+ * the fixup code. This confuses the CR3 checker, so switch to user-cr3
+ * as the checker expects it.
+ */
+ pushl %eax
+ SWITCH_TO_USER_CR3 scratch_reg=%eax
+ popl %eax
+#endif
+
jmp common_exception
.previous
_ASM_EXTABLE(.Lirq_return, iret_exc)