x86: implement eager FP save/restore
Speculative execution side channel attacks can read the entire FPU/SIMD register state on affected Intel Core processors, see CVE-2018-3665. We now have two options for managing floating point context between threads on x86: CONFIG_EAGER_FP_SHARING and CONFIG_LAZY_FP_SHARING. The mitigation is to unconditionally save/restore these registers on context switch, instead of the lazy sharing algorithm used by CONFIG_LAZY_FP_SHARING. Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
parent
9115386e4d
commit
50d72ed9c9
11 changed files with 88 additions and 35 deletions
|
@ -112,8 +112,8 @@ config X86_NO_SPECULATIVE_VULNERABILITIES
|
||||||
select X86_NO_SPECTRE_V4
|
select X86_NO_SPECTRE_V4
|
||||||
select X86_NO_LAZY_FP
|
select X86_NO_LAZY_FP
|
||||||
help
|
help
|
||||||
This hidden option should be set on a per-SOC basis that a
|
This hidden option should be set on a per-SOC basis to indicate that
|
||||||
particular SOC does not perform any kind of speculative execution,
|
a particular SOC does not perform any kind of speculative execution,
|
||||||
or is a newer chip which is immune to the class of vulnerabilities
|
or is a newer chip which is immune to the class of vulnerabilities
|
||||||
which exploit speculative execution side channel attacks.
|
which exploit speculative execution side channel attacks.
|
||||||
|
|
||||||
|
@ -182,6 +182,37 @@ config SSE_FP_MATH
|
||||||
|
|
||||||
Disabling this option means that the compiler utilizes only the
|
Disabling this option means that the compiler utilizes only the
|
||||||
x87 instruction set for floating point operations.
|
x87 instruction set for floating point operations.
|
||||||
|
|
||||||
|
config EAGER_FP_SHARING
|
||||||
|
bool
|
||||||
|
depends on FLOAT
|
||||||
|
depends on USERSPACE
|
||||||
|
default y if !X86_NO_LAZY_FP
|
||||||
|
help
|
||||||
|
This hidden option unconditionally saves/restores the FPU/SIMD
|
||||||
|
register state on every context switch.
|
||||||
|
|
||||||
|
Mitigates CVE-2018-3665, but incurs a performance hit.
|
||||||
|
|
||||||
|
For vulnerable systems that process sensitive information in the
|
||||||
|
FPU register set, should be used any time CONFIG_FLOAT is
|
||||||
|
enabled, regardless if the FPU is used by one thread or multiple.
|
||||||
|
|
||||||
|
config LAZY_FP_SHARING
|
||||||
|
bool
|
||||||
|
depends on FLOAT
|
||||||
|
depends on !EAGER_FP_SHARING
|
||||||
|
depends on FP_SHARING
|
||||||
|
default y if X86_NO_LAZY_FP || !USERSPACE
|
||||||
|
help
|
||||||
|
This hidden option allows multiple threads to use the floating point
|
||||||
|
registers, using logic to lazily save/restore the floating point
|
||||||
|
register state on context switch.
|
||||||
|
|
||||||
|
On Intel Core procesors, may be vulnerable to exploits which allows
|
||||||
|
malware to read the contents of all floating point registers, see
|
||||||
|
CVE-2018-3665.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
choice
|
choice
|
||||||
|
|
|
@ -29,7 +29,7 @@ zephyr_library_sources(
|
||||||
zephyr_library_sources_if_kconfig( irq_offload.c)
|
zephyr_library_sources_if_kconfig( irq_offload.c)
|
||||||
zephyr_library_sources_if_kconfig( x86_mmu.c)
|
zephyr_library_sources_if_kconfig( x86_mmu.c)
|
||||||
zephyr_library_sources_if_kconfig( reboot_rst_cnt.c)
|
zephyr_library_sources_if_kconfig( reboot_rst_cnt.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_FP_SHARING float.c)
|
zephyr_library_sources_ifdef(CONFIG_LAZY_FP_SHARING float.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_X86_USERSPACE userspace.S)
|
zephyr_library_sources_ifdef(CONFIG_X86_USERSPACE userspace.S)
|
||||||
|
|
||||||
# Last since we declare default exception handlers here
|
# Last since we declare default exception handlers here
|
||||||
|
|
|
@ -125,7 +125,7 @@ SECTION_FUNC(TEXT, _exception_enter)
|
||||||
#endif
|
#endif
|
||||||
/* ESP is pointing to the ESF at this point */
|
/* ESP is pointing to the ESF at this point */
|
||||||
|
|
||||||
#if defined(CONFIG_FP_SHARING)
|
#if defined(CONFIG_LAZY_FP_SHARING)
|
||||||
|
|
||||||
movl _kernel + _kernel_offset_to_current, %edx
|
movl _kernel + _kernel_offset_to_current, %edx
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ SECTION_FUNC(TEXT, _exception_enter)
|
||||||
|
|
||||||
orb $_EXC_ACTIVE, _thread_offset_to_thread_state(%edx)
|
orb $_EXC_ACTIVE, _thread_offset_to_thread_state(%edx)
|
||||||
|
|
||||||
#endif /* CONFIG_FP_SHARING */
|
#endif /* CONFIG_LAZY_FP_SHARING */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* restore interrupt enable state, then call the handler
|
* restore interrupt enable state, then call the handler
|
||||||
|
@ -172,7 +172,7 @@ allDone:
|
||||||
addl $0x4, %esp
|
addl $0x4, %esp
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_FP_SHARING)
|
#if defined(CONFIG_LAZY_FP_SHARING)
|
||||||
|
|
||||||
movl _kernel + _kernel_offset_to_current, %ecx
|
movl _kernel + _kernel_offset_to_current, %ecx
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ allDone:
|
||||||
andb $~_EXC_ACTIVE, _thread_offset_to_thread_state(%ecx)
|
andb $~_EXC_ACTIVE, _thread_offset_to_thread_state(%ecx)
|
||||||
|
|
||||||
nestedException:
|
nestedException:
|
||||||
#endif /* CONFIG_FP_SHARING */
|
#endif /* CONFIG_LAZY_FP_SHARING */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pop the non-volatile registers from the stack.
|
* Pop the non-volatile registers from the stack.
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
* safely by one or more cooperative threads OR by a single preemptive thread,
|
* safely by one or more cooperative threads OR by a single preemptive thread,
|
||||||
* but not by both.
|
* but not by both.
|
||||||
*
|
*
|
||||||
|
* This code is not necessary for systems with CONFIG_EAGER_FP_SHARING, as
|
||||||
|
* the floating point context is unconditionally saved/restored with every
|
||||||
|
* context switch.
|
||||||
|
*
|
||||||
* The floating point register sharing mechanism is designed for minimal
|
* The floating point register sharing mechanism is designed for minimal
|
||||||
* intrusiveness. Floating point state saving is only performed for threads
|
* intrusiveness. Floating point state saving is only performed for threads
|
||||||
* that explicitly indicate they are using FPU registers, to avoid impacting
|
* that explicitly indicate they are using FPU registers, to avoid impacting
|
||||||
|
@ -43,10 +47,6 @@
|
||||||
#include <toolchain.h>
|
#include <toolchain.h>
|
||||||
#include <asm_inline.h>
|
#include <asm_inline.h>
|
||||||
|
|
||||||
/* the entire library vanishes without the FP_SHARING option enabled */
|
|
||||||
|
|
||||||
#ifdef CONFIG_FP_SHARING
|
|
||||||
|
|
||||||
/* SSE control/status register default value (used by assembler code) */
|
/* SSE control/status register default value (used by assembler code) */
|
||||||
extern u32_t _sse_mxcsr_default_value;
|
extern u32_t _sse_mxcsr_default_value;
|
||||||
|
|
||||||
|
@ -244,5 +244,3 @@ void _FpNotAvailableExcHandler(NANO_ESF *pEsf)
|
||||||
k_float_enable(_current, _FP_USER_MASK);
|
k_float_enable(_current, _FP_USER_MASK);
|
||||||
}
|
}
|
||||||
_EXCEPTION_CONNECT_NOCODE(_FpNotAvailableExcHandler, IV_DEVICE_NOT_AVAILABLE);
|
_EXCEPTION_CONNECT_NOCODE(_FpNotAvailableExcHandler, IV_DEVICE_NOT_AVAILABLE);
|
||||||
|
|
||||||
#endif /* CONFIG_FP_SHARING */
|
|
||||||
|
|
|
@ -260,7 +260,7 @@ alreadyOnIntStack:
|
||||||
* debug tools that a preemptive context switch has occurred.
|
* debug tools that a preemptive context switch has occurred.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(CONFIG_FP_SHARING)
|
#if defined(CONFIG_LAZY_FP_SHARING)
|
||||||
orb $_INT_ACTIVE, _thread_offset_to_thread_state(%edx)
|
orb $_INT_ACTIVE, _thread_offset_to_thread_state(%edx)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ alreadyOnIntStack:
|
||||||
* returning control to it at the point where it was interrupted ...
|
* returning control to it at the point where it was interrupted ...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(CONFIG_FP_SHARING)
|
#if defined(CONFIG_LAZY_FP_SHARING)
|
||||||
/*
|
/*
|
||||||
* __swap() has restored the floating point registers, if needed.
|
* __swap() has restored the floating point registers, if needed.
|
||||||
* Clear the _INT_ACTIVE bit in the interrupted thread's state
|
* Clear the _INT_ACTIVE bit in the interrupted thread's state
|
||||||
|
@ -305,7 +305,7 @@ alreadyOnIntStack:
|
||||||
|
|
||||||
movl _kernel + _kernel_offset_to_current, %eax
|
movl _kernel + _kernel_offset_to_current, %eax
|
||||||
andb $~_INT_ACTIVE, _thread_offset_to_thread_state(%eax)
|
andb $~_INT_ACTIVE, _thread_offset_to_thread_state(%eax)
|
||||||
#endif /* CONFIG_FP_SHARING */
|
#endif /* CONFIG_LAZY_FP_SHARING */
|
||||||
|
|
||||||
/* Restore volatile registers and return to the interrupted thread */
|
/* Restore volatile registers and return to the interrupted thread */
|
||||||
#ifdef CONFIG_INT_LATENCY_BENCHMARK
|
#ifdef CONFIG_INT_LATENCY_BENCHMARK
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
#include <kernel_offsets.h>
|
#include <kernel_offsets.h>
|
||||||
|
|
||||||
#if defined(CONFIG_FP_SHARING)
|
#if defined(CONFIG_LAZY_FP_SHARING)
|
||||||
GEN_OFFSET_SYM(_thread_arch_t, excNestCount);
|
GEN_OFFSET_SYM(_thread_arch_t, excNestCount);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -171,7 +171,31 @@ SECTION_FUNC(TEXT, __swap)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_FP_SHARING
|
#ifdef CONFIG_EAGER_FP_SHARING
|
||||||
|
/* Eager floating point state restore logic
|
||||||
|
*
|
||||||
|
* Addresses CVE-2018-3665
|
||||||
|
* Used as an alternate to CONFIG_LAZY_FP_SHARING if there is any
|
||||||
|
* sensitive data in the floating point/SIMD registers in a system
|
||||||
|
* with untrusted threads.
|
||||||
|
*
|
||||||
|
* Unconditionally save/restore floating point registers on context
|
||||||
|
* switch.
|
||||||
|
*/
|
||||||
|
/* Save outgpoing thread context */
|
||||||
|
#ifdef CONFIG_SSE
|
||||||
|
fxsave _thread_offset_to_preempFloatReg(%edx)
|
||||||
|
fninit
|
||||||
|
#else
|
||||||
|
fnsave _thread_offset_to_preempFloatReg(%edx)
|
||||||
|
#endif
|
||||||
|
/* Restore incoming thread context */
|
||||||
|
#ifdef CONFIG_SSE
|
||||||
|
fxrstor _thread_offset_to_preempFloatReg(%eax)
|
||||||
|
#else
|
||||||
|
frstor _thread_offset_to_preempFloatReg(%eax)
|
||||||
|
#endif /* CONFIG_SSE */
|
||||||
|
#elif defined(CONFIG_LAZY_FP_SHARING)
|
||||||
/*
|
/*
|
||||||
* Clear the CR0[TS] bit (in the event the current thread
|
* Clear the CR0[TS] bit (in the event the current thread
|
||||||
* doesn't have floating point enabled) to prevent the "device not
|
* doesn't have floating point enabled) to prevent the "device not
|
||||||
|
@ -325,7 +349,7 @@ restoreContext_NoFloatSwap:
|
||||||
|
|
||||||
CROHandlingDone:
|
CROHandlingDone:
|
||||||
|
|
||||||
#endif /* CONFIG_FP_SHARING */
|
#endif /* CONFIG_LAZY_FP_SHARING */
|
||||||
|
|
||||||
/* update _kernel.current to reflect incoming thread */
|
/* update _kernel.current to reflect incoming thread */
|
||||||
|
|
||||||
|
|
|
@ -122,9 +122,9 @@ void z_new_thread(struct k_thread *thread, k_thread_stack_t *stack,
|
||||||
*/
|
*/
|
||||||
thread->callee_saved.esp = (unsigned long)initial_frame;
|
thread->callee_saved.esp = (unsigned long)initial_frame;
|
||||||
|
|
||||||
#if defined(CONFIG_FP_SHARING)
|
#if defined(CONFIG_LAZY_FP_SHARING)
|
||||||
thread->arch.excNestCount = 0;
|
thread->arch.excNestCount = 0;
|
||||||
#endif /* CONFIG_FP_SHARING */
|
#endif /* CONFIG_LAZY_FP_SHARING */
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_X86_USERSPACE
|
#ifdef CONFIG_X86_USERSPACE
|
||||||
|
|
|
@ -40,7 +40,7 @@ static inline unsigned int EflagsGet(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_FP_SHARING
|
#ifdef CONFIG_LAZY_FP_SHARING
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -133,7 +133,7 @@ static inline void _do_sse_regs_init(void)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_SSE */
|
#endif /* CONFIG_SSE */
|
||||||
|
|
||||||
#endif /* CONFIG_FP_SHARING */
|
#endif /* CONFIG_LAZY_FP_SHARING */
|
||||||
|
|
||||||
#endif /* _ASMLANGUAGE */
|
#endif /* _ASMLANGUAGE */
|
||||||
|
|
||||||
|
|
|
@ -64,9 +64,9 @@
|
||||||
|
|
||||||
/* end - states */
|
/* end - states */
|
||||||
|
|
||||||
#if defined(CONFIG_FP_SHARING) && defined(CONFIG_SSE)
|
#if defined(CONFIG_LAZY_FP_SHARING) && defined(CONFIG_SSE)
|
||||||
#define _FP_USER_MASK (K_FP_REGS | K_SSE_REGS)
|
#define _FP_USER_MASK (K_FP_REGS | K_SSE_REGS)
|
||||||
#elif defined(CONFIG_FP_SHARING)
|
#elif defined(CONFIG_LAZY_FP_SHARING)
|
||||||
#define _FP_USER_MASK (K_FP_REGS)
|
#define _FP_USER_MASK (K_FP_REGS)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -91,14 +91,14 @@ struct _callee_saved {
|
||||||
typedef struct _callee_saved _callee_saved_t;
|
typedef struct _callee_saved _callee_saved_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The macro CONFIG_FP_SHARING shall be set to indicate that the
|
* The macros CONFIG_{LAZY|EAGER}_FP_SHARING shall be set to indicate that the
|
||||||
* saving/restoring of the traditional x87 floating point (and MMX) registers
|
* saving/restoring of the traditional x87 floating point (and MMX) registers
|
||||||
* are supported by the kernel's context swapping code. The macro
|
* are supported by the kernel's context swapping code. The macro
|
||||||
* CONFIG_SSE shall _also_ be set if saving/restoring of the XMM
|
* CONFIG_SSE shall _also_ be set if saving/restoring of the XMM
|
||||||
* registers is also supported in the kernel's context swapping code.
|
* registers is also supported in the kernel's context swapping code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef CONFIG_FP_SHARING
|
#if defined(CONFIG_EAGER_FP_SHARING) || defined(CONFIG_LAZY_FP_SHARING)
|
||||||
|
|
||||||
/* definition of a single x87 (floating point / MMX) register */
|
/* definition of a single x87 (floating point / MMX) register */
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ typedef struct s_FpRegSetEx {
|
||||||
|
|
||||||
#endif /* CONFIG_SSE == 0 */
|
#endif /* CONFIG_SSE == 0 */
|
||||||
|
|
||||||
#else /* CONFIG_FP_SHARING == 0 */
|
#else /* !CONFIG_LAZY_FP_SHARING && !CONFIG_EAGER_FP_SHARING */
|
||||||
|
|
||||||
/* empty floating point register definition */
|
/* empty floating point register definition */
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ typedef struct s_FpRegSet {
|
||||||
typedef struct s_FpRegSetEx {
|
typedef struct s_FpRegSetEx {
|
||||||
} tFpRegSetEx;
|
} tFpRegSetEx;
|
||||||
|
|
||||||
#endif /* CONFIG_FP_SHARING == 0 */
|
#endif /* CONFIG_LAZY_FP_SHARING || CONFIG_EAGER_FP_SHARING */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following structure defines the set of 'non-volatile' x87 FPU/MMX/SSE
|
* The following structure defines the set of 'non-volatile' x87 FPU/MMX/SSE
|
||||||
|
@ -241,14 +241,14 @@ typedef struct s_preempFloatReg {
|
||||||
|
|
||||||
struct _thread_arch {
|
struct _thread_arch {
|
||||||
|
|
||||||
#if defined(CONFIG_FP_SHARING)
|
#if defined(CONFIG_LAZY_FP_SHARING)
|
||||||
/*
|
/*
|
||||||
* Nested exception count to maintain setting of EXC_ACTIVE flag across
|
* Nested exception count to maintain setting of EXC_ACTIVE flag across
|
||||||
* outermost exception. EXC_ACTIVE is used by z_swap() lazy FP
|
* outermost exception. EXC_ACTIVE is used by z_swap() lazy FP
|
||||||
* save/restore and by debug tools.
|
* save/restore and by debug tools.
|
||||||
*/
|
*/
|
||||||
unsigned excNestCount; /* nested exception count */
|
unsigned excNestCount; /* nested exception count */
|
||||||
#endif /* CONFIG_FP_SHARING */
|
#endif /* CONFIG_LAZY_FP_SHARING */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The location of all floating point related structures/fields MUST be
|
* The location of all floating point related structures/fields MUST be
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue