References: CVE-2013-0153 XSA-36 bnc#800275

# HG changeset patch
# User George Dunlap <george.dunlap@eu.citrix.com>
# Date 1311701818 -3600
# Node ID ef9ed3d2aa870a37ed5e611be9c524d526a2d604
# Parent  590aadf7c46ae979da3552332f592f9492ce6d8b
xen: Infrastructure to allow irqs to share vector maps

Laying the groundwork for per-device vector maps.  This generic
code allows any irq to point to a vector map; all irqs sharing the
same vector map will avoid sharing vectors.

Signed-off-by: George Dunlap <george.dunlap@eu.citrix.com>

# HG changeset patch
# User George Dunlap <george.dunlap@eu.citrix.com>
# Date 1314026133 -3600
# Node ID 3a05da2dc7c0a5fc0fcfc40c535d1fcb71203625
# Parent  d1cd78a73a79e0e648937322cdb8d92a7f86327a
x86: Fix up irq vector map logic

We need to make sure that cfg->used_vector is only cleared once;
otherwise there may be a race condition that allows the same vector to
be assigned twice, defeating the whole purpose of the map.

This makes two changes:
* __clear_irq_vector() only clears the vector if the irq is not being
moved
* smp_iqr_move_cleanup_interrupt() only clears used_vector if this
is the last place it's being used (move_cleanup_count==0 after
decrement).

Also make use of asserts more consistent, to catch this kind of logic
bug in the future.

Signed-off-by: George Dunlap <george.dunlap@eu.citrix.com>

--- a/xen/arch/x86/io_apic.c
+++ b/xen/arch/x86/io_apic.c
@@ -552,6 +552,13 @@ fastcall void smp_irq_move_cleanup_inter
         }
         __get_cpu_var(vector_irq)[vector] = -1;
         cfg->move_cleanup_count--;
+
+        if ( cfg->move_cleanup_count == 0
+             &&  cfg->used_vectors )
+        {
+            ASSERT(test_bit(vector, cfg->used_vectors));
+            clear_bit(vector, cfg->used_vectors);
+        }
 unlock:
         spin_unlock(&desc->lock);
     }
--- a/xen/arch/x86/irq.c
+++ b/xen/arch/x86/irq.c
@@ -96,6 +96,11 @@ static int __bind_irq_vector(int irq, in
         per_cpu(vector_irq, cpu)[vector] = irq;
     cfg->vector = vector;
     cfg->domain = mask;
+    if ( cfg->used_vectors )
+    {
+        ASSERT(!test_bit(vector, cfg->used_vectors));
+        set_bit(vector, cfg->used_vectors);
+    }
     irq_status[irq] = IRQ_USED;
     if (IO_APIC_IRQ(irq))
         irq_vector[irq] = vector;
@@ -161,6 +166,7 @@ static void dynamic_irq_cleanup(unsigned
     desc->depth   = 1;
     desc->msi_desc = NULL;
     desc->handler = &no_irq_type;
+    desc->chip_data->used_vectors=NULL;
     cpus_setall(desc->affinity);
     spin_unlock_irqrestore(&desc->lock, flags);
 
@@ -193,6 +199,7 @@ static void __clear_irq_vector(int irq)
 
     if (likely(!cfg->move_in_progress))
         return;
+
     for_each_cpu_mask(cpu, tmp_mask) {
         for (vector = FIRST_DYNAMIC_VECTOR; vector <= LAST_DYNAMIC_VECTOR;
                                 vector++) {
@@ -203,6 +210,12 @@ static void __clear_irq_vector(int irq)
         }
      }
 
+    if ( cfg->used_vectors )
+    {
+        ASSERT(test_bit(vector, cfg->used_vectors));
+        clear_bit(vector, cfg->used_vectors);
+    }
+
     cfg->move_in_progress = 0;
 }
 
@@ -262,6 +275,7 @@ static void init_one_irq_cfg(struct irq_
     cfg->vector = IRQ_VECTOR_UNASSIGNED;
     cpus_clear(cfg->domain);
     cpus_clear(cfg->old_domain);
+    cfg->used_vectors = NULL;
 }
 
 int init_irq_data(void)
@@ -388,6 +402,10 @@ next:
         if (test_bit(vector, used_vectors))
             goto next;
 
+        if (cfg->used_vectors
+            && test_bit(vector, cfg->used_vectors) )
+            goto next;
+
         for_each_cpu_mask(new_cpu, tmp_mask)
             if (per_cpu(vector_irq, new_cpu)[vector] != -1)
                 goto next;
@@ -403,6 +421,11 @@ next:
             per_cpu(vector_irq, new_cpu)[vector] = irq;
         cfg->vector = vector;
         cpus_copy(cfg->domain, tmp_mask);
+        if ( cfg->used_vectors )
+        {
+            ASSERT(!test_bit(vector, cfg->used_vectors));
+            set_bit(vector, cfg->used_vectors);
+        }
 
         irq_status[irq] = IRQ_USED;
             if (IO_APIC_IRQ(irq))
--- a/xen/include/asm-x86/irq.h
+++ b/xen/include/asm-x86/irq.h
@@ -26,11 +26,16 @@
 #define MAX_GSI_IRQS PAGE_SIZE * 8
 #define MAX_NR_IRQS (2 * MAX_GSI_IRQS)
 
+struct vector_mask {
+    DECLARE_BITMAP(_bits,NR_VECTORS);
+};
+
 struct irq_cfg {
         int  vector;
         cpumask_t domain;
         cpumask_t old_domain;
         unsigned move_cleanup_count;
+        struct vector_mask *used_vectors;
         u8 move_in_progress : 1;
 };
 
