Race condition in device probing / driver binding

HIGH
torvalds/linux
Commit: 8c2bf4a2e5cb
Affected: v7.0-rc6 and earlier (up to and including the pre-7.0-rc6 trees; part of the 7.1-rc series as well)
2026-04-25 13:29 UTC

Description

The commit introduces a gating mechanism to ensure a device is not probed until device_add() has finished its initialization by using a ready_to_probe flag. If a probe would occur before the device is ready, probe is deferred (EPROBE_DEFER). This directly addresses a race between device_add()/bus_add_device() and driver probing (via driver_register()/probe), reducing timing windows where a partially initialized device could be probed. Additional changes include: returning -ENOTCONN from software_node_get_reference_args() when a referenced software node exists but is not yet registered (allowing callers to defer probing), and ensuring sysfs group visibility respects both new and existing visibility helpers. A minor kernel-doc warning was fixed and a const-variant visibility path was added to sysfs ownership checks. Overall, these changes mitigate a class of security-relevant races, improve synchronization between device initialization and probing, and reduce exposure during partial initialization.

Commit Details

Author: Linus Torvalds

Date: 2026-04-19 19:58 UTC

Message:

Merge tag 'driver-core-7.1-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core Pull driver core fixes from Danilo Krummrich: - Prevent a device from being probed before device_add() has finished initializing it; gate probe with a "ready_to_probe" device flag to avoid races with concurrent driver_register() calls - Fix a kernel-doc warning for DEV_FLAG_COUNT introduced by the above - Return -ENOTCONN from software_node_get_reference_args() when a referenced software node is known but not yet registered, allowing callers to defer probe - In sysfs_group_attrs_change_owner(), also check is_visible_const(); missed when the const variant was introduced * tag 'driver-core-7.1-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: driver core: Add kernel-doc for DEV_FLAG_COUNT enum value sysfs: attribute_group: Respect is_visible_const() when changing owner software node: return -ENOTCONN when referenced swnode is not registered yet driver core: Don't let a device probe until it's ready

Triage Assessment

Vulnerability Type: Race condition

Confidence: HIGH

Reasoning:

The patch introduces a gating mechanism (ready_to_probe) to ensure a device is fully initialized before probing, preventing races where probe could run concurrently with driver registration. This mitigates a class of timing/race conditions that could lead to improper initialization, potential security weaknesses or instability. Additional changes also ensure software node references defer probing until registration, reducing exposure during partial initialization. Together, these address security-relevant race conditions and timing windows.

Verification Assessment

Vulnerability Type: Race condition in device probing / driver binding

Confidence: HIGH

Affected Versions: v7.0-rc6 and earlier (up to and including the pre-7.0-rc6 trees; part of the 7.1-rc series as well)

Code Diff

diff --git a/drivers/base/core.c b/drivers/base/core.c index 673d709fa7e645..bd2ddf2aab505f 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3688,6 +3688,21 @@ int device_add(struct device *dev) fw_devlink_link_device(dev); } + /* + * The moment the device was linked into the bus's "klist_devices" in + * bus_add_device() then it's possible that probe could have been + * attempted in a different thread via userspace loading a driver + * matching the device. "ready_to_probe" being unset would have + * blocked those attempts. Now that all of the above initialization has + * happened, unblock probe. If probe happens through another thread + * after this point but before bus_probe_device() runs then it's fine. + * bus_probe_device() -> device_initial_probe() -> __device_attach() + * will notice (under device_lock) that the device is already bound. + */ + device_lock(dev); + dev_set_ready_to_probe(dev); + device_unlock(dev); + bus_probe_device(dev); /* diff --git a/drivers/base/dd.c b/drivers/base/dd.c index cb5046f0634de9..1dc1e3528043c3 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -836,6 +836,26 @@ static int __driver_probe_device(const struct device_driver *drv, struct device if (dev->driver) return -EBUSY; + /* + * In device_add(), the "struct device" gets linked into the subsystem's + * list of devices and broadcast to userspace (via uevent) before we're + * quite ready to probe. Those open pathways to driver probe before + * we've finished enough of device_add() to reliably support probe. + * Detect this and tell other pathways to try again later. device_add() + * itself will also try to probe immediately after setting + * "ready_to_probe". + */ + if (!dev_ready_to_probe(dev)) + return dev_err_probe(dev, -EPROBE_DEFER, "Device not ready to probe\n"); + + /* + * Set can_match = true after calling dev_ready_to_probe(), so + * driver_deferred_probe_add() won't actually add the device to the + * deferred probe list when dev_ready_to_probe() returns false. + * + * When dev_ready_to_probe() returns false, it means that device_add() + * will do another probe() attempt for us. + */ dev->can_match = true; dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n", drv->bus->name, __func__, drv->name); diff --git a/drivers/base/property.c b/drivers/base/property.c index d16e9c5f1921a1..8e0148a37fffb4 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -602,6 +602,8 @@ EXPORT_SYMBOL_GPL(fwnode_property_match_property_string); * %-ENOENT when the index is out of bounds, the index has an empty * reference or the property was not found * %-EINVAL on parse error + * %-ENOTCONN when the remote firmware node exists but has not been + * registered yet */ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode, const char *prop, const char *nargs_prop, diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index a80575bf598b50..a19f8f722bc8e1 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -554,7 +554,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, return -EINVAL; if (!refnode) - return -ENOENT; + return -ENOTCONN; if (nargs_prop) { error = fwnode_property_read_u32(refnode, nargs_prop, &nargs_prop_val); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index b3edae0578c073..182e54e575ee9c 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -517,8 +517,11 @@ static int sysfs_group_attrs_change_owner(struct kobject *kobj, struct attribute *const *attr; for (i = 0, attr = grp->attrs; *attr; i++, attr++) { - if (grp->is_visible) { - mode = grp->is_visible(kobj, *attr, i); + if (grp->is_visible || grp->is_visible_const) { + if (grp->is_visible) + mode = grp->is_visible(kobj, *attr, i); + else + mode = grp->is_visible_const(kobj, *attr, i); if (mode & SYSFS_GROUP_INVISIBLE) break; if (!mode) diff --git a/include/linux/device.h b/include/linux/device.h index 67cec9ec0cd0c3..9c8fde6a3d866b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -504,6 +504,22 @@ struct device_physical_location { bool lid; }; +/** + * enum struct_device_flags - Flags in struct device + * + * Each flag should have a set of accessor functions created via + * __create_dev_flag_accessors() for each access. + * + * @DEV_FLAG_READY_TO_PROBE: If set then device_add() has finished enough + * initialization that probe could be called. + * @DEV_FLAG_COUNT: Number of defined struct_device_flags. + */ +enum struct_device_flags { + DEV_FLAG_READY_TO_PROBE = 0, + + DEV_FLAG_COUNT +}; + /** * struct device - The basic device structure * @parent: The device's "parent" device, the device to which it is attached. @@ -599,6 +615,7 @@ struct device_physical_location { * @dma_skip_sync: DMA sync operations can be skipped for coherent buffers. * @dma_iommu: Device is using default IOMMU implementation for DMA and * doesn't rely on dma_ops structure. + * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. * * At the lowest level, every device in a Linux system is represented by an * instance of struct device. The device structure contains the information @@ -721,8 +738,36 @@ struct device { #ifdef CONFIG_IOMMU_DMA bool dma_iommu:1; #endif + + DECLARE_BITMAP(flags, DEV_FLAG_COUNT); }; +#define __create_dev_flag_accessors(accessor_name, flag_name) \ +static inline bool dev_##accessor_name(const struct device *dev) \ +{ \ + return test_bit(flag_name, dev->flags); \ +} \ +static inline void dev_set_##accessor_name(struct device *dev) \ +{ \ + set_bit(flag_name, dev->flags); \ +} \ +static inline void dev_clear_##accessor_name(struct device *dev) \ +{ \ + clear_bit(flag_name, dev->flags); \ +} \ +static inline void dev_assign_##accessor_name(struct device *dev, bool value) \ +{ \ + assign_bit(flag_name, dev->flags, value); \ +} \ +static inline bool dev_test_and_set_##accessor_name(struct device *dev) \ +{ \ + return test_and_set_bit(flag_name, dev->flags); \ +} + +__create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE); + +#undef __create_dev_flag_accessors + /** * struct device_link - Device link representation. * @supplier: The device on the supplier end of the link.
← Back to Alerts View on GitHub →