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: 12046f8c77e0 ("platform/x86: wmi: Add driver_override support")
Signed-off-by: Danilo Krummrich <[email protected]>
---
 drivers/platform/wmi/core.c | 36 +++++-------------------------------
 include/linux/wmi.h         |  4 ----
 2 files changed, 5 insertions(+), 35 deletions(-)

diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c
index b8e6b9a421c6..750e3619724e 100644
--- a/drivers/platform/wmi/core.c
+++ b/drivers/platform/wmi/core.c
@@ -842,39 +842,11 @@ static ssize_t expensive_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(expensive);
 
-static ssize_t driver_override_show(struct device *dev, struct 
device_attribute *attr,
-                                   char *buf)
-{
-       struct wmi_device *wdev = to_wmi_device(dev);
-       ssize_t ret;
-
-       device_lock(dev);
-       ret = sysfs_emit(buf, "%s\n", wdev->driver_override);
-       device_unlock(dev);
-
-       return ret;
-}
-
-static ssize_t driver_override_store(struct device *dev, struct 
device_attribute *attr,
-                                    const char *buf, size_t count)
-{
-       struct wmi_device *wdev = to_wmi_device(dev);
-       int ret;
-
-       ret = driver_set_override(dev, &wdev->driver_override, buf, count);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-static DEVICE_ATTR_RW(driver_override);
-
 static struct attribute *wmi_attrs[] = {
        &dev_attr_modalias.attr,
        &dev_attr_guid.attr,
        &dev_attr_instance_count.attr,
        &dev_attr_expensive.attr,
-       &dev_attr_driver_override.attr,
        NULL
 };
 ATTRIBUTE_GROUPS(wmi);
@@ -943,7 +915,6 @@ static void wmi_dev_release(struct device *dev)
 {
        struct wmi_block *wblock = dev_to_wblock(dev);
 
-       kfree(wblock->dev.driver_override);
        kfree(wblock);
 }
 
@@ -952,10 +923,12 @@ static int wmi_dev_match(struct device *dev, const struct 
device_driver *driver)
        const struct wmi_driver *wmi_driver = to_wmi_driver(driver);
        struct wmi_block *wblock = dev_to_wblock(dev);
        const struct wmi_device_id *id = wmi_driver->id_table;
+       int ret;
 
        /* When driver_override is set, only bind to the matching driver */
-       if (wblock->dev.driver_override)
-               return !strcmp(wblock->dev.driver_override, driver->name);
+       ret = device_match_driver_override(dev, driver);
+       if (ret >= 0)
+               return ret;
 
        if (id == NULL)
                return 0;
@@ -1076,6 +1049,7 @@ static struct class wmi_bus_class = {
 static const struct bus_type wmi_bus_type = {
        .name = "wmi",
        .dev_groups = wmi_groups,
+       .driver_override = true,
        .match = wmi_dev_match,
        .uevent = wmi_dev_uevent,
        .probe = wmi_dev_probe,
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index 75cb0c7cfe57..14fb644e1701 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -18,16 +18,12 @@
  * struct wmi_device - WMI device structure
  * @dev: Device associated with this WMI device
  * @setable: True for devices implementing the Set Control Method
- * @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.
  *
  * This represents WMI devices discovered by the WMI driver core.
  */
 struct wmi_device {
        struct device dev;
        bool setable;
-       const char *driver_override;
 };
 
 /**
-- 
2.53.0


Reply via email to