Driver core fixes for 7.4-rc1

- Make parent-lock requirements per device type instead of per bus,
   fixing a use-after-free on USB disconnect by limiting the requirement
   to USB interfaces, which retain a reference to their parent
 
 - Honor parent-lock requirements during deferred and asynchronous
   probing, fixing USB interface probe races, including duplicate
   endpoint creation attempts in sysfs
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQS2q/xV6QjXAdC7k+1FlHeO1qrKLgUCat5gLQAKCRBFlHeO1qrK
 LtDiAP42G5THtYuYWgtS1S42wNJGEFLAxD5FheBMDu7AfL8QuQD+KbI0FmAhQRp7
 avf4YvdeDWE1QZWV5THYvBDqzNB8BQ4=
 =6oMk
 -----END PGP SIGNATURE-----

Merge tag 'driver-core-7.4-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core

Pull driver core fixes from Danilo Krummrich:

 - Make parent-lock requirements per device type instead of per bus,
   fixing a use-after-free on USB disconnect by limiting the requirement
   to USB interfaces, which retain a reference to their parent

 - Honor parent-lock requirements during deferred and asynchronous
   probing, fixing USB interface probe races, including duplicate
   endpoint creation attempts in sysfs

* tag 'driver-core-7.4-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core:
  driver core: take parent lock in async device attach
  driver core: take parent lock on deferred probe
  driver core: fix parent lock use-after-free on USB disconnect
This commit is contained in:
Linus Torvalds 2026-10-26 07:04:41 -10:00
commit 0cc3ad8f4d
7 changed files with 79 additions and 54 deletions

View File

@ -156,6 +156,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);

View File

@ -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
@ -857,10 +871,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;

View File

@ -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);
@ -1029,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 device type 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 device type 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;
@ -1038,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
@ -1060,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);
}
@ -1117,8 +1150,11 @@ 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 && !async_schedule_dev_nocall(__device_attach_async_helper, dev)) {
put_device(dev);
ret = 2; /* retry synchronously */
}
return ret;
}
@ -1145,48 +1181,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 (parent && dev->bus->need_parent_lock)
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 (parent && dev->bus->need_parent_lock)
device_unlock(parent);
}
/**
* device_driver_attach - attach a specific driver to a specific device
* @drv: Driver to attach

View File

@ -2082,5 +2082,4 @@ const struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.need_parent_lock = true,
};

View File

@ -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,

View File

@ -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;
};
/**

View File

@ -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 companion_bus_register(const struct bus_type *bus);