mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
usb: gadget: aspeed_udc: check endpoint DMA allocation
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: 055276c132 ("usb: gadget: add Aspeed ast2600 udc driver")
Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au>
Link: https://patch.msgid.link/20260610121022.3-1-ruoyuw560@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
83f0355cf4
commit
97cee53a94
|
|
@ -1431,25 +1431,12 @@ static void ast_udc_init_hw(struct ast_udc_dev *udc)
|
|||
ast_udc_write(udc, 0, AST_UDC_EP0_CTRL);
|
||||
}
|
||||
|
||||
static void ast_udc_remove(struct platform_device *pdev)
|
||||
static void ast_udc_cleanup(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) {
|
||||
/*
|
||||
* This is broken as only some cleanup is skipped, *udev is
|
||||
* freed and the register mapping goes away. Any further usage
|
||||
* probably crashes. Also the device is unbound, so the skipped
|
||||
* cleanup is never catched up later.
|
||||
*/
|
||||
dev_alert(&pdev->dev,
|
||||
"Driver is busy and still going away. Fasten your seat belts!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
/* Disable upstream port connection */
|
||||
|
|
@ -1469,6 +1456,26 @@ static void ast_udc_remove(struct platform_device *pdev)
|
|||
udc->ep0_buf = NULL;
|
||||
}
|
||||
|
||||
static void ast_udc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ast_udc_dev *udc = platform_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&udc->gadget);
|
||||
if (udc->driver) {
|
||||
/*
|
||||
* This is broken as only some cleanup is skipped, *udev is
|
||||
* freed and the register mapping goes away. Any further usage
|
||||
* probably crashes. Also the device is unbound, so the skipped
|
||||
* cleanup is never catched up later.
|
||||
*/
|
||||
dev_alert(&pdev->dev,
|
||||
"Driver is busy and still going away. Fasten your seat belts!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ast_udc_cleanup(pdev);
|
||||
}
|
||||
|
||||
static int ast_udc_probe(struct platform_device *pdev)
|
||||
{
|
||||
enum usb_device_speed max_speed;
|
||||
|
|
@ -1521,6 +1528,12 @@ static int ast_udc_probe(struct platform_device *pdev)
|
|||
AST_UDC_NUM_ENDPOINTS,
|
||||
&udc->ep0_buf_dma, GFP_KERNEL);
|
||||
|
||||
if (!udc->ep0_buf) {
|
||||
clk_disable_unprepare(udc->clk);
|
||||
rc = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
udc->gadget.max_speed = USB_SPEED_HIGH;
|
||||
udc->creq = udc->reg + AST_UDC_SETUP0;
|
||||
|
|
@ -1550,20 +1563,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",
|
||||
|
|
@ -1571,9 +1584,10 @@ static int ast_udc_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
err_cleanup:
|
||||
ast_udc_cleanup(pdev);
|
||||
err:
|
||||
dev_err(&pdev->dev, "Failed to udc probe, rc:0x%x\n", rc);
|
||||
ast_udc_remove(pdev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user