Commit 6c8dfb03 authored by Danilo Krummrich's avatar Danilo Krummrich
Browse files

bus: fsl-mc: 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]

Tested-by: default avatarIoana Ciornei <ioana.ciornei@nxp.com>
Acked-by: default avatarIoana Ciornei <ioana.ciornei@nxp.com>
Acked-by: default avatarChristophe Leroy (CS GROUP) <chleroy@kernel.org>
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: 1f86a00c ("bus/fsl-mc: add support for 'driver_override' in the mc-bus")
Link: https://patch.msgid.link/20260324005919.2408620-3-dakr@kernel.org


Signed-off-by: default avatarDanilo Krummrich <dakr@kernel.org>
parent 81d6f7c3
Loading
Loading
Loading
Loading
+7 −36
Original line number Diff line number Diff line
@@ -86,12 +86,16 @@ static int fsl_mc_bus_match(struct device *dev, const struct device_driver *drv)
	struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
	const struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
	bool found = false;
	int ret;

	/* When driver_override is set, only bind to the matching driver */
	if (mc_dev->driver_override) {
		found = !strcmp(mc_dev->driver_override, mc_drv->driver.name);
	ret = device_match_driver_override(dev, drv);
	if (ret > 0) {
		found = true;
		goto out;
	}
	if (ret == 0)
		goto out;

	if (!mc_drv->match_id_table)
		goto out;
@@ -210,39 +214,8 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(modalias);

static ssize_t driver_override_store(struct device *dev,
				     struct device_attribute *attr,
				     const char *buf, size_t count)
{
	struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
	int ret;

	if (WARN_ON(dev->bus != &fsl_mc_bus_type))
		return -EINVAL;

	ret = driver_set_override(dev, &mc_dev->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 fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
	ssize_t len;

	device_lock(dev);
	len = sysfs_emit(buf, "%s\n", mc_dev->driver_override);
	device_unlock(dev);
	return len;
}
static DEVICE_ATTR_RW(driver_override);

static struct attribute *fsl_mc_dev_attrs[] = {
	&dev_attr_modalias.attr,
	&dev_attr_driver_override.attr,
	NULL,
};

@@ -345,6 +318,7 @@ ATTRIBUTE_GROUPS(fsl_mc_bus);

const struct bus_type fsl_mc_bus_type = {
	.name = "fsl-mc",
	.driver_override = true,
	.match = fsl_mc_bus_match,
	.uevent = fsl_mc_bus_uevent,
	.probe = fsl_mc_probe,
@@ -910,9 +884,6 @@ static struct notifier_block fsl_mc_nb;
 */
void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
{
	kfree(mc_dev->driver_override);
	mc_dev->driver_override = NULL;

	/*
	 * The device-specific remove callback will get invoked by device_del()
	 */
+1 −3
Original line number Diff line number Diff line
@@ -424,9 +424,7 @@ static int vfio_fsl_mc_bus_notifier(struct notifier_block *nb,

	if (action == BUS_NOTIFY_ADD_DEVICE &&
	    vdev->mc_dev == mc_cont) {
		mc_dev->driver_override = kasprintf(GFP_KERNEL, "%s",
						    vfio_fsl_mc_ops.name);
		if (!mc_dev->driver_override)
		if (device_set_driver_override(dev, vfio_fsl_mc_ops.name))
			dev_warn(dev, "VFIO_FSL_MC: Setting driver override for device in dprc %s failed\n",
				 dev_name(&mc_cont->dev));
		else
+0 −4
Original line number Diff line number Diff line
@@ -178,9 +178,6 @@ struct fsl_mc_obj_desc {
 * @regions: pointer to array of MMIO region entries
 * @irqs: pointer to array of pointers to interrupts allocated to this device
 * @resource: generic resource associated with this MC object device, if any.
 * @driver_override: driver name to force a match; do not set directly,
 *                   because core frees it; use driver_set_override() to
 *                   set or clear it.
 *
 * Generic device object for MC object devices that are "attached" to a
 * MC bus.
@@ -214,7 +211,6 @@ struct fsl_mc_device {
	struct fsl_mc_device_irq **irqs;
	struct fsl_mc_resource *resource;
	struct device_link *consumer_link;
	const char *driver_override;
};

#define to_fsl_mc_device(_dev) \