From: mosafer <mohsafer@gmail.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
"Rafael J. Wysocki" <rafael@kernel.org>,
Danilo Krummrich <dakr@kernel.org>
Cc: driver-core@lists.linux.dev, linux-usb@vger.kernel.org,
linux-kernel@vger.kernel.org,
syzbot+863936f50214e843ae0c@syzkaller.appspotmail.com,
Mohammad Mosafer <mohsafer@gmail.com>,
stable@vger.kernel.org
Subject: [PATCH] driver core: complete deferred binds when drivers_autoprobe is off
Date: Thu, 8 Oct 2026 10:41:05 -0500 [thread overview]
Message-ID: <20261008154105.256340-1-mosafer@node0.quickhttpnode15.cloudfaas-pg0.wisc.cloudlab.us> (raw)
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
next reply other threads:[~2026-10-08 15:42 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-08 15:41 mosafer [this message]
2026-10-08 15:53 ` [PATCH] driver core: complete deferred binds when drivers_autoprobe is off 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261008154105.256340-1-mosafer@node0.quickhttpnode15.cloudfaas-pg0.wisc.cloudlab.us \
--to=mohsafer@gmail.com \
--cc=dakr@kernel.org \
--cc=driver-core@lists.linux.dev \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=rafael@kernel.org \
--cc=stable@vger.kernel.org \
--cc=syzbot+863936f50214e843ae0c@syzkaller.appspotmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox