[PATCH 09/11] PCI: add matching checks for driver_override binding

From: Max Gurtovoy
Date: Thu Jun 03 2021 - 12:09:14 EST


Allowing any driver in the system to be unconditionally bound to any
PCI HW is dangerous. Connecting a driver to a physical HW device it was
never intended to operate may trigger exploitable kernel bugs, or worse.
It also allows userspace to load and run kernel code that otherwise
would never be runnable on the system.

driver_override was designed to make it easier to load vfio_pci, so
focus it on that single use case. driver_override will only work on
drivers that specifically opt into this feature and the driver now has
the opportunity to provide a proper match table that indicates what HW
it can properly support. vfio-pci continues to support everything.

This also causes the modalias tables to be populated with dedicated
match table and userspace now becomes aware that vfio-pci can be loaded
against any PCI device using driver_override.

Signed-off-by: Max Gurtovoy <mgurtovoy@xxxxxxxxxx>
---
Documentation/ABI/testing/sysfs-bus-pci | 6 +++++-
drivers/pci/pci-driver.c | 22 ++++++++++++----------
drivers/vfio/pci/vfio_pci.c | 9 ++++++++-
3 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
index ef00fada2efb..6d78970b1c69 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -289,7 +289,11 @@ Description:
will not bind to any driver. This also allows devices to
opt-out of driver binding using a driver_override name such as
"none". Only a single driver may be specified in the override,
- there is no support for parsing delimiters.
+ there is no support for parsing delimiters. The binding to the
+ device is allowed if and only if the matching driver has
+ enabled the driver_override alias in the ID table (by setting
+ a suitable bit in the "flags" field of pci_device_id
+ structure).

What: /sys/bus/pci/devices/.../numa_node
Date: Oct 2014
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index ec44a79e951a..e4037db817da 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -115,13 +115,6 @@ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
}
EXPORT_SYMBOL(pci_match_id);

-static const struct pci_device_id pci_device_id_any = {
- .vendor = PCI_ANY_ID,
- .device = PCI_ANY_ID,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
-};
-
/**
* pci_match_device - See if a device matches a driver's list of IDs
* @drv: the PCI driver to match against
@@ -137,6 +130,7 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
{
struct pci_dynid *dynid;
const struct pci_device_id *found_id = NULL;
+ bool is_driver_override;

/* When driver_override is set, only bind to the matching driver */
if (dev->driver_override && strcmp(dev->driver_override, drv->name))
@@ -155,9 +149,17 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
if (!found_id)
found_id = pci_match_id(drv->id_table, dev);

- /* driver_override will always match, send a dummy id */
- if (!found_id && dev->driver_override)
- found_id = &pci_device_id_any;
+ /*
+ * if and only if PCI_ID_F_DRIVER_OVERRIDE flag is set, driver_override
+ * should be provided.
+ */
+ if (found_id) {
+ is_driver_override =
+ (found_id->flags & PCI_ID_F_DRIVER_OVERRIDE) != 0;
+ if ((is_driver_override && !dev->driver_override) ||
+ (dev->driver_override && !is_driver_override))
+ return NULL;
+ }

return found_id;
}
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 850ea3a94e28..9beb4b841945 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -166,9 +166,16 @@ static int vfio_pci_sriov_configure(struct pci_dev *pdev, int nr_virtfn)
return vfio_pci_core_sriov_configure(pdev, nr_virtfn);
}

+static const struct pci_device_id vfio_pci_table[] = {
+ { PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_ANY_ID, PCI_ANY_ID) }, /* match all by default */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, vfio_pci_table);
+
static struct pci_driver vfio_pci_driver = {
.name = "vfio-pci",
- .id_table = NULL, /* only dynamic ids */
+ .id_table = vfio_pci_table,
.probe = vfio_pci_probe,
.remove = vfio_pci_remove,
.sriov_configure = vfio_pci_sriov_configure,
--
2.21.0