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: 1f86a00c1159 ("bus/fsl-mc: add support for 'driver_override' in the 
mc-bus")
Signed-off-by: Danilo Krummrich <[email protected]>
---
 drivers/bus/fsl-mc/fsl-mc-bus.c   | 43 +++++--------------------------
 drivers/vfio/fsl-mc/vfio_fsl_mc.c |  4 +--
 include/linux/fsl/mc.h            |  4 ---
 3 files changed, 8 insertions(+), 43 deletions(-)

diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index c117745cf206..221146e4860b 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -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()
         */
diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc.c 
b/drivers/vfio/fsl-mc/vfio_fsl_mc.c
index 462fae1aa538..b4c3958201b2 100644
--- a/drivers/vfio/fsl-mc/vfio_fsl_mc.c
+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c
@@ -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
diff --git a/include/linux/fsl/mc.h b/include/linux/fsl/mc.h
index 897d6211c163..1da63f2d7040 100644
--- a/include/linux/fsl/mc.h
+++ b/include/linux/fsl/mc.h
@@ -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) \
-- 
2.53.0


Reply via email to