x86/fpu: Check to ensure increasing-offset xstate offsets
authorDave Hansen <dave.hansen@linux.intel.com>
Wed, 2 Sep 2015 23:31:30 +0000 (16:31 -0700)
committerIngo Molnar <mingo@kernel.org>
Mon, 14 Sep 2015 10:22:02 +0000 (12:22 +0200)
The xstate CPUID leaves enumerate where each state component is
inside the XSAVE buffer, along with the size of the entire
buffer.  Our new XSAVE sanity-checking code extrapolates an
expected _total_ buffer size by looking at the last component
that it encounters.

That method requires that the highest-numbered component also
be the one with the highest offset.  This is a pretty safe
assumption, but let's add some code to ensure it stays true.

To make this check work correctly, we also need to ensure we
only consider the offsets from enabled features because the
offset register (ebx) will return 0 on unsupported features.

This also means that we will preserve the -1's that we
initialized xstate_offsets/sizes[] with.  That will help
find bugs.

Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Tim Chen <tim.c.chen@linux.intel.com>
Cc: dave@sr71.net
Cc: linux-kernel@vger.kernel.org
Link: http://lkml.kernel.org/r/20150902233130.0843AB15@viggo.jf.intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/kernel/fpu/xstate.c

index 4704955044031bc72da6dc31516b86f5d5ffddfc..a8297f2ca2d1a35e8ec34b539bc722793b2f64a1 100644 (file)
@@ -184,6 +184,16 @@ void fpu__init_cpu_xstate(void)
        xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask);
 }
 
+/*
+ * Note that in the future we will likely need a pair of
+ * functions here: one for user xstates and the other for
+ * system xstates.  For now, they are the same.
+ */
+static int xfeature_enabled(enum xfeature xfeature)
+{
+       return !!(xfeatures_mask & (1UL << xfeature));
+}
+
 /*
  * Record the offsets and sizes of various xstates contained
  * in the XSAVE state memory layout.
@@ -191,12 +201,25 @@ void fpu__init_cpu_xstate(void)
 static void __init setup_xstate_features(void)
 {
        u32 eax, ebx, ecx, edx, i;
+       /* start at the beginnning of the "extended state" */
+       unsigned int last_good_offset = offsetof(struct xregs_state,
+                                                extended_state_area);
 
        for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
-               cpuid_count(XSTATE_CPUID, i, &eax, &ebx, &ecx, &edx);
+               if (!xfeature_enabled(i))
+                       continue;
 
+               cpuid_count(XSTATE_CPUID, i, &eax, &ebx, &ecx, &edx);
                xstate_offsets[i] = ebx;
                xstate_sizes[i] = eax;
+               /*
+                * In our xstate size checks, we assume that the
+                * highest-numbered xstate feature has the
+                * highest offset in the buffer.  Ensure it does.
+                */
+               WARN_ONCE(last_good_offset > xstate_offsets[i],
+                       "x86/fpu: misordered xstate at %d\n", last_good_offset);
+               last_good_offset = xstate_offsets[i];
 
                printk(KERN_INFO "x86/fpu: xstate_offset[%d]: %4d, xstate_sizes[%d]: %4d\n", i, ebx, i, eax);
        }
@@ -225,16 +248,6 @@ static void __init print_xstate_features(void)
        print_xstate_feature(XFEATURE_MASK_Hi16_ZMM);
 }
 
-/*
- * Note that in the future we will likely need a pair of
- * functions here: one for user xstates and the other for
- * system xstates.  For now, they are the same.
- */
-static int xfeature_enabled(enum xfeature xfeature)
-{
-       return !!(xfeatures_mask & (1UL << xfeature));
-}
-
 /*
  * This function sets up offsets and sizes of all extended states in
  * xsave area. This supports both standard format and compacted format