Fix an information disclosure issue on AMD cpus. The x87 debug registers,
authorMatthew Dillon <dillon@dragonflybsd.org>
Tue, 2 May 2006 22:52:24 +0000 (22:52 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Tue, 2 May 2006 22:52:24 +0000 (22:52 +0000)
in particular the last instruction and last data pointers, are not restored
by fxrstor() if there was no pending exception.  Add some dummy FP
instructions (about ~7ns) to 'clean' the registers so a process has no
visibility into another process's FP state.

Submitted-by: Gary <gary@velocity-servers.net>
Taken-from: FreeBSD
Security-Advistory: FreeBSD-SA-06:14.fpu

sys/i386/isa/npx.c
sys/platform/pc32/isa/npx.c

index 8a1de55..2057d19 100644 (file)
@@ -33,7 +33,7 @@
  *
  *     from: @(#)npx.c 7.2 (Berkeley) 5/12/91
  * $FreeBSD: src/sys/i386/isa/npx.c,v 1.80.2.3 2001/10/20 19:04:38 tegge Exp $
- * $DragonFly: src/sys/i386/isa/Attic/npx.c,v 1.30 2006/04/02 20:43:27 dillon Exp $
+ * $DragonFly: src/sys/i386/isa/Attic/npx.c,v 1.31 2006/05/02 22:52:24 dillon Exp $
  */
 
 #include "opt_cpu.h"
@@ -139,6 +139,10 @@ void       stop_emulating  (void);
 #endif /* CPU_DISABLE_SSE */
 
 typedef u_char bool_t;
+#ifndef CPU_DISABLE_SSE
+static void    fpu_clean_state(void);
+#endif
+
 
 static int     npx_attach      (device_t dev);
        void    npx_intr        (void *);
@@ -973,15 +977,52 @@ fpusave(union savefpu *addr)
                fnsave(addr);
 }
 
+#ifndef CPU_DISABLE_SSE
+/*
+ * On AuthenticAMD processors, the fxrstor instruction does not restore
+ * the x87's stored last instruction pointer, last data pointer, and last
+ * opcode values, except in the rare case in which the exception summary
+ * (ES) bit in the x87 status word is set to 1.
+ *
+ * In order to avoid leaking this information across processes, we clean
+ * these values by performing a dummy load before executing fxrstor().
+ */
+static double  dummy_variable = 0.0;
+static void
+fpu_clean_state(void)
+{
+       u_short status;
+
+       /*
+        * Clear the ES bit in the x87 status word if it is currently
+        * set, in order to avoid causing a fault in the upcoming load.
+        */
+       fnstsw(&status);
+       if (status & 0x80)
+               fnclex();
+
+       /*
+        * Load the dummy variable into the x87 stack.  This mangles
+        * the x87 stack, but we don't care since we're about to call
+        * fxrstor() anyway.
+        */
+       __asm __volatile("ffree %%st(7); fld %0" : : "m" (dummy_variable));
+}
+#endif /* CPU_DISABLE_SSE */
+
 static void
 fpurstor(union savefpu *addr)
 {
 #ifndef CPU_DISABLE_SSE
-       if (cpu_fxsr)
+       if (cpu_fxsr) {
+               fpu_clean_state();
                fxrstor(addr);
-       else
-#endif
+       } else {
                frstor(addr);
+       }
+#else
+       frstor(addr);
+#endif
 }
 
 /*
index be0a0d7..b4f29ea 100644 (file)
@@ -33,7 +33,7 @@
  *
  *     from: @(#)npx.c 7.2 (Berkeley) 5/12/91
  * $FreeBSD: src/sys/i386/isa/npx.c,v 1.80.2.3 2001/10/20 19:04:38 tegge Exp $
- * $DragonFly: src/sys/platform/pc32/isa/npx.c,v 1.30 2006/04/02 20:43:27 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/isa/npx.c,v 1.31 2006/05/02 22:52:24 dillon Exp $
  */
 
 #include "opt_cpu.h"
@@ -139,6 +139,10 @@ void       stop_emulating  (void);
 #endif /* CPU_DISABLE_SSE */
 
 typedef u_char bool_t;
+#ifndef CPU_DISABLE_SSE
+static void    fpu_clean_state(void);
+#endif
+
 
 static int     npx_attach      (device_t dev);
        void    npx_intr        (void *);
@@ -973,15 +977,52 @@ fpusave(union savefpu *addr)
                fnsave(addr);
 }
 
+#ifndef CPU_DISABLE_SSE
+/*
+ * On AuthenticAMD processors, the fxrstor instruction does not restore
+ * the x87's stored last instruction pointer, last data pointer, and last
+ * opcode values, except in the rare case in which the exception summary
+ * (ES) bit in the x87 status word is set to 1.
+ *
+ * In order to avoid leaking this information across processes, we clean
+ * these values by performing a dummy load before executing fxrstor().
+ */
+static double  dummy_variable = 0.0;
+static void
+fpu_clean_state(void)
+{
+       u_short status;
+
+       /*
+        * Clear the ES bit in the x87 status word if it is currently
+        * set, in order to avoid causing a fault in the upcoming load.
+        */
+       fnstsw(&status);
+       if (status & 0x80)
+               fnclex();
+
+       /*
+        * Load the dummy variable into the x87 stack.  This mangles
+        * the x87 stack, but we don't care since we're about to call
+        * fxrstor() anyway.
+        */
+       __asm __volatile("ffree %%st(7); fld %0" : : "m" (dummy_variable));
+}
+#endif /* CPU_DISABLE_SSE */
+
 static void
 fpurstor(union savefpu *addr)
 {
 #ifndef CPU_DISABLE_SSE
-       if (cpu_fxsr)
+       if (cpu_fxsr) {
+               fpu_clean_state();
                fxrstor(addr);
-       else
-#endif
+       } else {
                frstor(addr);
+       }
+#else
+       frstor(addr);
+#endif
 }
 
 /*