From: sashiko-bot@kernel.org
To: "Igor Putko" <igorpetindev@gmail.com>
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH] usb: core: hcd: cancel giveback work synchronously on stop
Date: Fri, 23 Oct 2026 18:06:44 +0000 [thread overview]
Message-ID: <sashiko-outbox-183042@kernel.org> (raw)
In-Reply-To: <20261023174015.54793-1-igorpetindev@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] usb: core: hcd: cancel_work_sync() in usb_stop_hcd() strands pending BH URBs
--
commit 6e7c5c14d78d88d1d1fec5ad2d48ff9cf59b253d
Author: Igor Putko <igorpetindev@gmail.com>
usb: core: hcd: cancel giveback work synchronously on stop
This commit adds cancel_work_sync() calls for both high and low priority
giveback BH work items in usb_stop_hcd() to prevent use-after-free accesses
to struct usb_hcd when usb_giveback_urb_bh() is still running during HCD
teardown.
> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> index a865a15ea3863..e0f00d68598a1 100644
> --- a/drivers/usb/core/hcd.c
> +++ b/drivers/usb/core/hcd.c
> @@ -2788,6 +2788,9 @@ static void usb_stop_hcd(struct usb_hcd *hcd)
> /* In case the HCD restarted the timer, stop it again. */
> clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
> timer_delete_sync(&hcd->rh_timer);
> +
> + cancel_work_sync(&hcd->high_prio_bh.bh);
> + cancel_work_sync(&hcd->low_prio_bh.bh);
[Severity: High]
Could calling cancel_work_sync() instead of draining the BH work items leave
URBs stranded on hcd->high_prio_bh.head and hcd->low_prio_bh.head, leaking
struct urb references and DMA mappings or hanging callers in usb_kill_urb()?
Unlike flush_work(), cancel_work_sync() steals the pending bit if &bh->bh has
not started executing yet, so usb_giveback_urb_bh() never runs for URBs
already queued on bh->head. In addition, if usb_giveback_urb_bh() is already
running a snapshot when new URBs are added to bh->head, it relies on
re-queueing &bh->bh before returning:
drivers/usb/core/hcd.c:usb_giveback_urb_bh() {
...
spin_lock_irq(&bh->lock);
if (!list_empty(&bh->head)) {
if (bh->high_prio)
queue_work(system_bh_highpri_wq, &bh->bh);
else
queue_work(system_bh_wq, &bh->bh);
}
bh->running = false;
spin_unlock_irq(&bh->lock);
}
Because cancel_work_sync() sets WORK_CANCEL_DISABLE while waiting for the
current execution to finish, that queue_work() call is silently dropped.
This can be reached in two ways during HCD teardown:
1. In usb_stop_hcd(), hcd->driver->stop(hcd) runs right before
cancel_work_sync(). For HCD drivers with HCD_BH set that complete remaining
URBs in their stop callback, such as in drivers/usb/dwc2/hcd.c:
_dwc2_hcd_stop()
-> dwc2_hcd_disconnect()
-> dwc2_kill_all_urbs()
-> dwc2_host_complete()
-> usb_hcd_giveback_urb()
URBs are added to bh->head and &bh->bh is queued immediately before
cancel_work_sync() cancels the pending work item.
2. During usb_remove_hcd() -> usb_disconnect() -> usb_disable_device() ->
usb_hcd_flush_endpoint(), usb_hcd_unlink_urb_from_ep() removes the URB from
ep->urb_list before usb_hcd_giveback_urb() appends it to bh->head. In
drivers/usb/core/hcd.c:usb_hcd_flush_endpoint():
/* Wait until the endpoint queue is completely empty */
while (!list_empty (&ep->urb_list)) {
this loop can observe an empty ep->urb_list and return without calling
usb_kill_urb() while the URB is still sitting on bh->head, allowing
usb_stop_hcd() to cancel &bh->bh before usb_giveback_urb_bh() processes it.
Any URB stranded on bh->head never reaches __usb_hcd_giveback_urb(), so it
is never unmapped via unmap_urb_for_dma(), its struct urb reference from
usb_get_urb() is leaked, urb->complete() is skipped, and urb->use_count
remains non-zero (which hangs any caller waiting in usb_kill_urb() or
usb_poison_urb()).
Also, should the comment in drivers/usb/core/hcd.c:usb_remove_hcd() right
above usb_stop_hcd(hcd):
/*
* flush_work() isn't needed here because:
* - driver's disconnect() called from usb_disconnect() should
* make sure its URBs are completed during the disconnect()
* callback
*
* - it is too late to run complete() here since driver may have
* been removed already now
*/
be updated now that usb_stop_hcd() synchronously waits on the BH work items?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261023174015.54793-1-igorpetindev@gmail.com?part=1
next prev parent reply other threads:[~2026-10-23 18:06 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 17:40 [PATCH] usb: core: hcd: cancel giveback work synchronously on stop Igor Putko
2026-10-23 18:06 ` sashiko-bot [this message]
2026-10-23 19:15 ` Alan Stern
2026-10-23 19:31 ` IgorpetinDev
2026-10-24 1:53 ` Alan Stern
2026-10-24 8:36 ` [PATCH v2] usb: core: hcd: flush giveback work " Igor Putko
2026-10-24 9:11 ` sashiko-bot
2026-10-24 15:10 ` Alan Stern
2026-10-24 15:56 ` [PATCH v3] " Igor Putko
2026-10-24 16:11 ` sashiko-bot
2026-10-24 18:28 ` Alan Stern
2026-10-24 22:06 ` Michal Pecio
2026-10-25 7:54 ` IgorpetinDev
2026-10-25 15:16 ` Alan Stern
2026-10-25 16:10 ` Michal Pecio
2026-10-26 1:09 ` Alan Stern
2026-10-29 9:42 ` Greg Kroah-Hartman
2026-10-29 10:44 ` Michal Pecio
2026-10-29 14:21 ` Alan Stern
2026-10-29 11:47 ` [PATCH v4] " Igor Putko
2026-10-29 11:53 ` 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=sashiko-outbox-183042@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=igorpetindev@gmail.com \
--cc=linux-usb@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.