]> xenbits.xensource.com Git - people/andrewcoop/xen-test-framework.git/commitdiff
PV FSGSBASE behaviour
authorAndrew Cooper <andrew.cooper3@citrix.com>
Tue, 30 Apr 2019 13:29:24 +0000 (14:29 +0100)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Tue, 30 Apr 2019 17:11:52 +0000 (18:11 +0100)
Functional test, which also doubles for XSA-293

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
docs/all-tests.dox
tests/pv-fsgsbase/Makefile [new file with mode: 0644]
tests/pv-fsgsbase/main.c [new file with mode: 0644]

index 1344671eb9b3d5479487dee1aef303d067fc8a66..f0169bf41fd09f30f6583a3fa07a660f21bd0641 100644 (file)
@@ -30,6 +30,8 @@ and functionality.
 
 @subpage test-nmi-taskswitch-priv - Task Gate handling of interrupts.
 
+@subpage test-pv-fsgsbase - FSGSBASE behaviour for PV guests.
+
 @subpage test-pv-iopl - IOPL emulation for PV guests.
 
 @subpage test-swint-emulation - Software interrupt emulation for HVM guests.
@@ -134,6 +136,8 @@ removals.
 @subpage test-xsa-279 - x86: DoS from attempting to use INVPCID with a
 non-canonical addresses.
 
+XSA-293 - See @ref test-pv-fsgsbase.
+
 
 @section index-utility Utilities
 
