]> xenbits.xensource.com Git - people/vhanquez/xen.git/commitdiff
x86, vtd: [CVE-2011-1898] Protect against malicious MSIs from untrusted devices.
authorKeir Fraser <keir@xen.org>
Thu, 12 May 2011 17:03:47 +0000 (18:03 +0100)
committerKeir Fraser <keir@xen.org>
Thu, 12 May 2011 17:03:47 +0000 (18:03 +0100)
In the absence of VT-d interrupt remapping support, a device can send
arbitrary APIC messages to host CPUs. One class of attack that results
is to confuse the hypervisor by delivering asynchronous interrupts to
vectors that are expected to handle only synchronous
traps/exceptions.

We block this class of attack by:
(1) setting APIC.TPR=0x10, to block all interrupts below vector
0x20. This blocks delivery to all architectural exception vectors.
(2) checking APIC.ISR[vec] for vectors 0x80 (fast syscall) and 0x82
(hypercall). In these cases we BUG if we detect we are handling a
hardware interrupt -- turning a potentially more severe infiltration
into a straightforward system crash (i.e, DoS).

Thanks to Invisible Things Lab <http://www.invisiblethingslab.com>
for discovery and detailed investigation of this attack.

Signed-off-by: Keir Fraser <keir@xen.org>
xen-unstable changeset:   23337:cc91832a02c7
xen-unstable date:        Thu May 12 16:39:31 2011 +0100

xen/arch/x86/apic.c
xen/arch/x86/x86_64/compat/entry.S
xen/arch/x86/x86_64/entry.S
xen/drivers/passthrough/vtd/iommu.c

index 1590c3a1712033a9419b965c4a7658d8b0cb5e89..3a0f6fb72519bb0b92b00ce4368836146a141d12 100644 (file)
@@ -575,12 +575,9 @@ void __devinit setup_local_APIC(void)
     init_apic_ldr();
 
     /*
-     * Set Task Priority to 'accept all'. We never change this
-     * later on.
+     * Set Task Priority to reject any interrupts below FIRST_DYNAMIC_VECTOR.
      */
-    value = apic_read(APIC_TASKPRI);
-    value &= ~APIC_TPRI_MASK;
-    apic_write_around(APIC_TASKPRI, value);
+    apic_write_around(APIC_TASKPRI, (FIRST_DYNAMIC_VECTOR & 0xF0) - 0x10);
 
     /*
      * After a crash, we no longer service the interrupts and a pending
@@ -1483,3 +1480,9 @@ int __init APIC_init_uniprocessor (void)
 
     return 0;
 }
+
+void check_for_unexpected_msi(unsigned int vector)
+{
+    unsigned long v = apic_read(APIC_ISR + ((vector & ~0x1f) >> 1));
+    BUG_ON(v & (1 << (vector & 0x1f)));
+}
index d87ef0cfdecd984d0a7518c37685d5fe435c340a..8208b6b54f2d436cfe5780d883fe7b626268687a 100644 (file)
 #include <asm/page.h>
 #include <asm/desc.h>
 #include <public/xen.h>
+#include <irq_vectors.h>
 
         ALIGN
 ENTRY(compat_hypercall)
         pushq $0
         movl  $TRAP_syscall,4(%rsp)
         SAVE_ALL
+
+        cmpb  $0,untrusted_msi(%rip)
+UNLIKELY_START(ne, msi_check)
+        movl  $HYPERCALL_VECTOR,%edi
+        call  check_for_unexpected_msi
+        RESTORE_ALL
+        SAVE_ALL
+UNLIKELY_END(msi_check)
+
         GET_CURRENT(%rbx)
 
         cmpl  $NR_hypercalls,%eax
index a6c68220c89a58e8a2d5adfad38c3395a5beb1d0..61938f8f495e5c47b7d7b43fc64174ce1385fd04 100644 (file)
@@ -296,6 +296,14 @@ ENTRY(int80_direct_trap)
         pushq $0
         SAVE_ALL
 
+        cmpb  $0,untrusted_msi(%rip)
+UNLIKELY_START(ne, msi_check)
+        movl  $0x80,%edi
+        call  check_for_unexpected_msi
+        RESTORE_ALL
+        SAVE_ALL
+UNLIKELY_END(msi_check)
+
         GET_CURRENT(%rbx)
 
         /* Check that the callback is non-null. */
index abcf02245481363115fcb3814819d75a22d799d5..b33cff22e10f24877eed6280007cedb81d736985 100644 (file)
@@ -48,6 +48,9 @@
 static int sharept = 0;
 boolean_param("sharept", sharept);
 
+/* Possible unfiltered LAPIC/MSI messages from untrusted sources? */
+bool_t __read_mostly untrusted_msi;
+
 int nr_iommus;
 
 static void setup_dom0_devices(struct domain *d);
@@ -1582,6 +1585,14 @@ static int reassign_device_ownership(
     if (!pdev)
         return -ENODEV;
 
+    /*
+     * Devices assigned to untrusted domains (here assumed to be any domU)
+     * can attempt to send arbitrary LAPIC/MSI messages. We are unprotected
+     * by the root complex unless interrupt remapping is enabled.
+     */
+    if ( (target != dom0) && !iommu_intremap )
+        untrusted_msi = 1;
+
     ret = domain_context_unmap(source, bus, devfn);
     if ( ret )
         return ret;