summaryrefslogtreecommitdiffstats
path: root/drivers/vfio
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2015-06-09 12:08:57 -0400
committerAlex Williamson <alex.williamson@redhat.com>2015-06-09 12:08:57 -0400
commit20f300175a1e150dae231e21dfa1fc4c6fcf4db6 (patch)
treee1ad32bedc25162d87b58fa61445b8000df6ce27 /drivers/vfio
parentd4a4f75cd8f29cd9464a5a32e9224a91571d6649 (diff)
vfio/pci: Fix racy vfio_device_get_from_dev() call
Testing the driver for a PCI device is racy, it can be all but complete in the release path and still report the driver as ours. Therefore we can't trust drvdata to be valid. This race can sometimes be seen when one port of a multifunction device is being unbound from the vfio-pci driver while another function is being released by the user and attempting a bus reset. The device in the remove path is found as a dependent device for the bus reset of the release path device, the driver is still set to vfio-pci, but the drvdata has already been cleared, resulting in a null pointer dereference. To resolve this, fix vfio_device_get_from_dev() to not take the dev_get_drvdata() shortcut and instead traverse through the iommu_group, vfio_group, vfio_device path to get a reference we can trust. Once we have that reference, we know the device isn't in transition and we can test to make sure the driver is still what we expect, so that we don't interfere with devices we don't own. Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Diffstat (limited to 'drivers/vfio')
-rw-r--r--drivers/vfio/pci/vfio_pci.c16
-rw-r--r--drivers/vfio/vfio.c27
2 files changed, 28 insertions, 15 deletions
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index e9851add6f4e..964ad572aaee 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -1056,19 +1056,21 @@ struct vfio_devices {
1056static int vfio_pci_get_devs(struct pci_dev *pdev, void *data) 1056static int vfio_pci_get_devs(struct pci_dev *pdev, void *data)
1057{ 1057{
1058 struct vfio_devices *devs = data; 1058 struct vfio_devices *devs = data;
1059 struct pci_driver *pci_drv = ACCESS_ONCE(pdev->driver); 1059 struct vfio_device *device;
1060
1061 if (pci_drv != &vfio_pci_driver)
1062 return -EBUSY;
1063 1060
1064 if (devs->cur_index == devs->max_index) 1061 if (devs->cur_index == devs->max_index)
1065 return -ENOSPC; 1062 return -ENOSPC;
1066 1063
1067 devs->devices[devs->cur_index] = vfio_device_get_from_dev(&pdev->dev); 1064 device = vfio_device_get_from_dev(&pdev->dev);
1068 if (!devs->devices[devs->cur_index]) 1065 if (!device)
1069 return -EINVAL; 1066 return -EINVAL;
1070 1067
1071 devs->cur_index++; 1068 if (pci_dev_driver(pdev) != &vfio_pci_driver) {
1069 vfio_device_put(device);
1070 return -EBUSY;
1071 }
1072
1073 devs->devices[devs->cur_index++] = device;
1072 return 0; 1074 return 0;
1073} 1075}
1074 1076
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index e1278fe04b1e..2fb29dfeffbd 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -661,18 +661,29 @@ int vfio_add_group_dev(struct device *dev,
661EXPORT_SYMBOL_GPL(vfio_add_group_dev); 661EXPORT_SYMBOL_GPL(vfio_add_group_dev);
662 662
663/** 663/**
664 * Get a reference to the vfio_device for a device that is known to 664 * Get a reference to the vfio_device for a device. Even if the
665 * be bound to a vfio driver. The driver implicitly holds a 665 * caller thinks they own the device, they could be racing with a
666 * vfio_device reference between vfio_add_group_dev and 666 * release call path, so we can't trust drvdata for the shortcut.
667 * vfio_del_group_dev. We can therefore use drvdata to increment 667 * Go the long way around, from the iommu_group to the vfio_group
668 * that reference from the struct device. This additional 668 * to the vfio_device.
669 * reference must be released by calling vfio_device_put.
670 */ 669 */
671struct vfio_device *vfio_device_get_from_dev(struct device *dev) 670struct vfio_device *vfio_device_get_from_dev(struct device *dev)
672{ 671{
673 struct vfio_device *device = dev_get_drvdata(dev); 672 struct iommu_group *iommu_group;
673 struct vfio_group *group;
674 struct vfio_device *device;
675
676 iommu_group = iommu_group_get(dev);
677 if (!iommu_group)
678 return NULL;
674 679
675 vfio_device_get(device); 680 group = vfio_group_get_from_iommu(iommu_group);
681 iommu_group_put(iommu_group);
682 if (!group)
683 return NULL;
684
685 device = vfio_group_get_device(group, dev);
686 vfio_group_put(group);
676 687
677 return device; 688 return device;
678} 689}