Re: [PATCH] driver core: take parent lock on deferred probe

From: Alan Stern

Date: Tue Oct 13 2026 - 22:42:00 EST


On Tue, Oct 13, 2026 at 11:01:16PM +0200, Danilo Krummrich wrote:
> On Tue Oct 13, 2026 at 9:11 PM CEST, Alan Stern wrote:
> > On Tue, Oct 13, 2026 at 07:43:08PM +0200, Danilo Krummrich wrote:
> >> On Tue Oct 13, 2026 at 4:06 PM CEST, Johan Hovold wrote:
> >> > +void bus_probe_device_deferred(struct device *dev)
> >> > +{
> >> > + if (dev->parent && dev->bus->need_parent_lock)
> >> > + device_lock(dev->parent);
> >>
> >> This looks like it has the same potential UAF as the other patch. It's quite
> >> unfortunate we need the whole need_parent_lock thing just for USB. IIUC the
> >> main reason USB needs this is because interface devices share resources of USB
> >> devices and we need to keep the locking order to avoid inversions, right? Is
> >> there more to this from the USB side of things?
> >
> > That's pretty much it.
> >
> >> I think we can probably do something like this
> >>
> >> struct device *parent = NULL;
> >>
> >> scoped_guard(device, dev) {
> >> if (dev->p->dead)
> >> return;
> >>
> >> if (dev->bus->need_parent_lock)
> >> parent = get_device(dev->parent);
> >> }
> >>
> >> to avoid the problem, but it's still a bit ugly.
> >>
> >> > + __bus_probe_device(dev);
> >> > + if (dev->parent && dev->bus->need_parent_lock)
> >> > + device_unlock(dev->parent);
> >> > +}
> >
> > Wouldn't it be simpler just to make each USB interface take a reference
> > to its parent device? And likewise for USB devices, if needed?
>
> The driver core already holds a reference count of the parent between
> device_add() and device_del(), which is where a "parent" is technically defined.
> But the deferred retry can extend beyond device_del().

There could be another reference that extends all the way to the child's
release.

> USB interfaces already take an additional reference to their USB device in
> usb_set_configuration() and drop it in usb_release_interface(), so they should
> already be protected against this particular UAF.
>
> The issue concerns a USB device and its parent hub's USB device. (This makes me
> wonder whether probing a USB device actually requires the parent USB HUB
> device's lock to be held, or whether this is just a consequence of sharing the
> same usb_bus_type?)

The latter.

> In theory a USB device could also keep a reference count to its parent, but then
> the driver core code would rely on an implementation detail of its users; I'd
> prefer not to make driver core's access to dev->parent depend on references
> maintained by individual subsystems.
>
> I'd even go a step further any say that if subsystems try to keep this pointer
> valid beyond what's guaranteed by the driver core, they should do this with a
> separate bus specific pointer. The reason is that otherwise the lifetime and
> ownership definition of this pointer does not depend on API contracts, but on
> individual implementation details, which is a very common source for all kinds
> of bugs.

I was going to suggest that the driver core could acquire and release
these extra references automatically for devices on a bus with
need_parent_lock set, but it looks like that can't be done because the
bus_type might not yet be set when device_initialize() runs.

However, is there any real reason why the driver core wouldn't want to
guarantee that dev->parent will be a valid pointer up until the time
when dev is released? For every device that has a parent, not just USB?

On the other hand, there's something a bit weird about this whole thing.
The problem arises when you want to probe a device whose parent has been
deleted. Isn't there some sort of protection to prevent that from ever
happening? I mean, doesn't deleting a device imply that all its
descendants have been (or are in the process of being) deleted? And of
course, deleting and probing are mutually exclusive -- they can't race,
right?

Alan Stern