From: "Danilo Krummrich" <dakr@kernel.org>
To: "Alan Stern" <stern@rowland.harvard.edu>
Cc: "Nguyen Quang Le Kien" <khiemtranzo532001@gmail.com>,
<gregkh@linuxfoundation.org>, <rafael@kernel.org>,
<driver-core@lists.linux.dev>, <linux-kernel@vger.kernel.org>,
<syzbot+87188222c77c0dbbdb4d@syzkaller.appspotmail.com>,
<stable@vger.kernel.org>, <linux-usb@vger.kernel.org>
Subject: Re: [PATCH v4] driver core: avoid klist_remove() on unattached knode_driver
Date: Thu, 20 Aug 2026 22:54:26 +0200 [thread overview]
Message-ID: <DKU2J83B6IX6.2CG1WOB66WC39@kernel.org> (raw)
In-Reply-To: <0198b9fe-4bc0-4e12-b788-85aa2c9f8162@rowland.harvard.edu>
On Thu Aug 20, 2026 at 7:22 PM CEST, Alan Stern wrote:
> On Thu, Aug 20, 2026 at 06:14:37PM +0200, Danilo Krummrich wrote:
>> (Cc: linux-usb)
>>
>> On Thu Aug 20, 2026 at 10:45 AM CEST, Nguyen Quang Le Kien wrote:
>> > Fixes: 94e7b1c5ff20 ("[PATCH] Add a klist to struct device_driver for the devices bound to it.")
>>
>> This is not the correct commit to reference, this commit seems fine.
>>
>> > diff --git a/drivers/base/dd.c b/drivers/base/dd.c
>> > index 60c005223..4154b4499 100644
>> > --- a/drivers/base/dd.c
>> > +++ b/drivers/base/dd.c
>> > @@ -1354,7 +1354,8 @@ static void __device_release_driver(struct device *dev, struct device *parent)
>> > device_unbind_cleanup(dev);
>> > device_links_driver_cleanup(dev);
>> >
>> > - klist_remove(&dev->p->knode_driver);
>> > + if (device_is_bound(dev))
>> > + klist_remove(&dev->p->knode_driver);
>>
>> This looks like band-aid for the underlying design tension in the USB core
>> (which we should address instead) and does not belong in the driver core.
>>
>> It's not visible from the above diff, but with this patch the control flow
>> becomes:
>>
>> if (dev->driver) {
>> if (device_is_bound(dev))
>> klist_remove(&dev->p->knode_driver);
>> ...
>> }
>>
>> but dev->driver already indicates that the device is bound to dev->driver in
>> this context.
>>
>> The reason we "need" this check regardless is that usb_driver_claim_interface()
>> (ab)uses dev->driver to indicate that a certain USB driver claimed, or rather
>> reserved, this device.
>>
>> There are two cases in usb_driver_claim_interface():
>>
>> (1) The device to claim is already registered with the driver core, in which
>> case device_bind_driver(dev) is called and dev->driver is correctly set
>> during the bind attempt.
>>
>> (2) The device to claim was not yet registered with the driver core. This can
>> happen when the USB interface the driver actually binds to is registered
>> (and hence probed) before the additional interface the driver wants to
>> claim is registered. The USB core sets dev->driver independent of the bind
>> state to indicate it has reserved the interface.
>>
>> The first case is perfectly fine, but the issue with the second case is that now
>> dev->driver is semantically overloaded:
>>
>> The USB core treats it as "dev is reserved for dev->driver" and the driver core
>> treats it as "dev is currently binding or bound to dev->driver", but that's not
>> actually the case yet, since device_bind_driver() hasn't been called yet.
>>
>> IOW, dev->driver should only be set under the device lock before calling
>> device_bind_driver(), and, in case of failure, should be cleared after
>> device_bind_driver() with the device lock still held.
>>
>> Besides the reported crash, another implication of this is that all other
>> functions from device_release_driver() are called as well, even though
>> device_bind_driver() was never called before, and there is no guarantee that
>> this does not cause other unexpected side effects already or in the future.
>>
>> I think one solution could be to add a new claimed field to struct usb_interface
>> to indicate that the interface is reserved for a certain driver and make
>> usb_device_match() reject the device if ever probed otherwise.
>>
>> Another solution (but that's a bit more work) would be to separate interface
>> registration from interface probing in usb_set_configuration(). Of course that
>> needs help from the driver core as well, but it would also get us rid of the
>> slightly odd situation that a driver may operate a claimed device already, even
>> though it is not yet registered with the driver core.
>>
>> I'd suggest going for a claimed field in struct usb_interface first to fix the
>> immediate problem and then take it from there.
>>
>> That said, I wonder if there's more to think about with the
>> usb_driver_claim_interface() / usb_driver_release_interface() API.
>>
>> After having a brief look it seems that drivers have invented various different
>> approaches to protect against the case where userspace could write the claimed
>> interface's name to:
>>
>> /sys/bus/usb/drivers/<driver>/unbind
>>
>> I think this could be much cleaner if the driver core would support "claimed"
>> devices" natively. OTH, there's only ~20 drivers across USB and PnP though, so
>> probably not worth.
>
> I don't object to the idea of adding a "claimed" field to usb_interface.
>
> However, isn't it true that the driver core has always supported the
> idea of a driver being associated with a device before binding? In
> particular, __device_attach() specifically checks for dev->driver being
> already set. If it is, the match and probe steps are skipped.
Ah, I also wanted to add a comment about this, but forgot about it, sorry.
This was added ~25 years ago to be able to hardwire a system device to a
specific driver, which less than a year after was replaced entirely by something
else and later converted back to a bus with proper match() logic. But the
dev->driver special case in __device_attach() was never removed.
So, I guess it is fair to say it was always there, but the driver core does not
really handle dev->driver being set "randomly" outside of the bind/unbind
lifecycle in general. It is expected that it is only set when the driver is
actually bound (or binding).
One example is __device_release_driver(), if device_bind_driver() (or
really_probe()) wasn't called, we should also not land within the
if (dev->driver) conditional of __device_release_driver().
Also note that usb_driver_claim_interface() is the only callsite in the kernel
that does set dev->driver without a subsequent device_bind_driver() while
holding the device lock.
(Well, actually, after a thorough check, there's w1, which hardwires the
w1_slave_driver, where it should actually use a trivial match() callback.)
In general, matching should be done through the match() callback.
> Are you saying that this code path should be removed as well?
Yes, I think we should remove it. It's a rather error prone special case that
can easily be handled with normal match() logic.
prev parent reply other threads:[~2026-08-20 20:54 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-20 5:55 [PATCH] driver core: avoid klist_remove() on unattached knode_driver Nguyen Quang Le Kien
2026-08-20 6:05 ` [PATCH v2] " Nguyen Quang Le Kien
2026-08-20 6:40 ` Greg KH
2026-08-20 6:56 ` Nguyen Quang Le Kien
2026-08-20 7:05 ` Greg Kroah-Hartman
2026-08-20 7:45 ` [PATCH v3] " Nguyen Quang Le Kien
2026-08-20 8:23 ` Greg Kroah-Hartman
[not found] ` <20260820084557.129908-1-khiemtranzo532001@gmail.com>
2026-08-20 16:14 ` [PATCH v4] " Danilo Krummrich
2026-08-20 17:22 ` Alan Stern
2026-08-20 20:54 ` Danilo Krummrich [this message]
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=DKU2J83B6IX6.2CG1WOB66WC39@kernel.org \
--to=dakr@kernel.org \
--cc=driver-core@lists.linux.dev \
--cc=gregkh@linuxfoundation.org \
--cc=khiemtranzo532001@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=rafael@kernel.org \
--cc=stable@vger.kernel.org \
--cc=stern@rowland.harvard.edu \
--cc=syzbot+87188222c77c0dbbdb4d@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