x86/speculation/mds: Clear CPU buffers on exit to user
Add a static key which controls the invocation of the CPU buffer clear mechanism on exit to user space and add the call into prepare_exit_to_usermode() and do_nmi() right before actually returning. Add documentation which kernel to user space transition this covers and explain why some corner cases are not mitigated. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Reviewed-by: Borislav Petkov <bp@suse.de> Reviewed-by: Frederic Weisbecker <frederic@kernel.org> Reviewed-by: Jon Masters <jcm@redhat.com> Tested-by: Jon Masters <jcm@redhat.com>
This commit is contained in:
Родитель
6a9e529272
Коммит
04dcbdb805
|
@ -97,3 +97,55 @@ According to current knowledge additional mitigations inside the kernel
|
|||
itself are not required because the necessary gadgets to expose the leaked
|
||||
data cannot be controlled in a way which allows exploitation from malicious
|
||||
user space or VM guests.
|
||||
|
||||
Mitigation points
|
||||
-----------------
|
||||
|
||||
1. Return to user space
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When transitioning from kernel to user space the CPU buffers are flushed
|
||||
on affected CPUs when the mitigation is not disabled on the kernel
|
||||
command line. The migitation is enabled through the static key
|
||||
mds_user_clear.
|
||||
|
||||
The mitigation is invoked in prepare_exit_to_usermode() which covers
|
||||
most of the kernel to user space transitions. There are a few exceptions
|
||||
which are not invoking prepare_exit_to_usermode() on return to user
|
||||
space. These exceptions use the paranoid exit code.
|
||||
|
||||
- Non Maskable Interrupt (NMI):
|
||||
|
||||
Access to sensible data like keys, credentials in the NMI context is
|
||||
mostly theoretical: The CPU can do prefetching or execute a
|
||||
misspeculated code path and thereby fetching data which might end up
|
||||
leaking through a buffer.
|
||||
|
||||
But for mounting other attacks the kernel stack address of the task is
|
||||
already valuable information. So in full mitigation mode, the NMI is
|
||||
mitigated on the return from do_nmi() to provide almost complete
|
||||
coverage.
|
||||
|
||||
- Double fault (#DF):
|
||||
|
||||
A double fault is usually fatal, but the ESPFIX workaround, which can
|
||||
be triggered from user space through modify_ldt(2) is a recoverable
|
||||
double fault. #DF uses the paranoid exit path, so explicit mitigation
|
||||
in the double fault handler is required.
|
||||
|
||||
- Machine Check Exception (#MC):
|
||||
|
||||
Another corner case is a #MC which hits between the CPU buffer clear
|
||||
invocation and the actual return to user. As this still is in kernel
|
||||
space it takes the paranoid exit path which does not clear the CPU
|
||||
buffers. So the #MC handler repopulates the buffers to some
|
||||
extent. Machine checks are not reliably controllable and the window is
|
||||
extremly small so mitigation would just tick a checkbox that this
|
||||
theoretical corner case is covered. To keep the amount of special
|
||||
cases small, ignore #MC.
|
||||
|
||||
- Debug Exception (#DB):
|
||||
|
||||
This takes the paranoid exit path only when the INT1 breakpoint is in
|
||||
kernel space. #DB on a user space address takes the regular exit path,
|
||||
so no extra mitigation required.
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <asm/vdso.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/syscalls.h>
|
||||
|
@ -212,6 +213,8 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs)
|
|||
#endif
|
||||
|
||||
user_enter_irqoff();
|
||||
|
||||
mds_user_clear_cpu_buffers();
|
||||
}
|
||||
|
||||
#define SYSCALL_EXIT_WORK_FLAGS \
|
||||
|
|
|
@ -318,6 +318,8 @@ DECLARE_STATIC_KEY_FALSE(switch_to_cond_stibp);
|
|||
DECLARE_STATIC_KEY_FALSE(switch_mm_cond_ibpb);
|
||||
DECLARE_STATIC_KEY_FALSE(switch_mm_always_ibpb);
|
||||
|
||||
DECLARE_STATIC_KEY_FALSE(mds_user_clear);
|
||||
|
||||
#include <asm/segment.h>
|
||||
|
||||
/**
|
||||
|
@ -343,6 +345,17 @@ static inline void mds_clear_cpu_buffers(void)
|
|||
asm volatile("verw %[ds]" : : [ds] "m" (ds) : "cc");
|
||||
}
|
||||
|
||||
/**
|
||||
* mds_user_clear_cpu_buffers - Mitigation for MDS vulnerability
|
||||
*
|
||||
* Clear CPU buffers if the corresponding static key is enabled
|
||||
*/
|
||||
static inline void mds_user_clear_cpu_buffers(void)
|
||||
{
|
||||
if (static_branch_likely(&mds_user_clear))
|
||||
mds_clear_cpu_buffers();
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
/*
|
||||
|
|
|
@ -63,6 +63,9 @@ DEFINE_STATIC_KEY_FALSE(switch_mm_cond_ibpb);
|
|||
/* Control unconditional IBPB in switch_mm() */
|
||||
DEFINE_STATIC_KEY_FALSE(switch_mm_always_ibpb);
|
||||
|
||||
/* Control MDS CPU buffer clear before returning to user space */
|
||||
DEFINE_STATIC_KEY_FALSE(mds_user_clear);
|
||||
|
||||
void __init check_bugs(void)
|
||||
{
|
||||
identify_boot_cpu();
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <asm/x86_init.h>
|
||||
#include <asm/reboot.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/nmi.h>
|
||||
|
@ -533,6 +534,9 @@ nmi_restart:
|
|||
write_cr2(this_cpu_read(nmi_cr2));
|
||||
if (this_cpu_dec_return(nmi_state))
|
||||
goto nmi_restart;
|
||||
|
||||
if (user_mode(regs))
|
||||
mds_user_clear_cpu_buffers();
|
||||
}
|
||||
NOKPROBE_SYMBOL(do_nmi);
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include <asm/alternative.h>
|
||||
#include <asm/fpu/xstate.h>
|
||||
#include <asm/trace/mpx.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
#include <asm/mpx.h>
|
||||
#include <asm/vm86.h>
|
||||
#include <asm/umip.h>
|
||||
|
@ -366,6 +367,13 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
|
|||
regs->ip = (unsigned long)general_protection;
|
||||
regs->sp = (unsigned long)&gpregs->orig_ax;
|
||||
|
||||
/*
|
||||
* This situation can be triggered by userspace via
|
||||
* modify_ldt(2) and the return does not take the regular
|
||||
* user space exit, so a CPU buffer clear is required when
|
||||
* MDS mitigation is enabled.
|
||||
*/
|
||||
mds_user_clear_cpu_buffers();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче