* [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