Re: [PATCH 3/3] driver core: platform: use generic driver_override infrastructure
Gui-Dong Han <[email protected]>
| Newsgroups | gmane.linux.sound,gmane.linux.kernel,gmane.linux.kernel.hwmon,gmane.linux.ports.arm.msm,gmane.linux.ports.sh.devel |
|---|---|
| Message-ID | <CALbr=LbNE+aviHf+EwZNohzm86skWJu=adQsHaM98vQyKUAyvQ@mail.gmail.com> |
On Mon, Mar 2, 2026 at 8:28 AM Danilo Krummrich <[email protected]> wrote: > > 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: 3d713e0e382e ("driver core: platform: add device binding path 'driver_override'") > Signed-off-by: Danilo Krummrich <[email protected]> Looks good to me. One minor note for the commit message: this UAF can also be triggered from the bind_store path, not just __driver_attach(). It would be good to mention this as well. Reviewed-by: Gui-Dong Han <[email protected]> > --- > arch/sh/drivers/platform_early.c | 6 ++++-- > drivers/base/platform.c | 35 +++++--------------------------- > drivers/bus/simple-pm-bus.c | 4 ++-- > drivers/clk/imx/clk-scu.c | 3 +-- > drivers/slimbus/qcom-ngd-ctrl.c | 6 ++---- > include/linux/platform_device.h | 5 ----- > sound/soc/samsung/i2s.c | 6 +++--- > 7 files changed, 17 insertions(+), 48 deletions(-) > > diff --git a/arch/sh/drivers/platform_early.c b/arch/sh/drivers/platform_early.c > index 143747c45206..3cd17bb0be67 100644 > --- a/arch/sh/drivers/platform_early.c > +++ b/arch/sh/drivers/platform_early.c > @@ -25,10 +25,12 @@ static int platform_match(struct device *dev, struct device_driver *drv) > { > struct platform_device *pdev = to_platform_device(dev); > struct platform_driver *pdrv = to_platform_driver(drv); > + int ret; > > /* When driver_override is set, only bind to the matching driver */ > - if (pdev->driver_override) > - return !strcmp(pdev->driver_override, drv->name); > + ret = device_match_driver_override(dev, drv); > + if (ret >= 0) > + return ret; > > /* Then try to match against the id table */ > if (pdrv->id_table) > diff --git a/drivers/base/platform.c b/drivers/base/platform.c > index b45d41b018ca..22ae87921a7a 100644 > --- a/drivers/base/platform.c > +++ b/drivers/base/platform.c > @@ -603,7 +603,6 @@ static void platform_device_release(struct device *dev) > kfree(pa->pdev.dev.platform_data); > kfree(pa->pdev.mfd_cell); > kfree(pa->pdev.resource); > - kfree(pa->pdev.driver_override); > kfree(pa); > } > > @@ -1306,33 +1305,7 @@ static ssize_t numa_node_show(struct device *dev, > } > static DEVICE_ATTR_RO(numa_node); > > -static ssize_t driver_override_show(struct device *dev, > - struct device_attribute *attr, char *buf) > -{ > - struct platform_device *pdev = to_platform_device(dev); > - ssize_t len; > - > - device_lock(dev); > - len = sysfs_emit(buf, "%s\n", pdev->driver_override); > - device_unlock(dev); > - > - return len; > -} > - > -static ssize_t driver_override_store(struct device *dev, > - struct device_attribute *attr, > - const char *buf, size_t count) > -{ > - struct platform_device *pdev = to_platform_device(dev); > - int ret; > - > - ret = driver_set_override(dev, &pdev->driver_override, buf, count); > - if (ret) > - return ret; > - > - return count; > -} > -static DEVICE_ATTR_RW(driver_override); > +DEVICE_ATTR_DRIVER_OVERRIDE(); > > static struct attribute *platform_dev_attrs[] = { > &dev_attr_modalias.attr, > @@ -1377,10 +1350,12 @@ static int platform_match(struct device *dev, const struct device_driver *drv) > { > struct platform_device *pdev = to_platform_device(dev); > struct platform_driver *pdrv = to_platform_driver(drv); > + int ret; > > /* When driver_override is set, only bind to the matching driver */ > - if (pdev->driver_override) > - return !strcmp(pdev->driver_override, drv->name); > + ret = device_match_driver_override(dev, drv); > + if (ret >= 0) > + return ret; > > /* Attempt an OF style match first */ > if (of_driver_match_device(dev, drv)) > diff --git a/drivers/bus/simple-pm-bus.c b/drivers/bus/simple-pm-bus.c > index 3f00d953fb9a..c920bd6fbaaf 100644 > --- a/drivers/bus/simple-pm-bus.c > +++ b/drivers/bus/simple-pm-bus.c > @@ -36,7 +36,7 @@ static int simple_pm_bus_probe(struct platform_device *pdev) > * that's not listed in simple_pm_bus_of_match. We don't want to do any > * of the simple-pm-bus tasks for these devices, so return early. > */ > - if (pdev->driver_override) > + if (device_has_driver_override(&pdev->dev)) > return 0; > > match = of_match_device(dev->driver->of_match_table, dev); > @@ -78,7 +78,7 @@ static void simple_pm_bus_remove(struct platform_device *pdev) > { > const void *data = of_device_get_match_data(&pdev->dev); > > - if (pdev->driver_override || data) > + if (device_has_driver_override(&pdev->dev) || data) > return; > > dev_dbg(&pdev->dev, "%s\n", __func__); > diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c > index a85ec48a798b..9b33df9967ec 100644 > --- a/drivers/clk/imx/clk-scu.c > +++ b/drivers/clk/imx/clk-scu.c > @@ -706,8 +706,7 @@ struct clk_hw *imx_clk_scu_alloc_dev(const char *name, > if (ret) > goto put_device; > > - ret = driver_set_override(&pdev->dev, &pdev->driver_override, > - "imx-scu-clk", strlen("imx-scu-clk")); > + ret = device_set_driver_override(&pdev->dev, "imx-scu-clk"); > if (ret) > goto put_device; > > diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c > index 9aa7218b4e8d..1ed6be6e85d2 100644 > --- a/drivers/slimbus/qcom-ngd-ctrl.c > +++ b/drivers/slimbus/qcom-ngd-ctrl.c > @@ -1535,10 +1535,8 @@ static int of_qcom_slim_ngd_register(struct device *parent, > ngd->id = id; > ngd->pdev->dev.parent = parent; > > - ret = driver_set_override(&ngd->pdev->dev, > - &ngd->pdev->driver_override, > - QCOM_SLIM_NGD_DRV_NAME, > - strlen(QCOM_SLIM_NGD_DRV_NAME)); > + ret = device_set_driver_override(&ngd->pdev->dev, > + QCOM_SLIM_NGD_DRV_NAME); > if (ret) { > platform_device_put(ngd->pdev); > kfree(ngd); > diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h > index 813da101b5bf..ed1d50d1c3c1 100644 > --- a/include/linux/platform_device.h > +++ b/include/linux/platform_device.h > @@ -31,11 +31,6 @@ struct platform_device { > struct resource *resource; > > const struct platform_device_id *id_entry; > - /* > - * 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; > > /* MFD cell pointer */ > struct mfd_cell *mfd_cell; > diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c > index e9964f0e010a..140907a41a70 100644 > --- a/sound/soc/samsung/i2s.c > +++ b/sound/soc/samsung/i2s.c > @@ -1360,10 +1360,10 @@ static int i2s_create_secondary_device(struct samsung_i2s_priv *priv) > if (!pdev_sec) > return -ENOMEM; > > - pdev_sec->driver_override = kstrdup("samsung-i2s", GFP_KERNEL); > - if (!pdev_sec->driver_override) { > + ret = device_set_driver_override(&pdev_sec->dev, "samsung-i2s"); > + if (ret) { > platform_device_put(pdev_sec); > - return -ENOMEM; > + return ret; > } > > ret = platform_device_add(pdev_sec); > -- > 2.53.0 >