diff --git a/tests/pv-fsgsbase/Makefile b/tests/pv-fsgsbase/Makefile
new file mode 100644 (file)
index 0000000..cf1816c
--- /dev/null
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME      := pv-fsgsbase
+CATEGORY  := functional
+TEST-ENVS := pv64
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
diff --git a/tests/pv-fsgsbase/main.c b/tests/pv-fsgsbase/main.c
new file mode 100644 (file)
index 0000000..a9c3bc1
--- /dev/null
@@ -0,0 +1,224 @@
+/**
+ * @file tests/pv-fsgsbase/main.c
+ * @ref test-pv-fsgsbase
+ *
+ * @page test-pv-fsgsbase PV FSGSBASE behaviour
+ *
+ * Tests for the behaviour of FSGSBASE handling in PV guests.
+ *
+ * Before XSA-293, Xen had a bug whereby a PV guest could set and clear
+ * FSGSBASE in its view of %%cr4, but Xen left the feature actually enabled in
+ * hardware.
+ *
+ * This in practice leaves the `{RD,WR}{FS,GS}BASE` instructions usable behind
+ * the back of the guest kernel, and can cause state corruption on task switch
+ * if the guest kernel is expecting these instructions to be unusable.
+ *
+ * Another awkward point is that at the time of writing, Xen doesn't raise
+ * @#GP faults for bad %%cr4 updates, opting instead to discard bits it
+ * doesn't like.
+ *
+ *
+ * This test tries to cross-reference the architectural behaviour between the
+ * FSGSBASE CPUID bit, the enable bit in %%cr4, and whether the instructions
+ * are actually usable.
+ *
+ * In addition, it checks that @#GP faults are raised appropriately for trying
+ * to write non-canonical addresses.  This is anticipated to be a problem when
+ * Xen supports LA57 mode.
+ *
+ * @see tests/pv-fsgsbase/main.c
+ */
+#include <xtf.h>
+
+const char test_title[] = "Test PV FSGSBASE behaviour";
+
+static exinfo_t stub_rdfsbase(unsigned long unused)
+{
+    unsigned long tmp;
+    exinfo_t fault = 0;
+
+    asm volatile ("1: rdfsbase %[val]; 2:"
+                  _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_eax)
+                  : "+a" (fault), [val] "=r" (tmp)
+                  : "X" (ex_record_fault_eax));
+
+    return fault;
+}
+
+static exinfo_t stub_rdgsbase(unsigned long unused)
+{
+    unsigned long tmp;
+    exinfo_t fault = 0;
+
+    asm volatile ("1: rdgsbase %[val]; 2:"
+                  _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_eax)
+                  : "+a" (fault), [val] "=r" (tmp)
+                  : "X" (ex_record_fault_eax));
+
+    return fault;
+}
+
+static exinfo_t stub_wrfsbase(unsigned long val)
+{
+    exinfo_t fault = 0;
+
+    asm volatile ("1: wrfsbase %[val]; 2:"
+                  _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_eax)
+                  : "+a" (fault)
+                  : [val] "r" (val), "X" (ex_record_fault_eax));
+
+    return fault;
+}
+
+static exinfo_t stub_wrgsbase(unsigned long val)
+{
+    exinfo_t fault = 0;
+
+    asm volatile ("1: wrgsbase %[val]; 2:"
+                  _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_eax)
+                  : "+a" (fault)
+                  : [val] "r" (val), "X" (ex_record_fault_eax));
+
+    return fault;
+}
+
+static void test_fsgsbase_insns(bool should_ud)
+{
+    static const struct test {
+        const char *name;
+        exinfo_t (*fn)(unsigned long);
+    } tests[] = {
+        { "rdfsbase", stub_rdfsbase },
+        { "rdgsbase", stub_rdgsbase },
+        { "wrfsbase", stub_wrfsbase },
+        { "wrgsbase", stub_wrgsbase },
+    };
+    unsigned int i;
+    exinfo_t exp = should_ud ? EXINFO_SYM(UD, 0) : 0;
+
+    for ( i = 0; i < ARRAY_SIZE(tests); ++i )
+    {
+        const struct test *t = &tests[i];
+        exinfo_t res = t->fn(0);
+
+        if ( res != exp )
+            xtf_failure("Fail: Testing '%s'\n"
+                        "  expected %pe, got %pe\n",
+                        t->name, _p(exp), _p(res));
+    }
+}
+
+static void test_wrfsbase_values(void)
+{
+    static const struct test {
+        unsigned long val;
+        exinfo_t fault;
+    } tests[] = {
+        { 0x0000000000000000ul, 0 },
+        { 0x00007ffffffffffful, 0 },
+        { 0x0000800000000000ul, EXINFO_SYM(GP, 0) },
+        { 0x7ffffffffffffffful, EXINFO_SYM(GP, 0) },
+        { 0x8000000000000000ul, EXINFO_SYM(GP, 0) },
+        { 0xffff7ffffffffffful, EXINFO_SYM(GP, 0) },
+        { 0xffff800000000000ul, 0 },
+        { 0xfffffffffffffffful, 0 },
+    };
+    unsigned int i;
+
+    for ( i = 0; i < ARRAY_SIZE(tests); ++i )
+    {
+        const struct test *t = &tests[i];
+        exinfo_t res = stub_wrfsbase(t->val);
+
+        if ( res != t->fault )
+            xtf_failure("Fail: Testing WRFSBASE %016lx \n"
+                        "  expected %pe, got %pe\n",
+                        t->val , _p(t->fault), _p(res));
+    }
+}
+
+/*
+ * Bad updates to CR4 should fault, but at the time of writing, Xen squashes
+ * all faults and breaks the use of the *_safe() functions, and drops the
+ * feature.
+ *
+ * Wrap write_cr4_safe() with a variant which reads cr4 back, to see if write
+ * fully took effect.
+ */
+static bool pv_write_cr4_safe(unsigned long val)
+{
+    unsigned long old = read_cr4(), changed = old ^ val;
+    bool fault = write_cr4_safe(val);
+
+    if ( !fault )
+    {
+        unsigned long new = read_cr4();
+
+        fault = (old ^ new) != changed;
+    }
+
+    return fault;
+}
+
+void test_main(void)
+{
+    unsigned long cr4;
+
+    if ( !cpu_has_fsgsbase )
+        xtf_skip("FSGSBASE support not detected\n");
+
+    /* Check that CR4.FSGSBASE is clear to begin with. */
+    cr4 = read_cr4();
+    if ( cr4 & X86_CR4_FSGSBASE )
+    {
+        xtf_failure("Fail: Initial CR4.FSGSBASE state should be clear\n");
+
+        if ( pv_write_cr4_safe(cr4 &= ~X86_CR4_FSGSBASE) )
+            return xtf_failure("Fail: Fault while trying to clear CR4.FSGSBASE\n");
+    }
+
+    /*
+     * Check that the {RD,WR}{FS,GS}BASE instructions are disabled.  When
+     * vulnerable to XSA-293, this check will fail.
+     */
+    test_fsgsbase_insns(true);
+
+    if ( !cpu_has_fsgsbase )
+    {
+        /* If the FSGSBASE feature isn't visible, check we can't turn it on. */
+        if ( !pv_write_cr4_safe(cr4 |= X86_CR4_FSGSBASE) )
+            xtf_failure("Fail: Able to set CR4.FSGSBASE without the feature\n");
+
+        return;
+    }
+
+    /* Check we can turn CR4.FSGSBASE on. */
+    if ( pv_write_cr4_safe(cr4 |= X86_CR4_FSGSBASE) )
+        xtf_failure("Fail: Unable to enable CR4.FSGSBASE\n");
+
+    /* Check that {RD,WR}{FS,GS}BASE instructions are enabled. */
+    test_fsgsbase_insns(false);
+
+    /* Check that WRFSBASE faults on appropriate values. */
+    test_wrfsbase_values();
+
+    /* Check we can turn CR4.FSGSBASE off again. */
+    if ( pv_write_cr4_safe(cr4 &= ~X86_CR4_FSGSBASE) )
+        xtf_failure("Fail: Unable to enable CR4.FSGSBASE\n");
+
+    /* Check that {RD,WR}{FS,GS}BASE instructions are disabled again. */
+    test_fsgsbase_insns(true);
+
+    xtf_success(NULL);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */