[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] Re: [PATCH] tools/ioemu: Fixing Security Hole in Qemu MSIX table access management



Thanks for your comments. However, I got the impression that what I
saw here in this email thread was not in accordance with what I got
when this issue was first submitted.
I am not aware of any context of larger scope of MSI-X cleaning ups if
you are planning to do so. As a result, I might be missing some
important points. So please just go ahead and submit your patches.
Comments are embedded below.


2011/7/12 Jan Beulich <JBeulich@xxxxxxxxxx>:
>>>> On 12.07.11 at 07:24, Haitao Shan <maillists.shan@xxxxxxxxx> wrote:
>> Hi,
>>
>> As reported by Jan, current Qemu does not handle MSIX table mapping
>> properly.
>>
>> Details:
>>
>> MSI-X table resides in one of the physical BARs. When Qemu handles
>> guest's changes to BAR register (within which, MSI-X table resides),
>> Qemu first allows access of the whole BAR MMIO ranges and then removes
>> those of MSI-X. There is a small window here. It is possible that on a
>> SMP guests one vcpu could have access to the physical MSI-X
>> configurations when another vcpu is writing BAR registers.
>>
>> The patch fixes this issue by first producing the valid MMIO ranges by
>> removing MSI-X table's range from the whole BAR mmio range and later
>> passing these ranges to Xen.
>
> That's only half of it - something similar would need to be done for the
> pending bit array.
Please justify why this read-only PBA should also be removed from
guest access. Note that we actually mask physical MSI via MSI-X table
when guests mask it via virtualized MSI-X table.

>
> Further I'm having the impression that while you avoid assigning the
> questionable MMIO range to the guest (which isn't a security concern
> as long as the BAR determination for the device in the hypervisor is
> correct), your patch doesn't prevent qemu actually mapping these
> ranges writably and allow pci_msix_writel() to access it (which is the
> actual open security problem).
I totally disagree. I think Dom0 together with its management SW
entities such as Qemu and libxc/libxl are to be trusted. It can be
arguable to what extent Xen can trust them.
Mapping that to Qemu is mainly for writing MASK bit to physical MSI-X
table directly. Handling guests' masking MSI is already too long a
code path.
If Qemu is not trusted, I would say perhaps you can move MSI
virtualization part from Qemu to Xen itself.

>
> Further, I don't think it's correct to remove guest access to either of
> the two ranges altogether - either qemu needs to emulate access to
> these, or the guest ought to be able to access the ranges directly,
> but read-only.
PBA is exposed to guests, unless it happens to be located on the same
page of MSI-X table (in this case, it have to be removed), per my
understanding.
MSI-X table cannot be exposed to guests even read-only.

Shan Haitao

>
> Jan
>
>> Please have a review, thanks!
>>
>> Signed-off-by:    Shan Haitao <haitao.shan@xxxxxxxxx>
>>
>> diff --git a/hw/pass-through.c b/hw/pass-through.c
>> index 9c5620d..b9c2f32 100644
>> --- a/hw/pass-through.c
>> +++ b/hw/pass-through.c
>> @@ -92,6 +92,7 @@
>>
>>  #include <unistd.h>
>>  #include <sys/ioctl.h>
>> +#include <assert.h>
>>
>>  extern int gfx_passthru;
>>  int igd_passthru = 0;
>> @@ -1103,6 +1104,7 @@ static void pt_iomem_map(PCIDevice *d, int i,
>> uint32_t e_phys, uint32_t e_size,
>>  {
>>      struct pt_dev *assigned_device  = (struct pt_dev *)d;
>>      uint32_t old_ebase = assigned_device->bases[i].e_physbase;
>> +    uint32_t msix_last_pfn = 0, bar_last_pfn = 0;
>>      int first_map = ( assigned_device->bases[i].e_size == 0 );
>>      int ret = 0;
>>
>> @@ -1118,39 +1120,124 @@ static void pt_iomem_map(PCIDevice *d, int i,
>> uint32_t e_phys, uint32_t e_size,
>>
>>      if ( !first_map && old_ebase != -1 )
>>      {
>> -        add_msix_mapping(assigned_device, i);
>> -        /* Remove old mapping */
>> -        ret = xc_domain_memory_mapping(xc_handle, domid,
>> +        if ( has_msix_mapping(assigned_device, i) )
>> +        {
>> +            msix_last_pfn = (assigned_device->msix->mmio_base_addr - 1 +
>> +                  assigned_device->msix->total_entries * 16) >> 
>>  XC_PAGE_SHIFT;
>> +            bar_last_pfn = (old_ebase + e_size - 1) >> XC_PAGE_SHIFT;
>> +
>> +            if ( assigned_device->msix->table_off )
>> +            {
>> +                     ret = xc_domain_memory_mapping(xc_handle, domid,
>> +                    old_ebase >> XC_PAGE_SHIFT,
>> +                    assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT,
>> +                    (assigned_device->msix->mmio_base_addr >> XC_PAGE_SHIFT)
>> +                    - (old_ebase >> XC_PAGE_SHIFT),
>> +                    DPCI_REMOVE_MAPPING);
>> +                if ( ret != 0 )
>> +                {
>> +                    PT_LOG("Error: remove old mapping failed!\n");
>> +                    return;
>> +                }
>> +            }
>> +            if ( msix_last_pfn != bar_last_pfn )
>> +            {
>> +                assert(msix_last_pfn < bar_last_pfn);
>> +                     ret = xc_domain_memory_mapping(xc_handle, domid,
>> +                    msix_last_pfn + 1,
>> +                    (assigned_device->bases[i].access.maddr +
>> +                     assigned_device->msix->table_off +
>> +                     assigned_device->msix->total_entries * 16 +
>> +                     XC_PAGE_SIZE -1) >>  XC_PAGE_SHIFT,
>> +                    bar_last_pfn - msix_last_pfn,
>> +                    DPCI_REMOVE_MAPPING);
>> +                if ( ret != 0 )
>> +                {
>> +                    PT_LOG("Error: remove old mapping failed!\n");
>> +                    return;
>> +                }
>> +            }
>> +        }
>> +        else
>> +        {
>> +                 /* Remove old mapping */
>> +                 ret = xc_domain_memory_mapping(xc_handle, domid,
>>                  old_ebase >> XC_PAGE_SHIFT,
>>                  assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT,
>>                  (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT,
>>                  DPCI_REMOVE_MAPPING);
>> -        if ( ret != 0 )
>> -        {
>> -            PT_LOG("Error: remove old mapping failed!\n");
>> -            return;
>> +            if ( ret != 0 )
>> +            {
>> +                PT_LOG("Error: remove old mapping failed!\n");
>> +                return;
>> +            }
>>          }
>>      }
>>
>>      /* map only valid guest address */
>>      if (e_phys != -1)
>>      {
>> -        /* Create new mapping */
>> -        ret = xc_domain_memory_mapping(xc_handle, domid,
>> +        if ( has_msix_mapping(assigned_device, i) )
>> +             {
>> +            assigned_device->msix->mmio_base_addr =
>> +                assigned_device->bases[i].e_physbase
>> +                + assigned_device->msix->table_off;
>> +
>> +            msix_last_pfn = (assigned_device->msix->mmio_base_addr - 1 +
>> +                  assigned_device->msix->total_entries * 16) >> 
>>  XC_PAGE_SHIFT;
>> +            bar_last_pfn = (e_phys + e_size - 1) >> XC_PAGE_SHIFT;
>> +
>> +           
>>  cpu_register_physical_memory(assigned_device->msix->mmio_base_addr,
>> +                                 assigned_device->msix->total_entries * 16,
>> +                                 assigned_device->msix->mmio_index);
>> +
>> +            if ( assigned_device->msix->table_off )
>> +            {
>> +                     ret = xc_domain_memory_mapping(xc_handle, domid,
>> +                    assigned_device->bases[i].e_physbase >> XC_PAGE_SHIFT,
>> +                    assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT,
>> +                    (assigned_device->msix->mmio_base_addr >> XC_PAGE_SHIFT)
>> +                    - (assigned_device->bases[i].e_physbase >> 
>> XC_PAGE_SHIFT),
>> +                    DPCI_ADD_MAPPING);
>> +                if ( ret != 0 )
>> +                {
>> +                    PT_LOG("Error: remove old mapping failed!\n");
>> +                    return;
>> +                }
>> +            }
>> +            if ( msix_last_pfn != bar_last_pfn )
>> +            {
>> +                assert(msix_last_pfn < bar_last_pfn);
>> +                     ret = xc_domain_memory_mapping(xc_handle, domid,
>> +                    msix_last_pfn + 1,
>> +                    (assigned_device->bases[i].access.maddr +
>> +                     assigned_device->msix->table_off +
>> +                     assigned_device->msix->total_entries * 16 +
>> +                     XC_PAGE_SIZE -1) >>  XC_PAGE_SHIFT,
>> +                    bar_last_pfn - msix_last_pfn,
>> +                    DPCI_ADD_MAPPING);
>> +                if ( ret != 0 )
>> +                {
>> +                    PT_LOG("Error: remove old mapping failed!\n");
>> +                    return;
>> +                }
>> +            }
>> +             }
>> +             else
>> +        {
>> +                     /* Create new mapping */
>> +                     ret = xc_domain_memory_mapping(xc_handle, domid,
>>                  assigned_device->bases[i].e_physbase >> XC_PAGE_SHIFT,
>>                  assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT,
>>                  (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT,
>>                  DPCI_ADD_MAPPING);
>>
>> -        if ( ret != 0 )
>> -        {
>> -            PT_LOG("Error: create new mapping failed!\n");
>> +            if ( ret != 0 )
>> +            {
>> +                PT_LOG("Error: create new mapping failed!\n");
>> +            }
>>          }
>>
>> -        ret = remove_msix_mapping(assigned_device, i);
>> -        if ( ret != 0 )
>> -            PT_LOG("Error: remove MSI-X mmio mapping failed!\n");
>> -
>>          if ( old_ebase != e_phys && old_ebase != -1 )
>>              pt_msix_update_remap(assigned_device, i);
>>      }
>> diff --git a/hw/pt-msi.c b/hw/pt-msi.c
>> index 71fa6f0..1fbebd4 100644
>> --- a/hw/pt-msi.c
>> +++ b/hw/pt-msi.c
>> @@ -528,39 +528,12 @@ static CPUReadMemoryFunc *pci_msix_read[] = {
>>      pci_msix_readl
>>  };
>>
>> -int add_msix_mapping(struct pt_dev *dev, int bar_index)
>> +int has_msix_mapping(struct pt_dev *dev, int bar_index)
>>  {
>>      if ( !(dev->msix && dev->msix->bar_index == bar_index) )
>>          return 0;
>>
>> -    return xc_domain_memory_mapping(xc_handle, domid,
>> -                dev->msix->mmio_base_addr >> XC_PAGE_SHIFT,
>> -                (dev->bases[bar_index].access.maddr
>> -                + dev->msix->table_off) >> XC_PAGE_SHIFT,
>> -                (dev->msix->total_entries * 16
>> -                + XC_PAGE_SIZE -1) >> XC_PAGE_SHIFT,
>> -                DPCI_ADD_MAPPING);
>> -}
>> -
>> -int remove_msix_mapping(struct pt_dev *dev, int bar_index)
>> -{
>> -    if ( !(dev->msix && dev->msix->bar_index == bar_index) )
>> -        return 0;
>> -
>> -    dev->msix->mmio_base_addr = dev->bases[bar_index].e_physbase
>> -                                + dev->msix->table_off;
>> -
>> -    cpu_register_physical_memory(dev->msix->mmio_base_addr,
>> -                                 dev->msix->total_entries * 16,
>> -                                 dev->msix->mmio_index);
>> -
>> -    return xc_domain_memory_mapping(xc_handle, domid,
>> -                dev->msix->mmio_base_addr >> XC_PAGE_SHIFT,
>> -                (dev->bases[bar_index].access.maddr
>> -                + dev->msix->table_off) >> XC_PAGE_SHIFT,
>> -                (dev->msix->total_entries * 16
>> -                + XC_PAGE_SIZE -1) >> XC_PAGE_SHIFT,
>> -                DPCI_REMOVE_MAPPING);
>> +     return 1;
>>  }
>>
>>  int pt_msix_init(struct pt_dev *dev, int pos)
>> diff --git a/hw/pt-msi.h b/hw/pt-msi.h
>> index 9664f89..2dc1720 100644
>> --- a/hw/pt-msi.h
>> +++ b/hw/pt-msi.h
>> @@ -107,10 +107,7 @@ void
>>  pt_msix_disable(struct pt_dev *dev);
>>
>>  int
>> -remove_msix_mapping(struct pt_dev *dev, int bar_index);
>> -
>> -int
>> -add_msix_mapping(struct pt_dev *dev, int bar_index);
>> +has_msix_mapping(struct pt_dev *dev, int bar_index);
>>
>>  int
>>  pt_msix_init(struct pt_dev *dev, int pos);
>
>
>

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.