On Tue, Mar 24, 2026 at 9:00 AM Danilo Krummrich <[email protected]> wrote: > > When a driver is probed through __driver_attach(), the bus' match() > callback is called without the device lock held, thus accessing the > driver_override field without a lock, which can cause a UAF. > > Fix this by using the driver-core driver_override infrastructure taking > care of proper locking internally. > > Note that calling match() from __driver_attach() without the device lock > held is intentional. [1] > > Link: > https://lore.kernel.org/driver-core/[email protected]/ [1] > Reported-by: Gui-Dong Han <[email protected]> > Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 > Fixes: 782a985d7af2 ("PCI: Introduce new device binding path using > pci_dev.driver_override") > Signed-off-by: Danilo Krummrich <[email protected]>
Tested on QEMU PCI with multiple debug configs enabled. The original PoCs run cleanly without triggering the issue. Thanks Danilo. Tested-by: Gui-Dong Han <[email protected]> Reviewed-by: Gui-Dong Han <[email protected]> > --- > drivers/pci/pci-driver.c | 11 +++++++---- > drivers/pci/pci-sysfs.c | 28 ---------------------------- > drivers/pci/probe.c | 1 - > drivers/vfio/pci/vfio_pci_core.c | 5 ++--- > drivers/xen/xen-pciback/pci_stub.c | 6 ++++-- > include/linux/pci.h | 6 ------ > 6 files changed, 13 insertions(+), 44 deletions(-) > > diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c > index dd9075403987..d10ece0889f0 100644 > --- a/drivers/pci/pci-driver.c > +++ b/drivers/pci/pci-driver.c > @@ -138,9 +138,11 @@ 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, *ids; > + int ret; > > /* When driver_override is set, only bind to the matching driver */ > - if (dev->driver_override && strcmp(dev->driver_override, drv->name)) > + ret = device_match_driver_override(&dev->dev, &drv->driver); > + if (ret == 0) > return NULL; > > /* Look at the dynamic ids first, before the static ones */ > @@ -164,7 +166,7 @@ static const struct pci_device_id > *pci_match_device(struct pci_driver *drv, > * matching. > */ > if (found_id->override_only) { > - if (dev->driver_override) > + if (ret > 0) > return found_id; > } else { > return found_id; > @@ -172,7 +174,7 @@ static const struct pci_device_id > *pci_match_device(struct pci_driver *drv, > } > > /* driver_override will always match, send a dummy id */ > - if (dev->driver_override) > + if (ret > 0) > return &pci_device_id_any; > return NULL; > } > @@ -452,7 +454,7 @@ static int __pci_device_probe(struct pci_driver *drv, > struct pci_dev *pci_dev) > static inline bool pci_device_can_probe(struct pci_dev *pdev) > { > return (!pdev->is_virtfn || pdev->physfn->sriov->drivers_autoprobe || > - pdev->driver_override); > + device_has_driver_override(&pdev->dev)); > } > #else > static inline bool pci_device_can_probe(struct pci_dev *pdev) > @@ -1722,6 +1724,7 @@ static const struct cpumask > *pci_device_irq_get_affinity(struct device *dev, > > const struct bus_type pci_bus_type = { > .name = "pci", > + .driver_override = true, > .match = pci_bus_match, > .uevent = pci_uevent, > .probe = pci_device_probe, > diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c > index 16eaaf749ba9..a9006cf4e9c8 100644 > --- a/drivers/pci/pci-sysfs.c > +++ b/drivers/pci/pci-sysfs.c > @@ -615,33 +615,6 @@ static ssize_t devspec_show(struct device *dev, > static DEVICE_ATTR_RO(devspec); > #endif > > -static ssize_t driver_override_store(struct device *dev, > - struct device_attribute *attr, > - const char *buf, size_t count) > -{ > - struct pci_dev *pdev = to_pci_dev(dev); > - int ret; > - > - ret = driver_set_override(dev, &pdev->driver_override, buf, count); > - if (ret) > - return ret; > - > - return count; > -} > - > -static ssize_t driver_override_show(struct device *dev, > - struct device_attribute *attr, char *buf) > -{ > - struct pci_dev *pdev = to_pci_dev(dev); > - ssize_t len; > - > - device_lock(dev); > - len = sysfs_emit(buf, "%s\n", pdev->driver_override); > - device_unlock(dev); > - return len; > -} > -static DEVICE_ATTR_RW(driver_override); > - > static struct attribute *pci_dev_attrs[] = { > &dev_attr_power_state.attr, > &dev_attr_resource.attr, > @@ -669,7 +642,6 @@ static struct attribute *pci_dev_attrs[] = { > #ifdef CONFIG_OF > &dev_attr_devspec.attr, > #endif > - &dev_attr_driver_override.attr, > &dev_attr_ari_enabled.attr, > NULL, > }; > diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c > index bccc7a4bdd79..b4707640e102 100644 > --- a/drivers/pci/probe.c > +++ b/drivers/pci/probe.c > @@ -2488,7 +2488,6 @@ static void pci_release_dev(struct device *dev) > pci_release_of_node(pci_dev); > pcibios_release_device(pci_dev); > pci_bus_put(pci_dev->bus); > - kfree(pci_dev->driver_override); > bitmap_free(pci_dev->dma_alias_mask); > dev_dbg(dev, "device released\n"); > kfree(pci_dev); > diff --git a/drivers/vfio/pci/vfio_pci_core.c > b/drivers/vfio/pci/vfio_pci_core.c > index d43745fe4c84..460852f79f29 100644 > --- a/drivers/vfio/pci/vfio_pci_core.c > +++ b/drivers/vfio/pci/vfio_pci_core.c > @@ -1987,9 +1987,8 @@ static int vfio_pci_bus_notifier(struct notifier_block > *nb, > pdev->is_virtfn && physfn == vdev->pdev) { > pci_info(vdev->pdev, "Captured SR-IOV VF %s > driver_override\n", > pci_name(pdev)); > - pdev->driver_override = kasprintf(GFP_KERNEL, "%s", > - vdev->vdev.ops->name); > - WARN_ON(!pdev->driver_override); > + WARN_ON(device_set_driver_override(&pdev->dev, > + vdev->vdev.ops->name)); > } else if (action == BUS_NOTIFY_BOUND_DRIVER && > pdev->is_virtfn && physfn == vdev->pdev) { > struct pci_driver *drv = pci_dev_driver(pdev); > diff --git a/drivers/xen/xen-pciback/pci_stub.c > b/drivers/xen/xen-pciback/pci_stub.c > index e4b27aecbf05..79a2b5dfd694 100644 > --- a/drivers/xen/xen-pciback/pci_stub.c > +++ b/drivers/xen/xen-pciback/pci_stub.c > @@ -598,6 +598,8 @@ static int pcistub_seize(struct pci_dev *dev, > return err; > } > > +static struct pci_driver xen_pcibk_pci_driver; > + > /* Called when 'bind'. This means we must _NOT_ call pci_reset_function or > * other functions that take the sysfs lock. */ > static int pcistub_probe(struct pci_dev *dev, const struct pci_device_id *id) > @@ -609,8 +611,8 @@ static int pcistub_probe(struct pci_dev *dev, const > struct pci_device_id *id) > > match = pcistub_match(dev); > > - if ((dev->driver_override && > - !strcmp(dev->driver_override, PCISTUB_DRIVER_NAME)) || > + if (device_match_driver_override(&dev->dev, > + &xen_pcibk_pci_driver.driver) > 0 || > match) { > > if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL > diff --git a/include/linux/pci.h b/include/linux/pci.h > index 1c270f1d5123..57e9463e4347 100644 > --- a/include/linux/pci.h > +++ b/include/linux/pci.h > @@ -575,12 +575,6 @@ struct pci_dev { > u8 supported_speeds; /* Supported Link Speeds Vector */ > phys_addr_t rom; /* Physical address if not from BAR */ > size_t romlen; /* Length if not from BAR */ > - /* > - * Driver name to force a match. Do not set directly, because core > - * frees it. Use driver_set_override() to set or clear it. > - */ > - const char *driver_override; > - > unsigned long priv_flags; /* Private flags for the PCI driver */ > > /* These methods index pci_reset_fn_methods[] */ > -- > 2.53.0 >

