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/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1]
Reported-by: Gui-Dong Han <hanguidong02@gmail.com>
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 <dakr@kernel.org>
---
arch/sh/drivers/platform_early.c | 6 ++++--
drivers/base/platform.c | 37 +++++---------------------------
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(+), 50 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..d44591d52e36 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,38 +1305,9 @@ 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);
-
static struct attribute *platform_dev_attrs[] = {
&dev_attr_modalias.attr,
&dev_attr_numa_node.attr,
- &dev_attr_driver_override.attr,
NULL,
};
@@ -1377,10 +1347,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))
@@ -1516,6 +1488,7 @@ static const struct dev_pm_ops platform_dev_pm_ops = {
const struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
+ .driver_override = true,
.match = platform_match,
.uevent = platform_uevent,
.probe = platform_probe,
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
(Cc: Bartosz)
On Tue Mar 3, 2026 at 12:53 PM CET, Danilo Krummrich wrote:
> 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)
I was just about to pick up this series, but then noticed that checking for
driver_override in the platform_early case doesn't make sense in the first place
and was accidentally added when the platform_match() callback was copied over in
commit 507fd01d5333 ("drivers: move the early platform device support to
arch/sh").
Thus, I'm going to drop this hunk and add in the following patch; please let me
know if there are any concerns.
commit 39cae4095efda4b00b436c0fc46f21de84128969
Author: Danilo Krummrich <dakr@kernel.org>
Date: Tue Mar 17 00:37:15 2026 +0100
sh: platform_early: remove pdev->driver_override check
In commit 507fd01d5333 ("drivers: move the early platform device support to
arch/sh") platform_match() was copied over to the sh platform_early
code, accidentally including the driver_override check.
This check does not make sense for platform_early, as sysfs is not even
available in first place at this point in the boot process, hence remove
the check.
Fixes: 507fd01d5333 ("drivers: move the early platform device support to arch/sh")
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
diff --git a/arch/sh/drivers/platform_early.c b/arch/sh/drivers/platform_early.c
index 143747c45206..48ddbc547bd9 100644
--- a/arch/sh/drivers/platform_early.c
+++ b/arch/sh/drivers/platform_early.c
@@ -26,10 +26,6 @@ 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);
- /* When driver_override is set, only bind to the matching driver */
- if (pdev->driver_override)
- return !strcmp(pdev->driver_override, drv->name);
-
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
Hi Danilo,
On Tue, 17 Mar 2026 at 00:57, Danilo Krummrich <dakr@kernel.org> wrote:
> On Tue Mar 3, 2026 at 12:53 PM CET, Danilo Krummrich wrote:
> > 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)
>
> I was just about to pick up this series, but then noticed that checking for
> driver_override in the platform_early case doesn't make sense in the first place
> and was accidentally added when the platform_match() callback was copied over in
> commit 507fd01d5333 ("drivers: move the early platform device support to
> arch/sh").
>
> Thus, I'm going to drop this hunk and add in the following patch; please let me
> know if there are any concerns.
>
> commit 39cae4095efda4b00b436c0fc46f21de84128969
> Author: Danilo Krummrich <dakr@kernel.org>
> Date: Tue Mar 17 00:37:15 2026 +0100
>
> sh: platform_early: remove pdev->driver_override check
>
> In commit 507fd01d5333 ("drivers: move the early platform device support to
> arch/sh") platform_match() was copied over to the sh platform_early
> code, accidentally including the driver_override check.
>
> This check does not make sense for platform_early, as sysfs is not even
> available in first place at this point in the boot process, hence remove
> the check.
>
> Fixes: 507fd01d5333 ("drivers: move the early platform device support to arch/sh")
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>
> diff --git a/arch/sh/drivers/platform_early.c b/arch/sh/drivers/platform_early.c
> index 143747c45206..48ddbc547bd9 100644
> --- a/arch/sh/drivers/platform_early.c
> +++ b/arch/sh/drivers/platform_early.c
> @@ -26,10 +26,6 @@ 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);
>
> - /* When driver_override is set, only bind to the matching driver */
> - if (pdev->driver_override)
> - return !strcmp(pdev->driver_override, drv->name);
> -
> /* Then try to match against the id table */
> if (pdrv->id_table)
> return platform_match_id(pdrv->id_table, pdev) != NULL;
Nice catch!
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
On Tue, Mar 17, 2026 at 12:56:52AM +0100, Danilo Krummrich wrote:
> (Cc: Bartosz)
>
> On Tue Mar 3, 2026 at 12:53 PM CET, Danilo Krummrich wrote:
> > 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)
>
> I was just about to pick up this series, but then noticed that checking for
> driver_override in the platform_early case doesn't make sense in the first place
> and was accidentally added when the platform_match() callback was copied over in
> commit 507fd01d5333 ("drivers: move the early platform device support to
> arch/sh").
>
> Thus, I'm going to drop this hunk and add in the following patch; please let me
> know if there are any concerns.
>
> commit 39cae4095efda4b00b436c0fc46f21de84128969
> Author: Danilo Krummrich <dakr@kernel.org>
> Date: Tue Mar 17 00:37:15 2026 +0100
>
> sh: platform_early: remove pdev->driver_override check
>
> In commit 507fd01d5333 ("drivers: move the early platform device support to
> arch/sh") platform_match() was copied over to the sh platform_early
> code, accidentally including the driver_override check.
>
> This check does not make sense for platform_early, as sysfs is not even
> available in first place at this point in the boot process, hence remove
> the check.
>
> Fixes: 507fd01d5333 ("drivers: move the early platform device support to arch/sh")
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>
> diff --git a/arch/sh/drivers/platform_early.c b/arch/sh/drivers/platform_early.c
> index 143747c45206..48ddbc547bd9 100644
> --- a/arch/sh/drivers/platform_early.c
> +++ b/arch/sh/drivers/platform_early.c
> @@ -26,10 +26,6 @@ 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);
>
> - /* When driver_override is set, only bind to the matching driver */
> - if (pdev->driver_override)
> - return !strcmp(pdev->driver_override, drv->name);
> -
> /* Then try to match against the id table */
> if (pdrv->id_table)
> return platform_match_id(pdrv->id_table, pdev) != NULL;
>
>
Makes sense to me, good catch!
Reviewed--by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
On Tue Mar 3, 2026 at 12:53 PM CET, Danilo Krummrich wrote: > arch/sh/drivers/platform_early.c | 6 ++++-- > drivers/clk/imx/clk-scu.c | 3 +-- > drivers/slimbus/qcom-ngd-ctrl.c | 6 ++---- > sound/soc/samsung/i2s.c | 6 +++--- Some ACKs would be appreciated. :)
On Thu Mar 5, 2026 at 1:42 PM CET, Danilo Krummrich wrote: > On Tue Mar 3, 2026 at 12:53 PM CET, Danilo Krummrich wrote: >> arch/sh/drivers/platform_early.c | 6 ++++-- >> drivers/clk/imx/clk-scu.c | 3 +-- >> drivers/slimbus/qcom-ngd-ctrl.c | 6 ++---- >> sound/soc/samsung/i2s.c | 6 +++--- > > Some ACKs would be appreciated. :) Looking forward to pick this up soon, please let me know if there are any concerns. Thanks, Danilo
From: Frank Li (AI-BOT) <frank.li@nxp.com>
AI bot review and may be useless.
> @@ -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;
Good: centralizes locking logic. Verify device_match_driver_override()
handles -1 return for "not set" case consistently across all callers.
> @@ -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);
Correct: driver_override now managed by generic infrastructure, not
platform_device. Ensure device_release() in driver-core handles cleanup.
> -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;
> -}
Good: removes duplicate sysfs attribute. Verify generic implementation
in device-core provides equivalent locking and sysfs_emit() behavior.
> @@ -1377,10 +1347,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;
Same pattern as arch/sh. Consistent refactoring across platform code.
> + .driver_override = true,
Verify this flag is documented in struct bus_type definition and that
all bus_type users expecting driver_override support have this set.
> - if (pdev->driver_override)
> + if (device_has_driver_override(&pdev->dev))
Good: accessor function prevents direct field access. Confirm
device_has_driver_override() is properly exported and inlined where
needed for performance.
> - 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");
Good: removes strlen() call; generic function should handle length
internally. Verify no callers relied on old signature.
> - pdev_sec->driver_override = kstrdup("samsung-i2s", GFP_KERNEL);
> - if (!pdev_sec->driver_override) {
> + ret = device_set_driver_override(&pdev_sec->dev, "samsung
© 2016 - 2026 Red Hat, Inc.