Linux USB
 help / color / mirror / Atom feed
* [PATCH 0/3] usb: core: fix three use-after-free windows in the USB anchor protocol
@ 2026-10-22  3:53 Hongyu Xie
  2026-10-22  3:53 ` [PATCH 1/3] usb: core: Fix use-after-free in anchor wakeup protocol Hongyu Xie
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Hongyu Xie @ 2026-10-22  3:53 UTC (permalink / raw)
  To: Greg KH, Alan Stern; +Cc: linux-usb, Oliver Neukum, Hans de Goede, linux-kernel

Anchors are usually embedded in driver-private structures, and several
anchor API returns -- a non-zero usb_wait_anchor_empty_timeout(),
usb_kill_anchored_urbs(), usb_poison_anchored_urbs() and
usb_scuttle_anchored_urbs() -- license the caller to free that memory.
The wakeup protocol backing this license has three windows in which a
URB giveback or unanchor still touches the anchor after the caller has
been told it is unused:

 1. The waiter's condition check and the giveback's suspend_wakeups
    decrement plus wake_up() were not serialized, so the waiter could
    return while a giveback was still in flight.  Reported by syzbot
    repeatedly since 2019; patch 1 references the two reports this
    analysis is built on.

 2. __usb_hcd_giveback_urb() captured urb->anchor at the top of the
    function but incremented suspend_wakeups only after DMA unmapping
    and the usbmon hooks.  A concurrent unanchor could empty the anchor
    with suspend_wakeups still zero, so the waiter returned and freed
    the anchor underneath the pending increment.  No syzbot report is
    attributed to this interleaving; it was found by extending the
    analysis of patch 1.  Patch 2 folds the capture, the increment and
    the unanchor into a single anchor->lock critical section.

 3. usb_unanchor_urb() and the giveback read urb->anchor and only then
    acquire anchor->lock; between the two the anchor can already have
    been freed.  This gap is as old as usb_unanchor_urb() itself and
    cannot be closed with a reference count: the first dereference of
    the freshly read pointer would have to be the increment itself.
    Patch 3 therefore turns the wakeup protocol into a lifetime
    protocol: the lockless anchor reads run in RCU read-side critical
    sections, urb->anchor is published with rcu_assign_pointer(), and
    every return that lets the caller free the anchor holds a
    suspend_wakeups pin across one synchronize_rcu() first.

Patch 3 changes contracts, stated up front:

 - usb_kill_anchored_urbs(), usb_poison_anchored_urbs() and
   usb_scuttle_anchored_urbs() may now sleep even on an empty anchor.
   All in-tree callers already sleep (they sit next to usb_kill_urb()
   calls).

 - usb_wait_anchor_empty_timeout() returns 1 on success instead of the
   remaining jiffies, and a successful wait -- including one with a
   zero timeout -- pays one RCU grace period.

 - The cost is one grace period per successful teardown exit.  On
   x86-64 real hardware, 100 consecutive successful waits on an empty
   anchor cost 3.1 us per call on the base kernel and 8.2 us with this
   series, about +5 us per call.  Callers that wait once per batch
   (zd1211rw) or tear down several anchors at close (btusb) pay this
   per anchor; it is small next to the URB I/O these paths bracket,
   but it is the price of the free-after-return guarantee.

No fields are added to struct usb_anchor or struct urb, and no driver
changes are needed.

Testing:

 - A QEMU/KASAN attack harness reproduced each window separately
   (deterministic arming for the two giveback windows, a forced free
   inside the read-to-lock gap for the third) and verified that each
   patch closes its own window with zero reports under identical
   pressure.

 - The full stack also ran on real x86-64 hardware with KASAN kernels
   built from the same base.  The base kernel hits every probe: both
   deterministic windows fire within 100 iterations (hundreds of probe
   arm hits), and the natural-race variants hit within 2000 iterations
   with no arming at all.  The kernel with all three patches runs the
   identical workload with zero reports -- the attack still executes
   there, its probes arm hundreds of times too, only the windows are
   gone.  The zero-timeout contract probe (empty anchor, timeout 0)
   returns 1 as documented.

checkpatch: 0 errors, 0 warnings for all three patches.  Patch 2 keeps
one CHECK for an extern prototype in a header, matching the prevailing
style of that header.

Signed-off-by: Hongyu Xie <xiehongyu1@kylinos.cn>

Hongyu Xie (3):
  usb: core: Fix use-after-free in anchor wakeup protocol
  usb: core: Close anchor capture-to-increment race in URB giveback
  usb: core: Close residual anchor read-to-lock window with RCU

 drivers/usb/core/hcd.c |  10 +-
 drivers/usb/core/urb.c | 303 +++++++++++++++++++++++++++++++++++------
 drivers/usb/core/usb.h |   1 +
 include/linux/usb.h    |   7 +-
 4 files changed, 277 insertions(+), 44 deletions(-)


base-commit: 22430ae5d90ab288b0ee2ad99ae941f4a666b694
-- 
2.32.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-10-22 23:07 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-22  3:53 [PATCH 0/3] usb: core: fix three use-after-free windows in the USB anchor protocol Hongyu Xie
2026-10-22  3:53 ` [PATCH 1/3] usb: core: Fix use-after-free in anchor wakeup protocol Hongyu Xie
2026-10-22  4:00   ` sashiko-bot
2026-10-22  3:53 ` [PATCH 2/3] usb: core: Close anchor capture-to-increment race in URB giveback Hongyu Xie
2026-10-22  3:58   ` sashiko-bot
2026-10-22  3:53 ` [PATCH 3/3] usb: core: Close residual anchor read-to-lock window with RCU Hongyu Xie
2026-10-22  4:21   ` sashiko-bot
2026-10-22 17:29     ` What happened with Sashiko? Alan Stern
2026-10-22 23:07       ` Roman Gushchin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox