From: Andrew Jeffery <andrew@codeconstruct.com.au>
To: Ruoyu Wang <ruoyuw560@gmail.com>,
Neal Liu <neal_liu@aspeedtech.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Joel Stanley <joel@jms.id.au>,
linux-aspeed@lists.ozlabs.org, linux-usb@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH] usb: gadget: aspeed_udc: check endpoint DMA allocation
Date: Wed, 10 Jun 2026 21:15:46 +0930 [thread overview]
Message-ID: <9f2caf9887177bad39525a6713325c252ad47dcb.camel@codeconstruct.com.au> (raw)
In-Reply-To: <20260608081948.3-1-ruoyuw560@gmail.com>
On Mon, 2026-06-08 at 16:19 +0800, Ruoyu Wang wrote:
> ast_udc_probe() allocates a coherent DMA buffer used as the backing store
> for endpoint buffers. ast_udc_init_ep() derives per-endpoint buffer
> pointers from udc->ep0_buf, so a failed allocation is dereferenced during
> probe.
>
> Check the allocation before endpoint setup. The existing probe error path
> called ast_udc_remove(), which unregisters the gadget unconditionally and
> is not safe before usb_add_gadget_udc() succeeds. Add a local cleanup
> helper for probe failures so pre-registration failures only unwind the
> resources that were actually initialized.
>
> This was found by a local static analysis checker for unchecked allocator
> returns while scanning Linux 6.16. The change was checked by applying it
> to current mainline and by running checkpatch. I do not have access to
> Aspeed UDC hardware, so no runtime testing was performed.
>
> Fixes: 055276c13205 ("usb: gadget: add Aspeed ast2600 udc driver")
> Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
> ---
> Note: a 2022 patch attempted to add only a NULL check for this
> allocation:
> https://lore.kernel.org/all/20221213025120.23149-1-jiasheng@iscas.ac.cn/
>
> This version also fixes the probe unwind path so the clock is disabled
> on allocation failure and usb_del_gadget_udc() is not called before the
> gadget has been registered.
>
> diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c
> index 7fc6696b7..809a7d5b7 100644
> --- a/drivers/usb/gadget/udc/aspeed_udc.c
> +++ b/drivers/usb/gadget/udc/aspeed_udc.c
> @@ -1434,11 +1434,34 @@ static void ast_udc_init_hw(struct ast_udc_dev *udc)
> ast_udc_write(udc, 0, AST_UDC_EP0_CTRL);
> }
>
> +static void ast_udc_cleanup(struct platform_device *pdev)
> +{
> + struct ast_udc_dev *udc = platform_get_drvdata(pdev);
> + unsigned long flags;
> + u32 ctrl;
> +
> + spin_lock_irqsave(&udc->lock, flags);
> +
> + /* Disable upstream port connection */
> + ctrl = ast_udc_read(udc, AST_UDC_FUNC_CTRL) & ~USB_UPSTREAM_EN;
> + ast_udc_write(udc, ctrl, AST_UDC_FUNC_CTRL);
> +
> + clk_disable_unprepare(udc->clk);
> +
> + spin_unlock_irqrestore(&udc->lock, flags);
> +
> + if (udc->ep0_buf)
> + dma_free_coherent(&pdev->dev,
> + AST_UDC_EP_DMA_SIZE * AST_UDC_NUM_ENDPOINTS,
> + udc->ep0_buf,
> + udc->ep0_buf_dma);
> +
> + udc->ep0_buf = NULL;
> +}
> +
> static void ast_udc_remove(struct platform_device *pdev)
> {
> struct ast_udc_dev *udc = platform_get_drvdata(pdev);
> - unsigned long flags;
> - u32 ctrl;
>
> usb_del_gadget_udc(&udc->gadget);
> if (udc->driver) {
> @@ -1453,23 +1476,7 @@ static void ast_udc_remove(struct platform_device *pdev)
> return;
> }
>
> - spin_lock_irqsave(&udc->lock, flags);
> -
> - /* Disable upstream port connection */
> - ctrl = ast_udc_read(udc, AST_UDC_FUNC_CTRL) & ~USB_UPSTREAM_EN;
> - ast_udc_write(udc, ctrl, AST_UDC_FUNC_CTRL);
> -
> - clk_disable_unprepare(udc->clk);
> -
> - spin_unlock_irqrestore(&udc->lock, flags);
> -
> - if (udc->ep0_buf)
> - dma_free_coherent(&pdev->dev,
> - AST_UDC_EP_DMA_SIZE * AST_UDC_NUM_ENDPOINTS,
> - udc->ep0_buf,
> - udc->ep0_buf_dma);
> -
> - udc->ep0_buf = NULL;
> + ast_udc_cleanup(pdev);
> }
>
> static int ast_udc_probe(struct platform_device *pdev)
> @@ -1523,6 +1530,10 @@ static int ast_udc_probe(struct platform_device *pdev)
> AST_UDC_EP_DMA_SIZE *
> AST_UDC_NUM_ENDPOINTS,
> &udc->ep0_buf_dma, GFP_KERNEL);
> + if (!udc->ep0_buf) {
> + rc = -ENOMEM;
> + goto err_disable_clk;
> + }
>
> udc->gadget.speed = USB_SPEED_UNKNOWN;
> udc->gadget.max_speed = USB_SPEED_HIGH;
> @@ -1553,20 +1564,20 @@ static int ast_udc_probe(struct platform_device *pdev)
> udc->irq = platform_get_irq(pdev, 0);
> if (udc->irq < 0) {
> rc = udc->irq;
> - goto err;
> + goto err_cleanup;
> }
>
> rc = devm_request_irq(&pdev->dev, udc->irq, ast_udc_isr, 0,
> KBUILD_MODNAME, udc);
> if (rc) {
> dev_err(&pdev->dev, "Failed to request interrupt\n");
> - goto err;
> + goto err_cleanup;
> }
>
> rc = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
> if (rc) {
> dev_err(&pdev->dev, "Failed to add gadget udc\n");
> - goto err;
> + goto err_cleanup;
> }
>
> dev_info(&pdev->dev, "Initialized udc in USB%s mode\n",
> @@ -1574,9 +1585,14 @@ static int ast_udc_probe(struct platform_device *pdev)
>
> return 0;
>
> +err_disable_clk:
> + clk_disable_unprepare(udc->clk);
> + goto err;
> +err_cleanup:
> + ast_udc_cleanup(pdev);
> + goto err;
> err:
That last goto is unnecessary.
However, I find it unsettling that in a patch fixing resource handling
we add a mildly convoluted cleanup path, with portions jumping over
each other in this way.
The err_disable_clk label is only used once, and itself jumps down to
the err label. This is the case because beyond its goto we free udc-
>ep0_buf in ast_udc_cleanup(). I think it would make more sense to move
the call to clk_disable_unprepare() into the conditional body of the
allocation failure test, then change its goto label to 'err'. That way
the above hunk becomes:
+err_cleanup:
+ ast_udc_cleanup(pdev);
err:
...
Which seems a bit more natural.
Andrew
> dev_err(&pdev->dev, "Failed to udc probe, rc:0x%x\n", rc);
> - ast_udc_remove(pdev);
>
> return rc;
> }
next prev parent reply other threads:[~2026-06-10 11:46 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-08 8:19 [PATCH] usb: gadget: aspeed_udc: check endpoint DMA allocation Ruoyu Wang
2026-06-10 11:45 ` Andrew Jeffery [this message]
2026-06-10 12:03 ` Ruoyu Wang
2026-06-10 12:10 ` [PATCH v2] " Ruoyu Wang
2026-06-11 5:06 ` Andrew Jeffery
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=9f2caf9887177bad39525a6713325c252ad47dcb.camel@codeconstruct.com.au \
--to=andrew@codeconstruct.com.au \
--cc=gregkh@linuxfoundation.org \
--cc=joel@jms.id.au \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-aspeed@lists.ozlabs.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=neal_liu@aspeedtech.com \
--cc=ruoyuw560@gmail.com \
/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.