Re: [PATCH] [1/2] i386/x86-64: Fix x87 information leak between processes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Patch says 1/2 . Is there another patch that comes with this ? Or is vger 
lagging again ? 

On Wednesday 19 April 2006 5:36 pm, Andi Kleen wrote:
> 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 x87 load from a kernel variable to
> clear FOP/FIP/FDP.
>
> This means other processes always will only see a constant value
> defined by the kernel in their FP state.
>
> I took some pain to make sure to chose a variable that's already
> in L1 during context switch to make the overhead of this low.
>
> Also alternative() is used to patch away the new code on CPUs
> who don't need it.
>
> Patch for both i386/x86-64.
>
> The problem was discovered originally by Jan Beulich. Richard
> Brunner provided the basic code for the workarounds, with contribution
> from Jan.
>
> This is CVE-2006-1056
>
> Cc: [email protected]
> Cc: [email protected]
>
> Signed-off-by: Andi Kleen <[email protected]>
>
> Index: linux/include/asm-x86_64/i387.h
> ===================================================================
> --- linux.orig/include/asm-x86_64/i387.h
> +++ linux/include/asm-x86_64/i387.h
> @@ -72,6 +72,23 @@ extern int set_fpregs(struct task_struct
>  #define set_fpu_swd(t,val) ((t)->thread.i387.fxsave.swd = (val))
>  #define set_fpu_fxsr_twd(t,val) ((t)->thread.i387.fxsave.twd = (val))
>
> +#define X87_FSW_ES (1 << 7)	/* Exception Summary */
> +
> +/* AMD CPUs don't save/restore FDP/FIP/FOP unless an exception
> +   is pending. Clear the x87 state here by setting it to fixed
> +   values. The kernel data segment can be sometimes 0 and sometimes
> +   new user value. Both should be ok.
> +   Use the PDA as safe address because it should be already in L1. */
> +static inline void clear_fpu_state(struct i387_fxsave_struct *fx)
> +{
> +	if (unlikely(fx->swd & X87_FSW_ES))
> +		 asm volatile("fnclex");
> +	alternative_input(ASM_NOP8 ASM_NOP2,
> +	     	     "    emms\n"		/* clear stack tags */
> +	     	     "    fildl %%gs:0",	/* load to clear state */
> +		     X86_FEATURE_FXSAVE_LEAK);
> +}
> +
>  static inline int restore_fpu_checking(struct i387_fxsave_struct *fx)
>  {
>  	int err;
> @@ -119,6 +136,7 @@ static inline int save_i387_checking(str
>  #endif
>  	if (unlikely(err))
>  		__clear_user(fx, sizeof(struct i387_fxsave_struct));
> +	/* No need to clear here because the caller clears USED_MATH */
>  	return err;
>  }
>
> @@ -149,7 +167,7 @@ static inline void __fxsave_clear(struct
>  				"i" (offsetof(__typeof__(*tsk),
>  					      thread.i387.fxsave)));
>  #endif
> -	__asm__ __volatile__("fnclex");
> +	clear_fpu_state(&tsk->thread.i387.fxsave);
>  }
>
>  static inline void kernel_fpu_begin(void)
> Index: linux/include/asm-i386/i387.h
> ===================================================================
> --- linux.orig/include/asm-i386/i387.h
> +++ linux/include/asm-i386/i387.h
> @@ -13,6 +13,7 @@
>
>  #include <linux/sched.h>
>  #include <linux/init.h>
> +#include <linux/kernel_stat.h>
>  #include <asm/processor.h>
>  #include <asm/sigcontext.h>
>  #include <asm/user.h>
> @@ -38,17 +39,38 @@ extern void init_fpu(struct task_struct
>  extern void kernel_fpu_begin(void);
>  #define kernel_fpu_end() do { stts(); preempt_enable(); } while(0)
>
> +/* We need a safe address that is cheap to find and that is already
> +   in L1 during context switch. The best choices are unfortunately
> +   different for UP and SMP */
> +#ifdef CONFIG_SMP
> +#define safe_address (__per_cpu_offset[0])
> +#else
> +#define safe_address (kstat_cpu(0).cpustat.user)
> +#endif
> +
>  /*
>   * These must be called with preempt disabled
>   */
>  static inline void __save_init_fpu( struct task_struct *tsk )
>  {
> +	/* Use more nops than strictly needed in case the compiler
> +	   varies code */
>  	alternative_input(
> -		"fnsave %1 ; fwait ;" GENERIC_NOP2,
> -		"fxsave %1 ; fnclex",
> +		"fnsave %[fx] ;fwait;" GENERIC_NOP8 GENERIC_NOP4,
> +		"fxsave %[fx]\n"
> +		"bt $7,%[fsw] ; jc 1f ; fnclex\n1:",
>  		X86_FEATURE_FXSR,
> -		"m" (tsk->thread.i387.fxsave)
> -		:"memory");
> +		[fx] "m" (tsk->thread.i387.fxsave),
> +		[fsw] "m" (tsk->thread.i387.fxsave.swd) : "memory");
> +	/* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception
> +	   is pending.  Clear the x87 state here by setting it to fixed
> +   	   values. __per_cpu_offset[0] is a random variable that should be in
> L1 */ +	alternative_input(
> +		GENERIC_NOP8 GENERIC_NOP2,
> +		"emms\n\t"	  	/* clear stack tags */
> +		"fildl %[addr]", 	/* set F?P to defined value */
> +		X86_FEATURE_FXSAVE_LEAK,
> +		[addr] "m" (safe_address));
>  	task_thread_info(tsk)->status &= ~TS_USEDFPU;
>  }
>
> Index: linux/arch/i386/kernel/cpu/amd.c
> ===================================================================
> --- linux.orig/arch/i386/kernel/cpu/amd.c
> +++ linux/arch/i386/kernel/cpu/amd.c
> @@ -207,6 +207,8 @@ static void __init init_amd(struct cpuin
>  		set_bit(X86_FEATURE_K7, c->x86_capability);
>  		break;
>  	}
> +	if (c->x86 >= 6)
> +		set_bit(X86_FEATURE_FXSAVE_LEAK, c->x86_capability);
>
>  	display_cacheinfo(c);
>
> Index: linux/arch/x86_64/kernel/setup.c
> ===================================================================
> --- linux.orig/arch/x86_64/kernel/setup.c
> +++ linux/arch/x86_64/kernel/setup.c
> @@ -928,6 +928,10 @@ static int __init init_amd(struct cpuinf
>  	if (c->x86 == 15 && ((level >= 0x0f48 && level < 0x0f50) || level >=
> 0x0f58)) set_bit(X86_FEATURE_REP_GOOD, &c->x86_capability);
>
> +	/* Enable workaround for FXSAVE leak */
> +	if (c->x86 >= 6)
> +		set_bit(X86_FEATURE_FXSAVE_LEAK, &c->x86_capability);
> +
>  	r = get_model_name(c);
>  	if (!r) {
>  		switch (c->x86) {
> Index: linux/include/asm-i386/cpufeature.h
> ===================================================================
> --- linux.orig/include/asm-i386/cpufeature.h
> +++ linux/include/asm-i386/cpufeature.h
> @@ -71,6 +71,7 @@
>  #define X86_FEATURE_P4		(3*32+ 7) /* P4 */
>  #define X86_FEATURE_CONSTANT_TSC (3*32+ 8) /* TSC ticks at a constant rate
> */ #define X86_FEATURE_UP		(3*32+ 9) /* smp kernel running on up */
> +#define X86_FEATURE_FXSAVE_LEAK (3*32+10) /* FXSAVE leaks FOP/FIP/FOP */
>
>  /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
>  #define X86_FEATURE_XMM3	(4*32+ 0) /* Streaming SIMD Extensions-3 */
> Index: linux/include/asm-x86_64/cpufeature.h
> ===================================================================
> --- linux.orig/include/asm-x86_64/cpufeature.h
> +++ linux/include/asm-x86_64/cpufeature.h
> @@ -64,6 +64,7 @@
>  #define X86_FEATURE_REP_GOOD	(3*32+ 4) /* rep microcode works well on this
> CPU */ #define X86_FEATURE_CONSTANT_TSC (3*32+5) /* TSC runs at constant
> rate */ #define X86_FEATURE_SYNC_RDTSC  (3*32+6)  /* RDTSC syncs CPU core
> */ +#define X86_FEATURE_FXSAVE_LEAK (3*32+7)  /* FIP/FOP/FDP leaks through
> FXSAVE */
>
>  /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
>  #define X86_FEATURE_XMM3	(4*32+ 0) /* Streaming SIMD Extensions-3 */
> Index: linux/arch/x86_64/kernel/process.c
> ===================================================================
> --- linux.orig/arch/x86_64/kernel/process.c
> +++ linux/arch/x86_64/kernel/process.c
> @@ -575,8 +575,10 @@ __switch_to(struct task_struct *prev_p,
>  	prev->userrsp = read_pda(oldrsp);
>  	write_pda(oldrsp, next->userrsp);
>  	write_pda(pcurrent, next_p);
> +
>  	/* This must be here to ensure both math_state_restore() and
> -	   kernel_fpu_begin() work consistently. */
> +	   kernel_fpu_begin() work consistently.
> +	   And the AMD workaround requires it to be after DS reload. */
>  	unlazy_fpu(prev_p);
>  	write_pda(kernelstack,
>  		  task_stack_page(next_p) + THREAD_SIZE - PDA_STACKOFFSET);
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux