Re: [PATCH] usb: free iso schedules on failed submit
From: Alan Stern
Date: Sat Jun 27 2026 - 15:06:15 EST
On Sat, Jun 27, 2026 at 02:02:07PM +0800, Dawei Feng wrote:
> EHCI and FOTG210 isochronous submits build an ehci_iso_sched before
> linking the URB to the endpoint queue, and keep the staged schedule in
> urb->hcpriv until iso_stream_schedule() and the link helpers consume it.
> If the controller is no longer accessible, or usb_hcd_link_urb_to_ep()
> fails, submit jumps to done_not_linked before that handoff happens and
> leaks the staged schedule still attached to urb->hcpriv.
>
> Free the staged schedule from done_not_linked when submit fails before
> the URB is linked and clear urb->hcpriv after the free.
>
> The bug was first flagged by an experimental analysis tool we are
> developing for kernel memory-management bugs while analyzing
> v6.13-rc1. The tool is still under development and is not yet publicly
> available. Manual inspection confirms that the bug is still
> present in v7.1.1.
>
> An x86_64 allyesconfig build showed no new warnings. As we do not have an
> EHCI host controller with a USB isochronous device to test with, no
> runtime testing was able to be performed.
>
> Fixes: 8de98402652c ("[PATCH] USB: Fix USB suspend/resume crasher (#2)")
> Fixes: e9df41c5c589 ("USB: make HCDs responsible for managing endpoint queues")
> Fixes: 7d50195f6c50 ("usb: host: Faraday fotg210-hcd driver")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Dawei Feng <dawei.feng@xxxxxxxxxx>
> ---
> drivers/usb/fotg210/fotg210-hcd.c | 4 ++++
> drivers/usb/host/ehci-sched.c | 8 ++++++++
> 2 files changed, 12 insertions(+)
>
> diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c
> index 1a48329a4e08..d92b11d488a5 100644
> --- a/drivers/usb/fotg210/fotg210-hcd.c
> +++ b/drivers/usb/fotg210/fotg210-hcd.c
> @@ -4562,6 +4562,10 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb,
> else
> usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb);
> done_not_linked:
> + if (status < 0) {
> + iso_sched_free(stream, urb->hcpriv);
> + urb->hcpriv = NULL;
> + }
> spin_unlock_irqrestore(&fotg210->lock, flags);
> done:
> return status;
> diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
> index a241337c9af8..33a0111cfb37 100644
> --- a/drivers/usb/host/ehci-sched.c
> +++ b/drivers/usb/host/ehci-sched.c
> @@ -1966,6 +1966,10 @@ static int itd_submit(struct ehci_hcd *ehci, struct urb *urb,
> usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
> }
> done_not_linked:
> + if (status < 0) {
> + iso_sched_free(stream, urb->hcpriv);
> + urb->hcpriv = NULL;
> + }
That's not quite optimal, because iso_stream_schedule() already calls
iso_sched_free() whenever its return value is < 0. You should remove
that call now that the deallocation is being done down here. And also
have iso_stream_schedule() clear urb->hcpriv in the other place where it
calls iso_sched_free().
Aside from that, this looks good.
Alan Stern
> spin_unlock_irqrestore(&ehci->lock, flags);
> done:
> return status;
> @@ -2343,6 +2347,10 @@ static int sitd_submit(struct ehci_hcd *ehci, struct urb *urb,
> usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
> }
> done_not_linked:
> + if (status < 0) {
> + iso_sched_free(stream, urb->hcpriv);
> + urb->hcpriv = NULL;
> + }
> spin_unlock_irqrestore(&ehci->lock, flags);
> done:
> return status;
> --
> 2.34.1