* [PATCH] driver core: complete deferred binds when drivers_autoprobe is off
@ 2026-10-08 15:41 mosafer
2026-10-08 15:53 ` sashiko-bot
2026-10-09 8:52 ` [PATCH] " Danilo Krummrich
0 siblings, 2 replies; 7+ messages in thread
From: mosafer @ 2026-10-08 15:41 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
Cc: driver-core, linux-usb, linux-kernel, syzbot+863936f50214e843ae0c,
Mohammad Mosafer, stable
From: Mohammad Mosafer <mohsafer@gmail.com>
device_add() registers a device and then runs the bus's initial probe
via bus_probe_device() -> device_initial_probe(). With
drivers_autoprobe disabled for the bus, device_initial_probe() skips
__device_attach() entirely.
That is correct for automatic *matching* against the bus's driver list,
but __device_attach() also doubles as "complete a bind that a driver
already initiated": when dev->driver has been pre-assigned outside the
normal match/probe path, its else-if branch finishes the bind by
calling device_bind_driver().
With autoprobe off, that second duty is skipped too. USB depends on
it: usb_driver_claim_interface() pre-sets dev->driver on an interface
that is not yet registered, documenting "let the future device_add()
bind it, bypassing probe()". Composite drivers (cdc-acm, cdc_ncm,
cdc_mbim, ...) rely on it to bind their sibling data interfaces. If
drivers_autoprobe is written with 0 while such an interface is
between the claim and its device_add(), the interface ends up
registered, with dev->driver set and iface->condition ==
USB_INTERFACE_BOUND, but never bound: its knode_driver is never
attached to the driver's klist_devices.
Teardown then trusts those flags: usb_driver_release_interface() ->
device_release_driver() -> __device_release_driver() runs a full
release and calls klist_remove(&dev->p->knode_driver) on a
never-attached node whose knode_klist() is NULL, which klist_put()
dereferences:
Oops: general protection fault
KASAN: null-ptr-deref in range [0x0000000000000058-0x000000000000005f]
klist_put <- klist_remove <- device_release_driver_internal <-
usb_driver_release_interface <- acm_disconnect / cdc_ncm_unbind
Reproduced with syzkaller's C reproducer on a 7.3.0-rc6 tree:
instrumentation shows the doomed interface's device_add() racing an
autoprobe=0 write; the bind-completion branch never runs for it; later
teardown hits klist_remove() with a pristine node. With this patch the
same instrumented race completes the bind inside the window (knode
attached, crash gone): 3 VMs, 5 min per run, race window hit and bind
completed 8 times, zero KLIST-REMOVE-BAD, zero Oops.
Gate the flag on what it means - automatic matching - not on the
completion of a bind the driver already requested: pass through to
__device_attach() when dev->driver is pre-set. Other subsystems that
preset dev->driver before device_add() (w1, tegra xusb,
zynqmp-ipi-mailbox) get the same correctness back; when dev->driver is
NULL the behavior is byte-for-byte unchanged.
Reported-by: syzbot+863936f50214e843ae0c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=863936f50214e843ae0c
Fixes: b8c5cec23d5c ("Driver core: udev triggered device-<>driver binding")
Cc: stable@vger.kernel.org
Signed-off-by: Mohammad Mosafer <mohsafer@gmail.com>
---
drivers/base/dd.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index f6525a7ee8c5..98315264295e 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -1149,7 +1149,17 @@ void device_initial_probe(struct device *dev)
if (!sp)
return;
- if (sp->drivers_autoprobe)
+ /*
+ * The drivers_autoprobe flag only suppresses matching the device
+ * against the bus drivers. If a driver has already been assigned
+ * to the device (e.g. usb_driver_claim_interface() pre-setting
+ * dev->driver for an unregistered interface, expecting device_add()
+ * to complete the bind), the bind must be finished regardless of
+ * autoprobe: otherwise the device ends up registered with
+ * dev->driver set but never bound, and teardown oopses removing
+ * the never-attached knode_driver from the driver klist.
+ */
+ if (sp->drivers_autoprobe || dev->driver)
__device_attach(dev, true);
subsys_put(sp);
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] driver core: complete deferred binds when drivers_autoprobe is off
2026-10-08 15:41 [PATCH] driver core: complete deferred binds when drivers_autoprobe is off mosafer
@ 2026-10-08 15:53 ` sashiko-bot
2026-10-08 21:33 ` [PATCH v2] " mosafer
2026-10-09 8:52 ` [PATCH] " Danilo Krummrich
1 sibling, 1 reply; 7+ messages in thread
From: sashiko-bot @ 2026-10-08 15:53 UTC (permalink / raw)
To: mosafer; +Cc: linux-usb
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] driver core: TOCTOU race in device_initial_probe() causes spurious autoprobe
--
commit 363268c1e7ac2e5ecc70b8f7c4762aa363c48d73
Author: Mohammad Mosafer <mohsafer@gmail.com>
driver core: complete deferred binds when drivers_autoprobe is off
This commit updates device_initial_probe() to complete deferred device binds
even when bus autoprobing is disabled. It conditionally calls
__device_attach() if dev->driver is already set, preventing a null-pointer
dereference during device teardown.
> diff --git a/drivers/base/dd.c b/drivers/base/dd.c
> index f6525a7ee8c54..98315264295e4 100644
> --- a/drivers/base/dd.c
> +++ b/drivers/base/dd.c
> @@ -1149,7 +1149,17 @@ void device_initial_probe(struct device *dev)
> if (!sp)
> return;
>
> - if (sp->drivers_autoprobe)
> + /*
> + * The drivers_autoprobe flag only suppresses matching the device
> + * against the bus drivers. If a driver has already been assigned
> + * to the device (e.g. usb_driver_claim_interface() pre-setting
> + * dev->driver for an unregistered interface, expecting device_add()
> + * to complete the bind), the bind must be finished regardless of
> + * autoprobe: otherwise the device ends up registered with
> + * dev->driver set but never bound, and teardown oopses removing
> + * the never-attached knode_driver from the driver klist.
> + */
> + if (sp->drivers_autoprobe || dev->driver)
> __device_attach(dev, true);
[Severity: Medium]
If sp->drivers_autoprobe is 0 but dev->driver is set, device_initial_probe()
locklessly evaluates this as true and calls __device_attach().
Before __device_attach() acquires device_lock(dev), what happens if a
concurrent operation (like a sysfs unbind or sibling device disconnect)
acquires the lock, detaches the driver, and sets dev->driver to NULL?
When __device_attach() subsequently acquires the lock, it would check
dev->driver. Finding it NULL, it would skip the pre-assigned bind logic and
fall through to the else block:
static int __device_attach(struct device *dev, bool allow_async)
{
...
device_lock(dev);
if (dev->p->dead) {
goto out_unlock;
} else if (dev->driver) {
...
} else {
...
ret = bus_for_each_drv(dev->bus, NULL, &data,
__device_attach_driver);
This else block unconditionally executes bus_for_each_drv(), which performs
a full automatic match and probe against all drivers on the bus.
Does this result in a spurious bus-wide autoprobe, directly violating the
administrator's explicit drivers_autoprobe = 0 policy?
>
> subsys_put(sp);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261008154105.256340-1-mosafer@node0.quickhttpnode15.cloudfaas-pg0.wisc.cloudlab.us?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2] driver core: complete deferred binds when drivers_autoprobe is off
2026-10-08 15:53 ` sashiko-bot
@ 2026-10-08 21:33 ` mosafer
2026-10-08 21:39 ` sashiko-bot
0 siblings, 1 reply; 7+ messages in thread
From: mosafer @ 2026-10-08 21:33 UTC (permalink / raw)
To: sashiko-bot
Cc: linux-usb, mohsafer, sashiko-reviews, syzbot+863936f50214e843ae0c,
stable
From: Mohammad Mosafer <mohsafer@gmail.com>
device_add() registers a device and then runs the bus's initial probe
via bus_probe_device() -> device_initial_probe(). With
drivers_autoprobe disabled for the bus, device_initial_probe() skipped
__device_attach() entirely.
That is correct for automatic *matching* against the bus's driver list,
but __device_attach() also doubles as "complete a bind that a driver
already initiated": when dev->driver has been pre-assigned outside the
normal match/probe path, it finishes the bind by calling
device_bind_driver() under the device lock, bypassing probe().
With autoprobe off, that second duty was skipped too. USB depends on
it: usb_driver_claim_interface() pre-sets dev->driver on an interface
that is not yet registered, documenting "let the future device_add()
bind it, bypassing probe()". Composite drivers (cdc-acm, cdc_ncm,
cdc_mbim, ...) rely on it to bind their sibling data interfaces. If
drivers_autoprobe is written with 0 while such an interface is between
the claim and its device_add(), the interface ends up registered, with
dev->driver set and iface->condition == USB_INTERFACE_BOUND, but never
bound: its knode_driver is never attached to the driver's
klist_devices.
Teardown then trusts those flags: usb_driver_release_interface() ->
device_release_driver() -> __device_release_driver() runs a full
release and calls klist_remove(&dev->p->knode_driver) on a
never-attached node whose knode_klist() is NULL, which klist_put()
dereferences:
Oops: general protection fault
KASAN: null-ptr-deref in range [0x0000000000000058-0x000000000000005f]
klist_put <- klist_remove <- device_release_driver_internal <-
usb_driver_release_interface <- acm_disconnect / cdc_ncm_unbind
Keep device_initial_probe() always calling __device_attach(), but pass
whether automatic matching is allowed (sp->drivers_autoprobe) down to
it, and evaluate that flag inside __device_attach() under the device
lock, where dev->driver is read anyway: a device with a pre-assigned
driver completes its bind regardless of autoprobe, and only a device
without one is subject to the matching policy. Evaluating the two
conditions inside the locked branch (rather than as
"autoprobe || dev->driver" in the caller) also closes a TOCTOU: a
concurrent detach can no longer turn the completion of a pre-assigned
bind into bus-wide matching behind an "autoprobe off" policy.
Other subsystems that preset dev->driver before device_add() (w1,
tegra xusb, zynqmp-ipi-mailbox) get the same correctness back; for
devices with no pre-assigned driver and autoprobe off, behavior is
unchanged.
Reproduced with syzkaller's C reproducer on a 7.3.0-rc6 tree:
instrumentation shows the doomed interface's device_add() racing an
autoprobe=0 write; the bind-completion branch never runs for it; later
teardown hits klist_remove() with a pristine node. With this patch the
same instrumented race completes the bind inside the window (3 VMs,
5 min per run: window hit 3 times, bind completed 3 times, and 802
devices with no pre-assigned driver correctly skipped matching despite
__device_attach() now always running): zero KLIST-REMOVE-BAD, zero
Oops.
Reported-by: syzbot+863936f50214e843ae0c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=863936f50214e843ae0c
Fixes: b8c5cec23d5c ("Driver core: udev triggered device-<>driver binding")
Cc: stable@vger.kernel.org
Signed-off-by: Mohammad Mosafer <mohsafer@gmail.com>
---
---
Thanks for the review. The TOCTOU is real: v1 evaluated
"sp->drivers_autoprobe || dev->driver" in device_initial_probe() without
device_lock held, so a concurrent detach in that window could make
__device_attach() observe dev->driver == NULL under the lock and fall
through to bus_for_each_drv() - a bus-wide match despite the
drivers_autoprobe=0 policy.
Changes since v1:
- Move the decision into __device_attach(): it gains an allow_match
argument (device_initial_probe() passes sp->drivers_autoprobe,
device_attach() passes true) and evaluates it inside the locked
if/else chain: a pre-assigned dev->driver completes its bind
regardless of autoprobe; the bus_for_each_drv() matching branch only
runs when matching is allowed or requested. dev->driver is now read
exactly once, under device_lock, so a concurrent detach can neither
bypass the bind completion nor turn it into a spurious autoprobe.
- The crash fix itself is untouched code: the else-if branch that
completes the pre-assigned bind is identical to v1 / to the
autoprobe-on path; only its reachability changed.
Re-verified with the same instrumented race (3 VMs, 5 min): window hit
3 times (DATT match=0 drv=1 bound=0), bind completed all 3 (DD-BIND,
teardown attached=1), and 802 devices with no pre-assigned driver under
match=0 returned without touching bus_for_each_drv(); zero
KLIST-REMOVE-BAD, zero Oops.
v1: https://lore.kernel.org/all/20261008154105.256340-1-mosafer@node0.quickhttpnode15.cloudfaas-pg0.wisc.cloudlab.us/
review: https://lore.kernel.org/all/sashiko-outbox-164361@kernel.org/
drivers/base/dd.c | 30 ++++++++++++++++++++++++++----
1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index f6525a7ee8c5..4c1bb1979519 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -1065,7 +1065,8 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
put_device(dev);
}
-static int __device_attach(struct device *dev, bool allow_async)
+static int __device_attach(struct device *dev, bool allow_async,
+ bool allow_match)
{
int ret = 0;
bool async = false;
@@ -1085,6 +1086,17 @@ static int __device_attach(struct device *dev, bool allow_async)
device_set_driver(dev, NULL);
ret = 0;
}
+ } else if (!allow_match) {
+ /*
+ * Automatic driver matching is suppressed for this device
+ * (drivers_autoprobe is off), and no driver has been
+ * assigned to it: do not run bus_for_each_drv() matching
+ * for it. Deciding this here, under device_lock, means a
+ * concurrent driver detach between the caller picking
+ * allow_match=false and reaching here cannot turn the
+ * completion of a pre-assigned bind into bus-wide matching.
+ */
+ goto out_unlock;
} else {
struct device_attach_data data = {
.dev = dev,
@@ -1138,7 +1150,7 @@ static int __device_attach(struct device *dev, bool allow_async)
*/
int device_attach(struct device *dev)
{
- return __device_attach(dev, false);
+ return __device_attach(dev, false, true);
}
EXPORT_SYMBOL_GPL(device_attach);
@@ -1149,8 +1161,18 @@ void device_initial_probe(struct device *dev)
if (!sp)
return;
- if (sp->drivers_autoprobe)
- __device_attach(dev, true);
+ /*
+ * Always run the attach, but tell it whether automatic matching is
+ * allowed. Completing a bind that a driver already initiated (e.g.
+ * usb_driver_claim_interface() pre-setting dev->driver for an
+ * unregistered interface, expecting device_add() to bind it) is not
+ * automatic matching and must happen even with drivers_autoprobe
+ * off; matching is evaluated in __device_attach() under the device
+ * lock. Skipping the call entirely used to leave such devices
+ * registered-but-unbound, and teardown then oopsed klist_removing
+ * the never-attached knode_driver.
+ */
+ __device_attach(dev, true, sp->drivers_autoprobe);
subsys_put(sp);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2] driver core: complete deferred binds when drivers_autoprobe is off
2026-10-08 21:33 ` [PATCH v2] " mosafer
@ 2026-10-08 21:39 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-10-08 21:39 UTC (permalink / raw)
To: mosafer; +Cc: linux-usb
> From: Mohammad Mosafer <mohsafer@gmail.com>
>
> device_add() registers a device and then runs the bus's initial probe
> via bus_probe_device() -> device_initial_probe(). With
> drivers_autoprobe disabled for the bus, device_initial_probe() skipped
> __device_attach() entirely.
>
> That is correct for automatic *matching* against the bus's driver list,
> but __device_attach() also doubles as "complete a bind that a driver
> already initiated": when dev->driver has been pre-assigned outside the
> normal match/probe path, it finishes the bind by calling
> device_bind_driver() under the device lock, bypassing probe().
>
> With autoprobe off, that second duty was skipped too. USB depends on
> it: usb_driver_claim_interface() pre-sets dev->driver on an interface
> [ ... ]
> Reported-by: syzbot+863936f50214e843ae0c@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=863936f50214e843ae0c
> Fixes: b8c5cec23d5c ("Driver core: udev triggered device-<>driver binding")
> Cc: stable@vger.kernel.org
> Signed-off-by: Mohammad Mosafer <mohsafer@gmail.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261008213320.268141-1-mosafer@node0.quickhttpnode15.cloudfaas-pg0.wisc.cloudlab.us?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] driver core: complete deferred binds when drivers_autoprobe is off
2026-10-08 15:41 [PATCH] driver core: complete deferred binds when drivers_autoprobe is off mosafer
2026-10-08 15:53 ` sashiko-bot
@ 2026-10-09 8:52 ` Danilo Krummrich
2026-10-09 18:22 ` [PATCH v2] " mosafer
1 sibling, 1 reply; 7+ messages in thread
From: Danilo Krummrich @ 2026-10-09 8:52 UTC (permalink / raw)
To: mosafer
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, driver-core, linux-usb,
linux-kernel, syzbot+863936f50214e843ae0c, stable
On Thu Oct 8, 2026 at 5:41 PM CEST, mosafer wrote:
> From: Mohammad Mosafer <mohsafer@gmail.com>
>
> device_add() registers a device and then runs the bus's initial probe
> via bus_probe_device() -> device_initial_probe(). With
> drivers_autoprobe disabled for the bus, device_initial_probe() skips
> __device_attach() entirely.
>
> That is correct for automatic *matching* against the bus's driver list,
> but __device_attach() also doubles as "complete a bind that a driver
> already initiated": when dev->driver has been pre-assigned outside the
> normal match/probe path, its else-if branch finishes the bind by
> calling device_bind_driver().
We've recently had another patch to fix up cases where dev->driver is
pre-assigned. Please see [1] and the corresponding thread.
IIRC, there was no reasons the remaining users can't just use a proper match()
callback instead, so we don't have to worry about those edge cases in the
future.
[1] https://lore.kernel.org/driver-core/20260820084557.129908-1-khiemtranzo532001@gmail.com/
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2] driver core: complete deferred binds when drivers_autoprobe is off
2026-10-09 8:52 ` [PATCH] " Danilo Krummrich
@ 2026-10-09 18:22 ` mosafer
2026-10-09 18:28 ` sashiko-bot
0 siblings, 1 reply; 7+ messages in thread
From: mosafer @ 2026-10-09 18:22 UTC (permalink / raw)
To: dakr
Cc: driver-core, gregkh, linux-kernel, linux-usb, mohsafer, rafael,
stable, syzbot+863936f50214e843ae0c, khiemtranzo532001
From: Mohammad Mosafer <mohsafer@gmail.com>
device_add() registers a device and then runs the bus's initial probe
via bus_probe_device() -> device_initial_probe(). With
drivers_autoprobe disabled for the bus, device_initial_probe() skipped
__device_attach() entirely.
That is correct for automatic *matching* against the bus's driver list,
but __device_attach() also doubles as "complete a bind that a driver
already initiated": when dev->driver has been pre-assigned outside the
normal match/probe path, it finishes the bind by calling
device_bind_driver() under the device lock, bypassing probe().
With autoprobe off, that second duty was skipped too. USB depends on
it: usb_driver_claim_interface() pre-sets dev->driver on an interface
that is not yet registered, documenting "let the future device_add()
bind it, bypassing probe()". Composite drivers (cdc-acm, cdc_ncm,
cdc_mbim, ...) rely on it to bind their sibling data interfaces. If
drivers_autoprobe is written with 0 while such an interface is between
the claim and its device_add(), the interface ends up registered, with
dev->driver set and iface->condition == USB_INTERFACE_BOUND, but never
bound: its knode_driver is never attached to the driver's
klist_devices.
Teardown then trusts those flags: usb_driver_release_interface() ->
device_release_driver() -> __device_release_driver() runs a full
release and calls klist_remove(&dev->p->knode_driver) on a
never-attached node whose knode_klist() is NULL, which klist_put()
dereferences:
Oops: general protection fault
KASAN: null-ptr-deref in range [0x0000000000000058-0x000000000000005f]
klist_put <- klist_remove <- device_release_driver_internal <-
usb_driver_release_interface <- acm_disconnect / cdc_ncm_unbind
Keep device_initial_probe() always calling __device_attach(), but pass
whether automatic matching is allowed (sp->drivers_autoprobe) down to
it, and evaluate that flag inside __device_attach() under the device
lock, where dev->driver is read anyway: a device with a pre-assigned
driver completes its bind regardless of autoprobe, and only a device
without one is subject to the matching policy. Evaluating the two
conditions inside the locked branch (rather than as
"autoprobe || dev->driver" in the caller) also closes a TOCTOU: a
concurrent detach can no longer turn the completion of a pre-assigned
bind into bus-wide matching behind an "autoprobe off" policy.
Other subsystems that preset dev->driver before device_add() (w1,
tegra xusb, zynqmp-ipi-mailbox) get the same correctness back; for
devices with no pre-assigned driver and autoprobe off, behavior is
unchanged.
Reproduced with syzkaller's C reproducer on a 7.3.0-rc6 tree:
instrumentation shows the doomed interface's device_add() racing an
autoprobe=0 write; the bind-completion branch never runs for it; later
teardown hits klist_remove() with a pristine node. With this patch the
same instrumented race completes the bind inside the window (3 VMs,
5 min per run: window hit 3 times, bind completed 3 times, and 802
devices with no pre-assigned driver correctly skipped matching despite
__device_attach() now always running): zero KLIST-REMOVE-BAD, zero
Oops.
Reported-by: syzbot+863936f50214e843ae0c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=863936f50214e843ae0c
Fixes: b8c5cec23d5c ("Driver core: udev triggered device-<>driver binding")
Cc: stable@vger.kernel.org
Signed-off-by: Mohammad Mosafer <mohsafer@gmail.com>
---
On Fri Oct 9, Danilo Krummrich wrote:
> We've recently had another patch to fix up cases where dev->driver is
> pre-assigned. Please see [1] and the corresponding thread.
>
> IIRC, there was no reasons the remaining users can't just use a proper
> match() callback instead, so we don't have to worry about those edge
> cases in the future.
Thanks for the pointer.
I had not seen Kien's series; looking at it now, we are attacking the
same half-claimed state from opposite ends: his patch makes the release
path tolerant of it (skip klist_remove() when the device is not bound),
while mine makes the deferred bind that usb_driver_claim_interface()
promises actually complete when drivers_autoprobe is off, so the
half-claimed state never arises. If the direction is to remove the
pre-assigned dev->driver users entirely via proper match() callbacks,
I'm happy for that to supersede this fix -- I'd be glad to help with
the USB side of that conversion, as I now know the claim/bind/teardown
paths in detail.
Until such a conversion lands, though, the window is still open in
current releases (syzbot hits it, and any userspace writing
drivers_autoprobe races it), and pre-assignment is not only USB:
drivers/w1/, drivers/phy/tegra/xusb.c and
drivers/mailbox/zynqmp-ipi-mailbox.c pre-assign dev->driver the same
way today.
So please treat this as: whichever of Kien's guard, this bind-side
fix, or the match() conversion you want as the canonical answer, this
patch is yours to drop or pick -- but happy to have your steer before
it evolves further.
While the v1 was on the list the review bot found a real defect (the
dev->driver test was open-coded in device_initial_probe() lockless, a
TOCTOU against a concurrent detach). v2 below fixes it the way the
bot suggested: __device_attach() takes an allow_match flag and the
decision is evaluated under device_lock. Evidence unchanged in kind:
same reproducer, instrumented patched boots show the deferred bind
completing in the previously-crashing window, 0 crashes (v1: 6/9 on
the unpatched control), plus a clean bare soak.
drivers/base/dd.c | 30 ++++++++++++++++++++++++++----
1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index f6525a7ee8c5..4c1bb1979519 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -1065,7 +1065,8 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
put_device(dev);
}
-static int __device_attach(struct device *dev, bool allow_async)
+static int __device_attach(struct device *dev, bool allow_async,
+ bool allow_match)
{
int ret = 0;
bool async = false;
@@ -1085,6 +1086,17 @@ static int __device_attach(struct device *dev, bool allow_async)
device_set_driver(dev, NULL);
ret = 0;
}
+ } else if (!allow_match) {
+ /*
+ * Automatic driver matching is suppressed for this device
+ * (drivers_autoprobe is off), and no driver has been
+ * assigned to it: do not run bus_for_each_drv() matching
+ * for it. Deciding this here, under device_lock, means a
+ * concurrent driver detach between the caller picking
+ * allow_match=false and reaching here cannot turn the
+ * completion of a pre-assigned bind into bus-wide matching.
+ */
+ goto out_unlock;
} else {
struct device_attach_data data = {
.dev = dev,
@@ -1138,7 +1150,7 @@ static int __device_attach(struct device *dev, bool allow_async)
*/
int device_attach(struct device *dev)
{
- return __device_attach(dev, false);
+ return __device_attach(dev, false, true);
}
EXPORT_SYMBOL_GPL(device_attach);
@@ -1149,8 +1161,18 @@ void device_initial_probe(struct device *dev)
if (!sp)
return;
- if (sp->drivers_autoprobe)
- __device_attach(dev, true);
+ /*
+ * Always run the attach, but tell it whether automatic matching is
+ * allowed. Completing a bind that a driver already initiated (e.g.
+ * usb_driver_claim_interface() pre-setting dev->driver for an
+ * unregistered interface, expecting device_add() to bind it) is not
+ * automatic matching and must happen even with drivers_autoprobe
+ * off; matching is evaluated in __device_attach() under the device
+ * lock. Skipping the call entirely used to leave such devices
+ * registered-but-unbound, and teardown then oopsed klist_removing
+ * the never-attached knode_driver.
+ */
+ __device_attach(dev, true, sp->drivers_autoprobe);
subsys_put(sp);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2] driver core: complete deferred binds when drivers_autoprobe is off
2026-10-09 18:22 ` [PATCH v2] " mosafer
@ 2026-10-09 18:28 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-10-09 18:28 UTC (permalink / raw)
To: mosafer; +Cc: linux-usb
> From: Mohammad Mosafer <mohsafer@gmail.com>
>
> device_add() registers a device and then runs the bus's initial probe
> via bus_probe_device() -> device_initial_probe(). With
> drivers_autoprobe disabled for the bus, device_initial_probe() skipped
> __device_attach() entirely.
>
> That is correct for automatic *matching* against the bus's driver list,
> but __device_attach() also doubles as "complete a bind that a driver
> already initiated": when dev->driver has been pre-assigned outside the
> normal match/probe path, it finishes the bind by calling
> device_bind_driver() under the device lock, bypassing probe().
>
> With autoprobe off, that second duty was skipped too. USB depends on
> it: usb_driver_claim_interface() pre-sets dev->driver on an interface
> [ ... ]
> Reported-by: syzbot+863936f50214e843ae0c@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=863936f50214e843ae0c
> Fixes: b8c5cec23d5c ("Driver core: udev triggered device-<>driver binding")
> Cc: stable@vger.kernel.org
> Signed-off-by: Mohammad Mosafer <mohsafer@gmail.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261009182239.283329-1-mosafer@node0.quickhttpnode15.cloudfaas-pg0.wisc.cloudlab.us?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-09 18:28 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-08 15:41 [PATCH] driver core: complete deferred binds when drivers_autoprobe is off mosafer
2026-10-08 15:53 ` sashiko-bot
2026-10-08 21:33 ` [PATCH v2] " mosafer
2026-10-08 21:39 ` sashiko-bot
2026-10-09 8:52 ` [PATCH] " Danilo Krummrich
2026-10-09 18:22 ` [PATCH v2] " mosafer
2026-10-09 18:28 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox