driver core: parent lock fixes
Subsystems (3)
Johan Hovold
maintainerv2 cover driver core: parent lock fixes
Mario Peter reported that USB interfaces may be probed without holding the parent USB device lock during asynchronous and deferred probe.
This series addresses both cases, while fixing a long-standing use-after-free issue with the parent lock implementation stemming from the fact that the driver core does not necessarily hold a reference to the parent device when taking its lock.
Johan
Changes in v2:
- fix the parent lock UAF issue (new patch)
- include my update of Mario's fix as it also depends on the UAF fix [1]
[1] https://lore.kernel.org/all/[email protected]/
Johan Hovold (2):
driver core: fix parent lock use-after-free on USB disconnect driver core: take parent lock on deferred probe
Mario Peter (1):
driver core: take parent lock in async device attach
drivers/base/base.h | 1 + drivers/base/bus.c | 32 +++++++++---- drivers/base/dd.c | 92 +++++++++++++++++++++----------------- drivers/usb/core/driver.c | 1 - drivers/usb/core/message.c | 1 + include/linux/device.h | 5 +++ include/linux/device/bus.h | 3 -- 7 files changed, 81 insertions(+), 54 deletions(-)
Signature · 1 line
2.55.0
Johan Hovold
maintainerPATCH v2 1/3 driver core: fix parent lock use-after-free on USB disconnect
USB interfaces should be probed with their parent USB device lock held and the USB bus therefore sets the need_parent_lock bus flag so that driver core takes the parent lock when needed.
Driver core is only guaranteed to hold a reference to the parent device during initial probe however and nothing prevents the device and its parent from being deregistered and the parent potentially freed, for example, while iterating over devices in driver_attach() or bus_rescan_devices_helper().
Since commit 524134d42231 ("USB: don't cancel queued resets when unbinding drivers") USB interfaces hold a reference to their parent USB device, but a parent hub may still go away while probing a USB device (e.g. via the drivers_probe sysfs interface). This can lead to a use-after-free when taking the parent device lock before checking the device->p->dead flag of the device being probed.
As only USB interfaces actually need the parent lock, make the need_parent_lock feature even more fine grained by making it a device type rather than bus feature. By requiring that any users also make sure that their devices hold a reference to their parents (like USB does), driver core can lock a device and its parent without first checking if the device has been deregistered.
Note that this will also increase parallelism when using asynchronous probe as USB devices connected to the same hub can now be probed in parallel.
--- drivers/base/bus.c | 4 ++-- drivers/base/dd.c | 4 ++-- drivers/usb/core/driver.c | 1 - drivers/usb/core/message.c | 1 + include/linux/device.h | 5 +++++ include/linux/device/bus.h | 3 --- 6 files changed, 10 insertions(+), 8 deletions(-)
drivers/base/bus.c +2 -2
@@ -852,10 +852,10 @@ static int __must_check bus_rescan_devices_helper(struct device *dev,int ret =0 ;if (!dev->driver) { -if (dev->parent && dev->bus->need_parent_lock) +if (dev->type && dev->type->need_parent_lock)device_lock (dev->parent); ret =device_attach (dev); -if (dev->parent && dev->bus->need_parent_lock) +if (dev->type && dev->type->need_parent_lock)device_unlock (dev->parent); }return ret <0 ? ret :0 ;
drivers/base/dd.c +2 -2
@@ -1166,7 +1166,7 @@ void device_initial_probe(struct device *dev)*/ static void __device_driver_lock (struct device *dev,struct device *parent) { -if (parent && dev->bus->need_parent_lock) +if (dev->type && dev->type->need_parent_lock && parent)device_lock (parent);device_lock (dev); }
@@ -1183,7 +1183,7 @@ static void __device_driver_lock(struct device *dev, struct device *parent)static void __device_driver_unlock (struct device *dev,struct device *parent) {device_unlock (dev); -if (parent && dev->bus->need_parent_lock) +if (dev->type && dev->type->need_parent_lock && parent)device_unlock (parent); }
drivers/usb/core/driver.c +0 -1
@@ -2081,5 +2081,4 @@ const struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .uevent = usb_uevent, - .need_parent_lock = true, };
drivers/usb/core/message.c +1 -0
@@ -1922,6 +1922,7 @@ const struct device_type usb_if_device_type = { .name = "usb_interface", .release = usb_release_interface, .uevent = usb_if_uevent, + .need_parent_lock = true, }; static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
include/linux/device.h +5 -0
@@ -83,6 +83,10 @@ int subsys_virtual_register(const struct bus_type *subsys,* information, equivalent to the kobj_type of a kobject. * If "name" is specified, the uevent will contain it in * the DEVTYPE variable. +* +* @need_parent_lock: When probing or removing a device of this type, +* driver core should lock the device's parent. Note that +* the device must hold a reference to its parent. */ struct device_type {const char *name;
@@ -93,6 +97,7 @@ struct device_type {void (*release)(struct device *dev);const struct dev_pm_ops *pm; +bool need_parent_lock; };/**
include/linux/device/bus.h +0 -3
@@ -68,8 +68,6 @@ struct fwnode_handle;* @driver_override: Set to true if this bus supports the driver_override * mechanism, which allows userspace to force a specific * driver to bind to a device via a sysfs attribute. -* @need_parent_lock: When probing or removing a device on this bus, the -* device core should lock the device's parent. * * A bus is a channel between the processor and one or more devices. For the * purposes of the device model, all devices are connected via a bus, even if
@@ -110,7 +108,6 @@ struct bus_type {const struct dev_pm_ops *pm;bool driver_override; -bool need_parent_lock; };int __must_check bus_register (const struct bus_type *bus);
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v2 1/3] driver core: fix parent lock use-after-free on USB disconnect
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 15 lines
USB interfaces should be probed with their parent USB device lock held and the USB bus therefore sets the
need_parent_lockbus flag so that driver core takes the parent lock when needed.Driver core is only guaranteed to hold a reference to the parent device during initial probe however and nothing prevents the device and its parent from being deregistered and the parent potentially freed, for example, while iterating over devices in
driver_attach()orbus_rescan_devices_helper().Since commit
524134d42231("USB: don't cancel queued resets when unbinding drivers") USB interfaces hold a reference to their parent USB device, but a parent hub may still go away while probing a USB device (e.g. via thedrivers_probesysfs interface). This can lead to a use-after-free when taking the parent device lock before checking the
Quoted 6 lines
[ ... ]Fixes bf74ad5bc417 ("[PATCH] Hold the device's parent's lock during probe and remove")Cc [email protected] # 2.6.16: 524134d42231: USB: don't cancel queued resets when unbinding driversCc [email protected] # 2.6.16Cc Alan Stern <[email protected]>Signed-off-by Johan Hovold <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Danilo Krummrich
maintainerRe: [PATCH v2 1/3] driver core: fix parent lock use-after-free on USB disconnect
In reply to Johan HovoldQuoted 3 lines · Johan Hovold
+ * @need_parent_lock: When probing or removing a device of this type,
+ * driver core should lock the device's parent. Note that
+ * the device must hold a reference to its parent.
For the reasons described in [1], I object to making subsystems responsible for the parent reference needed by driver core's delayed accesses. The driver core should acquire and release that reference internally.
Re: [PATCH v2 1/3] driver core: fix parent lock use-after-free on USB disconnect
Quoted 10 lines · Danilo Krummrich
Quoted 3 lines · Johan Hovold
+ * @need_parent_lock: When probing or removing a device of this type,
+ * driver core should lock the device's parent. Note that
+ * the device must hold a reference to its parent.For the reasons described in [1], I object to making subsystems responsible for the parent reference needed by driver core's delayed accesses. The driver core should acquire and release that reference internally.
When is the appropriate time to take the reference? Since it is going to be dropped when the child device is released, the logical time to take it is when the child device is initialized. But at that time, the caller may not have set the bus_type or device_type.
Taking the reference when the child is added would work. But then how would the release routine know whether or not to drop the reference? Add an extra flag bit to keep track of this?
Alan Stern
Johan Hovold
maintainerPATCH v2 2/3 driver core: take parent lock on deferred probe
In reply to Johan HovoldUSB interfaces should be probed with the parent USB device lock held, but this is not honoured when probe is deferred.
Add a new bus_probe_device_deferred() helper to handle deferred probe where the parent device lock is not held.
--- drivers/base/base.h | 1 + drivers/base/bus.c | 28 +++++++++++++++++++++------- drivers/base/dd.c | 7 ++++--- 3 files changed, 26 insertions(+), 10 deletions(-)
drivers/base/base.h +1 -0
@@ -154,6 +154,7 @@ struct kobject *virtual_device_parent(void);int bus_add_device (struct device *dev);void bus_probe_device (struct device *dev); +void bus_probe_device_deferred (struct device *dev);void bus_remove_device (struct device *dev);void bus_notify (struct device *dev,enum bus_notifier_event value);bool bus_is_registered (const struct bus_type *bus);
drivers/base/bus.c +21 -7
@@ -606,13 +606,7 @@ int bus_add_device(struct device *dev)return error; } -/** -* bus_probe_device - probe drivers for a new device -* @dev: device to probe -* -* - Automatically probe for a driver if the bus allows it. -*/ -void bus_probe_device (struct device *dev) +static void __bus_probe_device (struct device *dev) {struct subsys_private *sp =bus_to_subsys (dev->bus);struct subsys_interface *sif;
@@ -630,6 +624,26 @@ void bus_probe_device(struct device *dev)subsys_put (sp); } +/** +* bus_probe_device - probe drivers for a new device +* @dev: device to probe +* +* - Automatically probe for a driver if the bus allows it. +*/ +void bus_probe_device (struct device *dev) +{ +__bus_probe_device (dev); +} + +void bus_probe_device_deferred (struct device *dev) +{ +if (dev->type && dev->type->need_parent_lock) +device_lock (dev->parent); +__bus_probe_device (dev); +if (dev->type && dev->type->need_parent_lock) +device_unlock (dev->parent); +} +/** * bus_remove_device - remove device from bus * @dev: device to be removed
drivers/base/dd.c +4 -3
@@ -86,8 +86,9 @@ static void deferred_probe_work_func(struct work_struct *work)/* * This block processes every device in the deferred 'active' list. * Each device is removed from the active list and passed to -* bus_probe_device() to re-attempt the probe. The loop continues -* until every device in the active list is removed and retried. +* bus_probe_device_deferred() to re-attempt the probe. The loop +* continues until every device in the active list is removed and +* retried. * * Note: Once the device is removed from the list and the mutex is * released, it is possible for the device get freed by another thread
@@ -121,7 +122,7 @@ static void deferred_probe_work_func(struct work_struct *work)device_pm_move_to_tail (dev);dev_dbg (dev,"Retrying from deferred list\n" ); -bus_probe_device (dev); +bus_probe_device_deferred (dev);mutex_lock (&deferred_probe_mutex);put_device (dev);
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v2 2/3] driver core: take parent lock on deferred probe
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] driver core: UAF on dev->parent in bus_probe_device_deferred() --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] driver core: UAF on dev->parent in bus_probe_device_deferred()
--
commit 768aedc18e0fe23bb1bfe38cd117e5b5a7a2fa43
Author: Johan Hovold <[email protected]>
driver core: take parent lock on deferred probe
This commit adds a new bus_probe_device_deferred() helper to handle deferred probing while ensuring the parent device lock is taken when required. This ensures USB interfaces are probed correctly with the parent lock held.
Quoted 4 lines
drivers/base/bus.c +0
-0
[ ... ]
Quoted 1 line
drivers/base/bus.c +0
-0@@ -627,6 +621,26 @@ void bus_probe_device(struct device *dev)
[ ... ]
drivers/base/bus.c void bus_probe_device(struct device *dev) · line 641
Quoted 4 lines
drivers/base/bus.c +4
-0+void bus_probe_device_deferred (struct device *dev) +{ +if (dev->type && dev->type->need_parent_lock) +device_lock (dev->parent);
[Severity: High]
Can this dereference a freed parent pointer?
In drivers/base/dd.c:deferred_probe_work_func(), the code dequeues the device and keeps it alive with get_device(), but drops the deferred_probe_mutex.
If a concurrent device teardown (like a parent device unplug) calls device_del(dev) while the mutex is dropped, device_del() completes and drops the parent device reference. If that was the last reference, the parent is freed. Pinning the child via get_device(dev) does not keep the parent alive after device_del() has completed.
When the worker thread resumes, it calls bus_probe_device_deferred(dev), which executes device_lock(dev->parent) here before checking dev->p->dead. Could this lead to a Use-After-Free regression?
Note that the follow-up patch 3/3 does not fix this regression, but actually propagates the same pattern to __device_attach_async_helper() via the newly introduced __device_driver_lock(dev, dev->parent).
Quoted 4 lines
drivers/base/bus.c +4
-0+__bus_probe_device (dev); +if (dev->type && dev->type->need_parent_lock) +device_unlock (dev->parent); +}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Johan Hovold
maintainerPATCH v2 3/3 driver core: take parent lock in async device attach
In reply to Johan HovoldFrom: Mario Peter <[email protected]>
On buses with need_parent_lock set (only USB), probe() must run with the parent device locked. A synchronous attach after device_add() gets this from the caller, e.g. usb_set_configuration() holds the udev lock while adding interfaces. __device_attach_async_helper() runs the probe from an async worker instead, where that lock isn't held, and only takes device_lock(dev).
With async probing enabled for the hub driver (e.g. driver_async_probe=* or driver_async_probe=hub), this races when a multi-TT hub is configured. For multi-TT hubs, hub_probe() calls usb_set_interface(hdev, 0, 1) to select the TT-per-port altsetting, and usb_set_interface() creates the endpoint devices of the interface a second time:
usb_set_configuration() async worker
[udev locked]
device_add(intf)
queues the probe ----------> __device_attach_async_helper()
[intf locked, udev not locked]
hub_probe()
usb_set_interface(hdev, 0, 1)
create_intf_ep_devs(intf) create_intf_ep_devs(intf)create_intf_ep_devs() checks and sets intf->ep_devs_created without a lock of its own. If usb_set_configuration() has created ep_81 but not yet set the flag, usb_set_interface() skips removing the endpoint devices of the old altsetting and creates ep_81 again. With a synchronous probe, hub_probe() runs inside device_add() with the udev lock held, and usb_set_configuration() then finds ep_devs_created already set.
Single-TT hubs don't call usb_set_interface() from hub_probe() and don't show this warning, but their probe runs without the udev lock as well.
On an i.MX8MM board with an onboard USB2514 hub this hit 11 of 100 boots:
sysfs: cannot create duplicate filename '.../1-1/1-1:1.0/ep_81'
Call trace: sysfs_warn_dup usb_create_ep_devs create_intf_ep_devs usb_set_interface hub_probe usb_probe_interface really_probe __device_attach_async_helper async_run_entry_fn
It also shows on v7.3-rc6 in QEMU, with dummy_hcd and a raw-gadget program emulating a multi-TT hub: re-authorizing the hub in a loop, while a SCHED_FIFO task preempts the writing task at random points for 3 ms, hit the warning in 29 of 6000 iterations.
Take the parent lock in __device_attach_async_helper() as well, like __driver_attach_async_helper() does, and move __device_driver_lock/unlock() up for that. With this the warning was gone in 300 boots on the board and in 6000 iterations in QEMU.
[ johan: switch to async_schedule_dev_nocall() to avoid deadlock if
async_schedule_dev() runs synchronously when parent lock is held ]
--- drivers/base/dd.c | 85 ++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 38 deletions(-)
drivers/base/dd.c +47 -38
@@ -1030,6 +1030,38 @@ static int __device_attach_driver(struct device_driver *drv, void *_data)return ret ==0 ; } +/* +* __device_driver_lock - acquire locks needed to manipulate dev->drv +* @dev: Device we will update driver info for +* @parent: Parent device. Needed if the bus requires parent lock +* +* This function will take the required locks for manipulating dev->drv. +* Normally this will just be the @dev lock, but when called for a USB +* interface, @parent lock will be held as well. +*/ +static void __device_driver_lock (struct device *dev,struct device *parent) +{ +if (dev->type && dev->type->need_parent_lock && parent) +device_lock (parent); +device_lock (dev); +} + +/* +* __device_driver_unlock - release locks needed to manipulate dev->drv +* @dev: Device we will update driver info for +* @parent: Parent device. Needed if the bus requires parent lock +* +* This function will release the required locks for manipulating dev->drv. +* Normally this will just be the @dev lock, but when called for a +* USB interface, @parent lock will be released as well. +*/ +static void __device_driver_unlock (struct device *dev,struct device *parent) +{ +device_unlock (dev); +if (dev->type && dev->type->need_parent_lock && parent) +device_unlock (parent); +} +static void __device_attach_async_helper (void *_dev,async_cookie_t cookie) {struct device *dev = _dev;
@@ -1039,7 +1071,7 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie) .want_async = true, }; -device_lock (dev); +__device_driver_lock (dev, dev->parent);/* * Check if device has already been removed or claimed. This may
@@ -1061,7 +1093,7 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)if (dev->parent)pm_runtime_put (dev->parent); out_unlock: -device_unlock (dev); +__device_driver_unlock (dev, dev->parent);put_device (dev); }
@@ -1118,8 +1150,13 @@ static int __device_attach(struct device *dev, bool allow_async) } out_unlock:device_unlock (dev); -if (async) -async_schedule_dev (__device_attach_async_helper, dev); +if (async) { +if (!async_schedule_dev_nocall (__device_attach_async_helper, dev)) { +put_device (dev); + ret =2 ;/* retry synchronously */ + } + } +return ret; }
@@ -1146,48 +1183,20 @@ EXPORT_SYMBOL_GPL(device_attach);void device_initial_probe (struct device *dev) {struct subsys_private *sp =bus_to_subsys (dev->bus); +int ret;if (!sp)return ; -if (sp->drivers_autoprobe) -__device_attach (dev, true); +if (sp->drivers_autoprobe) { + ret =__device_attach (dev, true); +if (ret ==2 ) +__device_attach (dev, false); + }subsys_put (sp); } -/* -* __device_driver_lock - acquire locks needed to manipulate dev->drv -* @dev: Device we will update driver info for -* @parent: Parent device. Needed if the bus requires parent lock -* -* This function will take the required locks for manipulating dev->drv. -* Normally this will just be the @dev lock, but when called for a USB -* interface, @parent lock will be held as well. -*/ -static void __device_driver_lock (struct device *dev,struct device *parent) -{ -if (dev->type && dev->type->need_parent_lock && parent) -device_lock (parent); -device_lock (dev); -} - -/* -* __device_driver_unlock - release locks needed to manipulate dev->drv -* @dev: Device we will update driver info for -* @parent: Parent device. Needed if the bus requires parent lock -* -* This function will release the required locks for manipulating dev->drv. -* Normally this will just be the @dev lock, but when called for a -* USB interface, @parent lock will be released as well. -*/ -static void __device_driver_unlock (struct device *dev,struct device *parent) -{ -device_unlock (dev); -if (dev->type && dev->type->need_parent_lock && parent) -device_unlock (parent); -} -/** * device_driver_attach - attach a specific driver to a specific device * @drv: Driver to attach
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v2 3/3] driver core: take parent lock in async device attach
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 18 lines
From: Mario Peter <[email protected]>
On buses with
need_parent_lockset (only USB),probe()must run with the parent device locked. A synchronous attach afterdevice_add()gets this from the caller, e.g.usb_set_configuration()holds the udev lock while adding interfaces.__device_attach_async_helper()runs the probe from an async worker instead, where that lock isn't held, and only takesdevice_lock(dev).With async probing enabled for the hub driver (e.g. driver_async_probe=* or driver_async_probe=hub), this races when a multi-TT hub is configured. For multi-TT hubs,
hub_probe()callsusb_set_interface(hdev, 0, 1)to select the TT-per-port altsetting, andusb_set_interface()creates the endpoint devices of the interface a second time:
[ ... ]
async_schedule_dev()runs synchronously when parent lock is held ]
Quoted 3 lines
Cc [email protected] # 4.2Cc Dmitry Torokhov <[email protected]>Signed-off-by Johan Hovold <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
Re: [PATCH v2 3/3] driver core: take parent lock in async device attach
In reply to Johan HovoldQuoted 72 lines · Johan Hovold
From: Mario Peter <[email protected]>
On buses with
need_parent_lockset (only USB),probe()must run with the parent device locked. A synchronous attach afterdevice_add()gets this from the caller, e.g.usb_set_configuration()holds the udev lock while adding interfaces.__device_attach_async_helper()runs the probe from an async worker instead, where that lock isn't held, and only takesdevice_lock(dev).With async probing enabled for the hub driver (e.g. driver_async_probe=* or driver_async_probe=hub), this races when a multi-TT hub is configured. For multi-TT hubs,
hub_probe()callsusb_set_interface(hdev, 0, 1)to select the TT-per-port altsetting, andusb_set_interface()creates the endpoint devices of the interface a second time:
usb_set_configuration()async worker[udev locked] device_add(intf) queues the probe ----------> __device_attach_async_helper() [intf locked, udev not locked] hub_probe() usb_set_interface(hdev, 0, 1) create_intf_ep_devs(intf) create_intf_ep_devs(intf)
create_intf_ep_devs()checks and setsintf->ep_devs_createdwithout a lock of its own. Ifusb_set_configuration()has createdep_81but not yet set the flag,usb_set_interface()skips removing the endpoint devices of the old altsetting and createsep_81again. With a synchronous probe,hub_probe()runs insidedevice_add()with the udev lock held, andusb_set_configuration()then findsep_devs_createdalready set.Single-TT hubs don't call
usb_set_interface()fromhub_probe()and don't show this warning, but their probe runs without the udev lock as well.On an i.MX8MM board with an onboard USB2514 hub this hit 11 of 100 boots:
sysfs: cannot create duplicate filename '.../1-1/1-1:1.0/ep_81'
Call trace: sysfs_warn_dup usb_create_ep_devs create_intf_ep_devs usb_set_interface hub_probe usb_probe_interface really_probe __device_attach_async_helper async_run_entry_fnIt also shows on v7.3-rc6 in QEMU, with
dummy_hcdand a raw-gadget program emulating a multi-TT hub: re-authorizing the hub in a loop, while aSCHED_FIFOtask preempts the writing task at random points for 3 ms, hit the warning in 29 of 6000 iterations.Take the parent lock in
__device_attach_async_helper()as well, like__driver_attach_async_helper()does, and move __device_driver_lock/unlock() up for that. With this the warning was gone in 300 boots on the board and in 6000 iterations in QEMU.Fixes 765230b5f084 ("driver-core: add asynchronous probing support for drivers")Assisted-by LLMSigned-off-by Mario Peter <[email protected]>[ johan: switch to
async_schedule_dev_nocall()to avoid deadlock if
async_schedule_dev()runs synchronously when parent lock is held ]Cc [email protected] # 4.2Cc Dmitry Torokhov <[email protected]>Signed-off-by Johan Hovold <[email protected]>---
...
Quoted 25 lines
drivers/base/dd.c +8
-2@@ -1118,8 +1150,13 @@ static int __device_attach(struct device *dev, bool allow_async) } out_unlock:device_unlock (dev); -if (async) -async_schedule_dev (__device_attach_async_helper, dev); +if (async) { +if (!async_schedule_dev_nocall (__device_attach_async_helper, dev)) { +put_device (dev); + ret =2 ;/* retry synchronously */ + } + } +return ret; }@@ -1146,48 +1183,20 @@ EXPORT_SYMBOL_GPL(device_attach);void device_initial_probe (struct device *dev) {struct subsys_private *sp =bus_to_subsys (dev->bus); +int ret;if (!sp)return ;
drivers/base/dd.c EXPORT_SYMBOL_GPL(device_attach); · line 1198
Quoted 10 lines
drivers/base/dd.c +5
-2-if (sp->drivers_autoprobe) -__device_attach (dev, true); +if (sp->drivers_autoprobe) { + ret =__device_attach (dev, true); +if (ret ==2 ) +__device_attach (dev, false); + }subsys_put (sp); }
These two pieces do not seem to be related to the changes mentioned in the patch description. Are they leftovers from some other patch?
Alan Stern
Johan Hovold
maintainerRe: [PATCH v2 3/3] driver core: take parent lock in async device attach
Quoted 49 lines · Alan Stern
Quoted 48 lines · Johan Hovold
From: Mario Peter <[email protected]>
Take the parent lock in
__device_attach_async_helper()as well, like__driver_attach_async_helper()does, and move __device_driver_lock/unlock() up for that. With this the warning was gone in 300 boots on the board and in 6000 iterations in QEMU.Fixes 765230b5f084 ("driver-core: add asynchronous probing support for drivers")Assisted-by LLMSigned-off-by Mario Peter <[email protected]>[ johan: switch to
async_schedule_dev_nocall()to avoid deadlock if
async_schedule_dev()runs synchronously when parent lock is held ]drivers/base/dd.c +13
-4@@ -1118,8 +1150,13 @@ static int __device_attach(struct device *dev, bool allow_async) } out_unlock:device_unlock (dev); -if (async) -async_schedule_dev (__device_attach_async_helper, dev); +if (async) { +if (!async_schedule_dev_nocall (__device_attach_async_helper, dev)) { +put_device (dev); + ret =2 ;/* retry synchronously */ + } + } +return ret; }@@ -1146,48 +1183,20 @@ EXPORT_SYMBOL_GPL(device_attach);void device_initial_probe (struct device *dev) {struct subsys_private *sp =bus_to_subsys (dev->bus); +int ret;if (!sp)return ; -if (sp->drivers_autoprobe) -__device_attach (dev, true); +if (sp->drivers_autoprobe) { + ret =__device_attach (dev, true); +if (ret ==2 ) +__device_attach (dev, false); + }
Quoted 5 lines
drivers/base/dd.c EXPORT_SYMBOL_GPL(device_attach); · line 1198
Quoted 2 lines
drivers/base/dd.c +0
-0subsys_put (sp); }These two pieces do not seem to be related to the changes mentioned in the patch description. Are they leftovers from some other patch?
They are needed since async_schedule_dev() may execute the callback synchronously and in which case we'll deadlock if the caller already holds the parent mutex (e.g. during initial probe).
I only highlighted the issue in the trailer section of the patch, perhaps I should have amended the commit message proper.
Johan
Danilo Krummrich
maintainerQuoted 7 lines · Johan Hovold
Mario Peter reported that USB interfaces may be probed without holding the parent USB device lock during asynchronous and deferred probe.
This series addresses both cases, while fixing a long-standing use-after-free issue with the parent lock implementation stemming from the fact that the driver core does not necessarily hold a reference to the parent device when taking its lock.
I've picked those fixes up, as I think it would be good to still land them for v7.4-rc1. (I might send them as a second PR in the middle of the merge window though.)
Please let me know in case you have any concerns.
Thanks,
Danilo
Quoted 1 line · Johan Hovold
[PATCH v2 0/3] driver core: parent lock fixes
Applied, thanks!
Branch: driver-core-testing
Tree: git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git
[1/3] driver core: fix parent lock use-after-free on USB disconnect
commit: af61396a992b
[ Fix mention of bus vs. device type in __device_driver_{un}lock().
- Danilo ]
[2/3] driver core: take parent lock on deferred probe
commit: 277d13e909dc
[3/3] driver core: take parent lock in async device attach
commit: 20dacc0138e3
[ Fold the nested conditions into a single if statement; correct commit
message to mention "device types" rather than "buses" for "need_parent_lock". - Danilo ]
The patches will appear in the next linux-next integration (typically within 24 hours on weekdays).
The patches are in the driver-core-testing branch and will be promoted to driver-core-next after validation.