Skip to content

Commit d723091

Browse files
committed
Merge tag 'driver-core-7.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core
Pull driver core fixes from Danilo Krummrich: - Generalize driver_override in the driver core, providing a common sysfs implementation and concurrency-safe accessors for bus implementations - Do not use driver_override as IRQ name in the hwmon axi-fan driver - Remove an unnecessary driver_override check in sh platform_early - Migrate the platform bus to use the generic driver_override infrastructure, fixing a UAF condition caused by accessing the driver_override field without proper locking in the platform_match() callback * tag 'driver-core-7.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: driver core: platform: use generic driver_override infrastructure sh: platform_early: remove pdev->driver_override check hwmon: axi-fan: don't use driver_override as IRQ name docs: driver-model: document driver_override driver core: generalize driver_override in struct device
2 parents 113ae7b + 2b38efc commit d723091

14 files changed

Lines changed: 224 additions & 54 deletions

File tree

Documentation/driver-api/driver-model/binding.rst

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,51 @@ of the driver is decremented. All symlinks between the two are removed.
9999
When a driver is removed, the list of devices that it supports is
100100
iterated over, and the driver's remove callback is called for each
101101
one. The device is removed from that list and the symlinks removed.
102+
103+
104+
Driver Override
105+
~~~~~~~~~~~~~~~
106+
107+
Userspace may override the standard matching by writing a driver name to
108+
a device's ``driver_override`` sysfs attribute. When set, only a driver
109+
whose name matches the override will be considered during binding. This
110+
bypasses all bus-specific matching (OF, ACPI, ID tables, etc.).
111+
112+
The override may be cleared by writing an empty string, which returns
113+
the device to standard matching rules. Writing to ``driver_override``
114+
does not automatically unbind the device from its current driver or
115+
make any attempt to load the specified driver.
116+
117+
Buses opt into this mechanism by setting the ``driver_override`` flag in
118+
their ``struct bus_type``::
119+
120+
const struct bus_type example_bus_type = {
121+
...
122+
.driver_override = true,
123+
};
124+
125+
When the flag is set, the driver core automatically creates the
126+
``driver_override`` sysfs attribute for every device on that bus.
127+
128+
The bus's ``match()`` callback should check the override before performing
129+
its own matching, using ``device_match_driver_override()``::
130+
131+
static int example_match(struct device *dev, const struct device_driver *drv)
132+
{
133+
int ret;
134+
135+
ret = device_match_driver_override(dev, drv);
136+
if (ret >= 0)
137+
return ret;
138+
139+
/* Fall through to bus-specific matching... */
140+
}
141+
142+
``device_match_driver_override()`` returns > 0 if the override matches
143+
the given driver, 0 if the override is set but does not match, or < 0 if
144+
no override is set at all.
145+
146+
Additional helpers are available:
147+
148+
- ``device_set_driver_override()`` - set or clear the override from kernel code.
149+
- ``device_has_driver_override()`` - check whether an override is set.

arch/sh/drivers/platform_early.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ static int platform_match(struct device *dev, struct device_driver *drv)
2626
struct platform_device *pdev = to_platform_device(dev);
2727
struct platform_driver *pdrv = to_platform_driver(drv);
2828

29-
/* When driver_override is set, only bind to the matching driver */
30-
if (pdev->driver_override)
31-
return !strcmp(pdev->driver_override, drv->name);
32-
3329
/* Then try to match against the id table */
3430
if (pdrv->id_table)
3531
return platform_match_id(pdrv->id_table, pdev) != NULL;

drivers/base/bus.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,36 @@ int bus_for_each_drv(const struct bus_type *bus, struct device_driver *start,
504504
}
505505
EXPORT_SYMBOL_GPL(bus_for_each_drv);
506506

507+
static ssize_t driver_override_store(struct device *dev,
508+
struct device_attribute *attr,
509+
const char *buf, size_t count)
510+
{
511+
int ret;
512+
513+
ret = __device_set_driver_override(dev, buf, count);
514+
if (ret)
515+
return ret;
516+
517+
return count;
518+
}
519+
520+
static ssize_t driver_override_show(struct device *dev,
521+
struct device_attribute *attr, char *buf)
522+
{
523+
guard(spinlock)(&dev->driver_override.lock);
524+
return sysfs_emit(buf, "%s\n", dev->driver_override.name);
525+
}
526+
static DEVICE_ATTR_RW(driver_override);
527+
528+
static struct attribute *driver_override_dev_attrs[] = {
529+
&dev_attr_driver_override.attr,
530+
NULL,
531+
};
532+
533+
static const struct attribute_group driver_override_dev_group = {
534+
.attrs = driver_override_dev_attrs,
535+
};
536+
507537
/**
508538
* bus_add_device - add device to bus
509539
* @dev: device being added
@@ -537,9 +567,15 @@ int bus_add_device(struct device *dev)
537567
if (error)
538568
goto out_put;
539569

570+
if (dev->bus->driver_override) {
571+
error = device_add_group(dev, &driver_override_dev_group);
572+
if (error)
573+
goto out_groups;
574+
}
575+
540576
error = sysfs_create_link(&sp->devices_kset->kobj, &dev->kobj, dev_name(dev));
541577
if (error)
542-
goto out_groups;
578+
goto out_override;
543579

544580
error = sysfs_create_link(&dev->kobj, &sp->subsys.kobj, "subsystem");
545581
if (error)
@@ -550,6 +586,9 @@ int bus_add_device(struct device *dev)
550586

551587
out_subsys:
552588
sysfs_remove_link(&sp->devices_kset->kobj, dev_name(dev));
589+
out_override:
590+
if (dev->bus->driver_override)
591+
device_remove_group(dev, &driver_override_dev_group);
553592
out_groups:
554593
device_remove_groups(dev, sp->bus->dev_groups);
555594
out_put:
@@ -607,6 +646,8 @@ void bus_remove_device(struct device *dev)
607646

608647
sysfs_remove_link(&dev->kobj, "subsystem");
609648
sysfs_remove_link(&sp->devices_kset->kobj, dev_name(dev));
649+
if (dev->bus->driver_override)
650+
device_remove_group(dev, &driver_override_dev_group);
610651
device_remove_groups(dev, dev->bus->dev_groups);
611652
if (klist_node_attached(&dev->p->knode_bus))
612653
klist_del(&dev->p->knode_bus);

drivers/base/core.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2556,6 +2556,7 @@ static void device_release(struct kobject *kobj)
25562556
devres_release_all(dev);
25572557

25582558
kfree(dev->dma_range_map);
2559+
kfree(dev->driver_override.name);
25592560

25602561
if (dev->release)
25612562
dev->release(dev);
@@ -3159,6 +3160,7 @@ void device_initialize(struct device *dev)
31593160
kobject_init(&dev->kobj, &device_ktype);
31603161
INIT_LIST_HEAD(&dev->dma_pools);
31613162
mutex_init(&dev->mutex);
3163+
spin_lock_init(&dev->driver_override.lock);
31623164
lockdep_set_novalidate_class(&dev->mutex);
31633165
spin_lock_init(&dev->devres_lock);
31643166
INIT_LIST_HEAD(&dev->devres_head);

drivers/base/dd.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,66 @@ static void __exit deferred_probe_exit(void)
381381
}
382382
__exitcall(deferred_probe_exit);
383383

384+
int __device_set_driver_override(struct device *dev, const char *s, size_t len)
385+
{
386+
const char *new, *old;
387+
char *cp;
388+
389+
if (!s)
390+
return -EINVAL;
391+
392+
/*
393+
* The stored value will be used in sysfs show callback (sysfs_emit()),
394+
* which has a length limit of PAGE_SIZE and adds a trailing newline.
395+
* Thus we can store one character less to avoid truncation during sysfs
396+
* show.
397+
*/
398+
if (len >= (PAGE_SIZE - 1))
399+
return -EINVAL;
400+
401+
/*
402+
* Compute the real length of the string in case userspace sends us a
403+
* bunch of \0 characters like python likes to do.
404+
*/
405+
len = strlen(s);
406+
407+
if (!len) {
408+
/* Empty string passed - clear override */
409+
spin_lock(&dev->driver_override.lock);
410+
old = dev->driver_override.name;
411+
dev->driver_override.name = NULL;
412+
spin_unlock(&dev->driver_override.lock);
413+
kfree(old);
414+
415+
return 0;
416+
}
417+
418+
cp = strnchr(s, len, '\n');
419+
if (cp)
420+
len = cp - s;
421+
422+
new = kstrndup(s, len, GFP_KERNEL);
423+
if (!new)
424+
return -ENOMEM;
425+
426+
spin_lock(&dev->driver_override.lock);
427+
old = dev->driver_override.name;
428+
if (cp != s) {
429+
dev->driver_override.name = new;
430+
spin_unlock(&dev->driver_override.lock);
431+
} else {
432+
/* "\n" passed - clear override */
433+
dev->driver_override.name = NULL;
434+
spin_unlock(&dev->driver_override.lock);
435+
436+
kfree(new);
437+
}
438+
kfree(old);
439+
440+
return 0;
441+
}
442+
EXPORT_SYMBOL_GPL(__device_set_driver_override);
443+
384444
/**
385445
* device_is_bound() - Check if device is bound to a driver
386446
* @dev: device to check

drivers/base/platform.c

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,6 @@ static void platform_device_release(struct device *dev)
603603
kfree(pa->pdev.dev.platform_data);
604604
kfree(pa->pdev.mfd_cell);
605605
kfree(pa->pdev.resource);
606-
kfree(pa->pdev.driver_override);
607606
kfree(pa);
608607
}
609608

@@ -1306,38 +1305,9 @@ static ssize_t numa_node_show(struct device *dev,
13061305
}
13071306
static DEVICE_ATTR_RO(numa_node);
13081307

1309-
static ssize_t driver_override_show(struct device *dev,
1310-
struct device_attribute *attr, char *buf)
1311-
{
1312-
struct platform_device *pdev = to_platform_device(dev);
1313-
ssize_t len;
1314-
1315-
device_lock(dev);
1316-
len = sysfs_emit(buf, "%s\n", pdev->driver_override);
1317-
device_unlock(dev);
1318-
1319-
return len;
1320-
}
1321-
1322-
static ssize_t driver_override_store(struct device *dev,
1323-
struct device_attribute *attr,
1324-
const char *buf, size_t count)
1325-
{
1326-
struct platform_device *pdev = to_platform_device(dev);
1327-
int ret;
1328-
1329-
ret = driver_set_override(dev, &pdev->driver_override, buf, count);
1330-
if (ret)
1331-
return ret;
1332-
1333-
return count;
1334-
}
1335-
static DEVICE_ATTR_RW(driver_override);
1336-
13371308
static struct attribute *platform_dev_attrs[] = {
13381309
&dev_attr_modalias.attr,
13391310
&dev_attr_numa_node.attr,
1340-
&dev_attr_driver_override.attr,
13411311
NULL,
13421312
};
13431313

@@ -1377,10 +1347,12 @@ static int platform_match(struct device *dev, const struct device_driver *drv)
13771347
{
13781348
struct platform_device *pdev = to_platform_device(dev);
13791349
struct platform_driver *pdrv = to_platform_driver(drv);
1350+
int ret;
13801351

13811352
/* When driver_override is set, only bind to the matching driver */
1382-
if (pdev->driver_override)
1383-
return !strcmp(pdev->driver_override, drv->name);
1353+
ret = device_match_driver_override(dev, drv);
1354+
if (ret >= 0)
1355+
return ret;
13841356

13851357
/* Attempt an OF style match first */
13861358
if (of_driver_match_device(dev, drv))
@@ -1516,6 +1488,7 @@ static const struct dev_pm_ops platform_dev_pm_ops = {
15161488
const struct bus_type platform_bus_type = {
15171489
.name = "platform",
15181490
.dev_groups = platform_dev_groups,
1491+
.driver_override = true,
15191492
.match = platform_match,
15201493
.uevent = platform_uevent,
15211494
.probe = platform_probe,

drivers/bus/simple-pm-bus.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ static int simple_pm_bus_probe(struct platform_device *pdev)
3636
* that's not listed in simple_pm_bus_of_match. We don't want to do any
3737
* of the simple-pm-bus tasks for these devices, so return early.
3838
*/
39-
if (pdev->driver_override)
39+
if (device_has_driver_override(&pdev->dev))
4040
return 0;
4141

4242
match = of_match_device(dev->driver->of_match_table, dev);
@@ -78,7 +78,7 @@ static void simple_pm_bus_remove(struct platform_device *pdev)
7878
{
7979
const void *data = of_device_get_match_data(&pdev->dev);
8080

81-
if (pdev->driver_override || data)
81+
if (device_has_driver_override(&pdev->dev) || data)
8282
return;
8383

8484
dev_dbg(&pdev->dev, "%s\n", __func__);

drivers/clk/imx/clk-scu.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -706,8 +706,7 @@ struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
706706
if (ret)
707707
goto put_device;
708708

709-
ret = driver_set_override(&pdev->dev, &pdev->driver_override,
710-
"imx-scu-clk", strlen("imx-scu-clk"));
709+
ret = device_set_driver_override(&pdev->dev, "imx-scu-clk");
711710
if (ret)
712711
goto put_device;
713712

drivers/hwmon/axi-fan-control.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ static int axi_fan_control_probe(struct platform_device *pdev)
507507
ret = devm_request_threaded_irq(&pdev->dev, ctl->irq, NULL,
508508
axi_fan_control_irq_handler,
509509
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
510-
pdev->driver_override, ctl);
510+
NULL, ctl);
511511
if (ret)
512512
return dev_err_probe(&pdev->dev, ret,
513513
"failed to request an irq\n");

drivers/slimbus/qcom-ngd-ctrl.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,10 +1535,8 @@ static int of_qcom_slim_ngd_register(struct device *parent,
15351535
ngd->id = id;
15361536
ngd->pdev->dev.parent = parent;
15371537

1538-
ret = driver_set_override(&ngd->pdev->dev,
1539-
&ngd->pdev->driver_override,
1540-
QCOM_SLIM_NGD_DRV_NAME,
1541-
strlen(QCOM_SLIM_NGD_DRV_NAME));
1538+
ret = device_set_driver_override(&ngd->pdev->dev,
1539+
QCOM_SLIM_NGD_DRV_NAME);
15421540
if (ret) {
15431541
platform_device_put(ngd->pdev);
15441542
kfree(ngd);

0 commit comments

Comments
 (0)