Linux USB
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Igor Putko" <igorpetindev@gmail.com>
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH v2] usb: core: hcd: flush giveback work on stop
Date: Sat, 24 Oct 2026 09:11:11 +0000	[thread overview]
Message-ID: <sashiko-outbox-183606@kernel.org> (raw)
In-Reply-To: <20261024083647.2599-1-igorpetindev@gmail.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] usb: core: hcd: incomplete bh work flush in usb_stop_hcd() on self-requeue
--

commit 9c9e1488a3b8b0e4cd42d6243a942c8d42aeb233
Author: Igor Putko <igorpetindev@gmail.com>

usb: core: hcd: flush giveback work on stop

This commit adds flush_work() calls on the high and low priority BH work
items in usb_stop_hcd() to prevent a use-after-free on struct usb_hcd when
usb_giveback_urb_bh() is still executing as the HCD is stopped and freed.

> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> index a865a15ea3863..9db0f39896065 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);
> +
> +	flush_work(&hcd->high_prio_bh.bh);
> +	flush_work(&hcd->low_prio_bh.bh);

[Severity: Critical]
Is a single flush_work() call on each BH work item sufficient to drain all
pending givebacks if usb_giveback_urb_bh() requeues itself?

When an instance of usb_giveback_urb_bh() starts running, process_one_work()
clears WORK_STRUCT_PENDING_BIT and WORK_STRUCT_PWQ on &bh->bh, and
usb_giveback_urb_bh() sets bh->running = true while moving bh->head to
local_list.

If another URB is passed to usb_hcd_giveback_urb() while bh->running is true,
it is appended to bh->head without calling queue_work():

drivers/usb/core/hcd.c:usb_hcd_giveback_urb() {
	...
	spin_lock(&bh->lock);
	list_add_tail(&urb->urb_list, &bh->head);
	running = bh->running;
	spin_unlock(&bh->lock);

	if (running)
		;
	else if (bh->high_prio)
		queue_work(system_bh_highpri_wq, &bh->bh);
	else
		queue_work(system_bh_wq, &bh->bh);
}

This can happen without a caller waiting in usb_kill_urb(). For example,
during usb_remove_hcd() -> usb_disconnect(), usb_hcd_flush_endpoint() calls
unlink1(), where usb_hcd_unlink_urb_from_ep() removes the URB from
ep->urb_list before usb_hcd_giveback_urb() adds it to bh->head. The second
loop in usb_hcd_flush_endpoint():

	/* Wait until the endpoint queue is completely empty */
	while (!list_empty (&ep->urb_list)) {

then sees ep->urb_list as empty and returns without calling usb_kill_urb().
Similarly, HCD ->stop(hcd) callbacks invoked in usb_stop_hcd() (such as
_dwc2_hcd_stop() -> dwc2_hcd_disconnect() -> dwc2_kill_all_urbs()) can give
back remaining URBs via usb_hcd_giveback_urb() while an earlier execution of
usb_giveback_urb_bh() is still in progress.

If usb_stop_hcd() calls flush_work(&bh->bh) before the running worker reaches
the end of usb_giveback_urb_bh(), &bh->bh is not marked pending, so
start_flush_work() -> insert_wq_barrier() places the flush barrier on
worker->scheduled:

kernel/workqueue.c:insert_wq_barrier() {
	...
	if (worker) {
		head = worker->scheduled.next;
		work_color = worker->current_color;
	}
	...
}

When the running usb_giveback_urb_bh() then finishes its loop, it sees the
newly added URB on bh->head and requeues &bh->bh onto pool->worklist:

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);
}

When that first execution returns, process_scheduled_works() immediately runs
the barrier on worker->scheduled and wakes flush_work() before bh_worker()
picks up the requeued &bh->bh from pool->worklist.

Could usb_stop_hcd() then return and allow usb_put_hcd() to free struct
usb_hcd while the requeued usb_giveback_urb_bh() is still pending or running?
Also, does the comment above usb_stop_hcd() in usb_remove_hcd() stating that
flush_work() isn't needed there need to be updated?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261024083647.2599-1-igorpetindev@gmail.com?part=1

  reply	other threads:[~2026-10-24  9:11 UTC|newest]

Thread overview: 16+ 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
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 [this message]
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

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-183606@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox