Commit 3c750172 authored by Pawan Gupta's avatar Pawan Gupta Committed by Dave Hansen

x86/entry_64: Add VERW just before userspace transition

Mitigation for MDS is to use VERW instruction to clear any secrets in
CPU Buffers. Any memory accesses after VERW execution can still remain
in CPU buffers. It is safer to execute VERW late in return to user path
to minimize the window in which kernel data can end up in CPU buffers.
There are not many kernel secrets to be had after SWITCH_TO_USER_CR3.

Add support for deploying VERW mitigation after user register state is
restored. This helps minimize the chances of kernel data ending up into
CPU buffers after executing VERW.

Note that the mitigation at the new location is not yet enabled.

  Corner case not handled
  =======================
  Interrupts returning to kernel don't clear CPUs buffers since the
  exit-to-user path is expected to do that anyways. But, there could be
  a case when an NMI is generated in kernel after the exit-to-user path
  has cleared the buffers. This case is not handled and NMI returning to
  kernel don't clear CPU buffers because:

  1. It is rare to get an NMI after VERW, but before returning to userspace.
  2. For an unprivileged user, there is no known way to make that NMI
     less rare or target it.
  3. It would take a large number of these precisely-timed NMIs to mount
     an actual attack.  There's presumably not enough bandwidth.
  4. The NMI in question occurs after a VERW, i.e. when user state is
     restored and most interesting data is already scrubbed. Whats left
     is only the data that NMI touches, and that may or may not be of
     any interest.
Suggested-by: default avatarDave Hansen <dave.hansen@intel.com>
Signed-off-by: default avatarPawan Gupta <pawan.kumar.gupta@linux.intel.com>
Signed-off-by: default avatarDave Hansen <dave.hansen@linux.intel.com>
Link: https://lore.kernel.org/all/20240213-delay-verw-v8-2-a6216d83edb7%40linux.intel.com
parent baf8361e
...@@ -161,6 +161,7 @@ syscall_return_via_sysret: ...@@ -161,6 +161,7 @@ syscall_return_via_sysret:
SYM_INNER_LABEL(entry_SYSRETQ_unsafe_stack, SYM_L_GLOBAL) SYM_INNER_LABEL(entry_SYSRETQ_unsafe_stack, SYM_L_GLOBAL)
ANNOTATE_NOENDBR ANNOTATE_NOENDBR
swapgs swapgs
CLEAR_CPU_BUFFERS
sysretq sysretq
SYM_INNER_LABEL(entry_SYSRETQ_end, SYM_L_GLOBAL) SYM_INNER_LABEL(entry_SYSRETQ_end, SYM_L_GLOBAL)
ANNOTATE_NOENDBR ANNOTATE_NOENDBR
...@@ -573,6 +574,7 @@ SYM_INNER_LABEL(swapgs_restore_regs_and_return_to_usermode, SYM_L_GLOBAL) ...@@ -573,6 +574,7 @@ SYM_INNER_LABEL(swapgs_restore_regs_and_return_to_usermode, SYM_L_GLOBAL)
.Lswapgs_and_iret: .Lswapgs_and_iret:
swapgs swapgs
CLEAR_CPU_BUFFERS
/* Assert that the IRET frame indicates user mode. */ /* Assert that the IRET frame indicates user mode. */
testb $3, 8(%rsp) testb $3, 8(%rsp)
jnz .Lnative_iret jnz .Lnative_iret
...@@ -723,6 +725,8 @@ native_irq_return_ldt: ...@@ -723,6 +725,8 @@ native_irq_return_ldt:
*/ */
popq %rax /* Restore user RAX */ popq %rax /* Restore user RAX */
CLEAR_CPU_BUFFERS
/* /*
* RSP now points to an ordinary IRET frame, except that the page * RSP now points to an ordinary IRET frame, except that the page
* is read-only and RSP[31:16] are preloaded with the userspace * is read-only and RSP[31:16] are preloaded with the userspace
...@@ -1449,6 +1453,12 @@ nmi_restore: ...@@ -1449,6 +1453,12 @@ nmi_restore:
std std
movq $0, 5*8(%rsp) /* clear "NMI executing" */ movq $0, 5*8(%rsp) /* clear "NMI executing" */
/*
* Skip CLEAR_CPU_BUFFERS here, since it only helps in rare cases like
* NMI in kernel after user state is restored. For an unprivileged user
* these conditions are hard to meet.
*/
/* /*
* iretq reads the "iret" frame and exits the NMI stack in a * iretq reads the "iret" frame and exits the NMI stack in a
* single instruction. We are returning to kernel mode, so this * single instruction. We are returning to kernel mode, so this
...@@ -1466,6 +1476,7 @@ SYM_CODE_START(entry_SYSCALL32_ignore) ...@@ -1466,6 +1476,7 @@ SYM_CODE_START(entry_SYSCALL32_ignore)
UNWIND_HINT_END_OF_STACK UNWIND_HINT_END_OF_STACK
ENDBR ENDBR
mov $-ENOSYS, %eax mov $-ENOSYS, %eax
CLEAR_CPU_BUFFERS
sysretl sysretl
SYM_CODE_END(entry_SYSCALL32_ignore) SYM_CODE_END(entry_SYSCALL32_ignore)
......
...@@ -270,6 +270,7 @@ SYM_INNER_LABEL(entry_SYSRETL_compat_unsafe_stack, SYM_L_GLOBAL) ...@@ -270,6 +270,7 @@ SYM_INNER_LABEL(entry_SYSRETL_compat_unsafe_stack, SYM_L_GLOBAL)
xorl %r9d, %r9d xorl %r9d, %r9d
xorl %r10d, %r10d xorl %r10d, %r10d
swapgs swapgs
CLEAR_CPU_BUFFERS
sysretl sysretl
SYM_INNER_LABEL(entry_SYSRETL_compat_end, SYM_L_GLOBAL) SYM_INNER_LABEL(entry_SYSRETL_compat_end, SYM_L_GLOBAL)
ANNOTATE_NOENDBR ANNOTATE_NOENDBR
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment