Commit 10a4206a authored by Danilo Krummrich's avatar Danilo Krummrich
Browse files

PCI: use generic driver_override infrastructure

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/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/

 [1]
Reported-by: default avatarGui-Dong Han <hanguidong02@gmail.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789


Fixes: 782a985d ("PCI: Introduce new device binding path using pci_dev.driver_override")
Acked-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Acked-by: default avatarAlex Williamson <alex@shazbot.org>
Tested-by: default avatarGui-Dong Han <hanguidong02@gmail.com>
Reviewed-by: default avatarGui-Dong Han <hanguidong02@gmail.com>
Link: https://patch.msgid.link/20260324005919.2408620-6-dakr@kernel.org


Signed-off-by: default avatarDanilo Krummrich <dakr@kernel.org>
parent 1cf996ac
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
@@ -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,
+0 −28
Original line number Diff line number Diff line
@@ -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,
};
+0 −1
Original line number Diff line number Diff line
@@ -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);
+2 −3
Original line number Diff line number Diff line
@@ -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);
+4 −2
Original line number Diff line number Diff line
@@ -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
Loading