[PATCH] i386/x86-64: Fix x87 information leak between processes
Linux Kernel Mailing List <[email protected]> Wed, 26 Apr 2006 23:59:20 GMT
| Newsgroups | gmane.linux.kernel.commits.2-4 |
|---|---|
| Message-ID | <[email protected]> |
commit d296e6191afbfc63077da02a1386bcd73bd4c1e0 tree 9c6968091a4104fb8c9fd2abb6af043fefbba746 parent 0dba0f6b382bf360a1974fd78538273478dfc784 author Andi Kleen <[email protected]> Wed, 19 Apr 2006 10:22:07 +0200 committer Marcelo Tosatti <[email protected]> Thu, 27 Apr 2006 02:48:18 -0300 [PATCH] i386/x86-64: Fix x87 information leak between processes AMD K7/K8 CPUs only save/restore the FOP/FIP/FDP x87 registers in FXSAVE when an exception is pending. This means the value leak through context switches and allow processes to observe some x87 instruction state of other processes. This was actually documented by AMD, but nobody recognized it as being different from Intel before. The fix first adds an optimization: instead of unconditionally calling FNCLEX after each FXSAVE test if ES is pending and skip it when not needed. Then do a dummy x87 load to clear FOP/FIP/FDP. This means other processes always will only see a constant value defined by the kernel. Then it does a ffree st(7) ; fild <l1 address> This is executed unconditionally on FXSAVE capable systems, but has been benchmarked on Intel systems to be reasonably fast. I also had to move unlazy_fpu for 64bit to make sure the code always executes with the data segment of the new process to prevent leaking the old one. Patch for both i386/x86-64. The problem was discovered originally by Jan Beulich. Richard Brunner provided the basic code for the workarounds with contributions from Jan. This is CVE-2006-1056 Signed-off-by: Andi Kleen <[email protected]> arch/i386/kernel/i387.c | 7 ++++++- arch/x86_64/kernel/process.c | 7 +++++-- include/asm-x86_64/i387.h | 6 +++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/arch/i386/kernel/i387.c b/arch/i386/kernel/i387.c index b6945c7..3e80fba 100644 --- a/arch/i386/kernel/i387.c +++ b/arch/i386/kernel/i387.c @@ -11,6 +11,7 @@ #include <linux/config.h> #include <linux/sched.h> #include <linux/init.h> +#include <linux/kernel_stat.h> #include <asm/processor.h> #include <asm/i387.h> #include <asm/math_emu.h> @@ -70,8 +71,12 @@ void init_fpu(void) static inline void __save_init_fpu( struct task_struct *tsk ) { if ( cpu_has_fxsr ) { - asm volatile( "fxsave %0 ; fnclex" + asm volatile( "fxsave %0" : "=m" (tsk->thread.i387.fxsave) ); + if (tsk->thread.i387.fxsave.swd & (1<<7)) + asm volatile("fnclex"); + /* AMD CPUs leak F?P. Clear it here */ + asm volatile("ffree %%st(7) ; fildl %0" :: "m" (kstat.context_swtch)); } else { asm volatile( "fnsave %0 ; fwait" : "=m" (tsk->thread.i387.fsave) ); diff --git a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c index a8df6c9..09924f0 100644 --- a/arch/x86_64/kernel/process.c +++ b/arch/x86_64/kernel/process.c @@ -564,8 +564,6 @@ struct task_struct *__switch_to(struct t *next = &next_p->thread; struct tss_struct *tss = init_tss + smp_processor_id(); - unlazy_fpu(prev_p); - /* * Reload rsp0, LDT and the page table pointer: */ @@ -583,6 +581,11 @@ struct task_struct *__switch_to(struct t loadsegment(ds, next->ds); /* + * Must be after DS reload for AMD workaround. + */ + unlazy_fpu(prev_p); + + /* * Switch FS and GS. */ { diff --git a/include/asm-x86_64/i387.h b/include/asm-x86_64/i387.h index a17ce96..5178962 100644 --- a/include/asm-x86_64/i387.h +++ b/include/asm-x86_64/i387.h @@ -125,8 +125,12 @@ static inline void kernel_fpu_begin(void static inline void save_init_fpu( struct task_struct *tsk ) { - asm volatile( "fxsave %0 ; fnclex" + asm volatile( "fxsave %0" : "=m" (tsk->thread.i387.fxsave)); + if (tsk->thread.i387.fxsave.swd & (1<<7)) + asm volatile("fnclex"); + /* AMD CPUs leak F?P through FXSAVE. Clear it here */ + asm volatile("ffree %st(7) ; fildl %gs:0"); tsk->flags &= ~PF_USEDFPU; stts(); }