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: ebc3d1791503 ("s390/cio: introduce driver_override on the css bus")
Signed-off-by: Danilo Krummrich <[email protected]>
---
 drivers/s390/cio/cio.h |  5 -----
 drivers/s390/cio/css.c | 34 ++++------------------------------
 2 files changed, 4 insertions(+), 35 deletions(-)

diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 08a5e9380e75..bad142c536e1 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -103,11 +103,6 @@ struct subchannel {
        struct work_struct todo_work;
        struct schib_config config;
        u64 dma_mask;
-       /*
-        * 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;
 } __attribute__ ((aligned(8)));
 
 DECLARE_PER_CPU_ALIGNED(struct irb, cio_irb);
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 5ab239f38588..e5a0ec6b4e3e 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -159,7 +159,6 @@ static void css_subchannel_release(struct device *dev)
 
        sch->config.intparm = 0;
        cio_commit_config(sch);
-       kfree(sch->driver_override);
        kfree(sch);
 }
 
@@ -323,37 +322,9 @@ 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 subchannel *sch = to_subchannel(dev);
-       int ret;
-
-       ret = driver_set_override(dev, &sch->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 subchannel *sch = to_subchannel(dev);
-       ssize_t len;
-
-       device_lock(dev);
-       len = sysfs_emit(buf, "%s\n", sch->driver_override);
-       device_unlock(dev);
-       return len;
-}
-static DEVICE_ATTR_RW(driver_override);
-
 static struct attribute *subch_attrs[] = {
        &dev_attr_type.attr,
        &dev_attr_modalias.attr,
-       &dev_attr_driver_override.attr,
        NULL,
 };
 
@@ -1356,9 +1327,11 @@ static int css_bus_match(struct device *dev, const 
struct device_driver *drv)
        struct subchannel *sch = to_subchannel(dev);
        const struct css_driver *driver = to_cssdriver(drv);
        struct css_device_id *id;
+       int ret;
 
        /* When driver_override is set, only bind to the matching driver */
-       if (sch->driver_override && strcmp(sch->driver_override, drv->name))
+       ret = device_match_driver_override(dev, drv);
+       if (ret == 0)
                return 0;
 
        for (id = driver->subchannel_type; id->match_flags; id++) {
@@ -1415,6 +1388,7 @@ static int css_uevent(const struct device *dev, struct 
kobj_uevent_env *env)
 
 static const struct bus_type css_bus_type = {
        .name     = "css",
+       .driver_override = true,
        .match    = css_bus_match,
        .probe    = css_probe,
        .remove   = css_remove,
-- 
2.53.0


Reply via email to