Re: Inconsistency in usb_add_gadget_udc_release() interface

From: Alan Stern
Date: Wed Aug 16 2017 - 11:24:58 EST


On Wed, 16 Aug 2017, Alexey Khoroshilov wrote:

> Hello,
>
> usb_add_gadget_udc_release() gets release() argument that allows to
> release user resources.
>
> As far as I can see, the release() is called on error paths
> of usb_add_gadget_udc_release() as a result of
> put_device(&gadget->dev);
> except for the only path going via err1.
>
> As a result a caller of the usb_add_gadget_udc_release() have no chance
> to know if the release() was invoked or not.
>
> It may lead to memory leaks (drivers/usb/gadget/udc/snps_udc_core.c)
> or to double free (drivers/usb/gadget/udc/fsl_udc_core.c).
>
> Is my reading correct? If so, should we always call release() on error paths?

How about this (untested)?

Alan Stern


Index: usb-4.x/drivers/usb/gadget/udc/core.c
===================================================================
--- usb-4.x.orig/drivers/usb/gadget/udc/core.c
+++ usb-4.x/drivers/usb/gadget/udc/core.c
@@ -1137,10 +1137,6 @@ int usb_add_gadget_udc_release(struct de
struct usb_udc *udc;
int ret = -ENOMEM;

- udc = kzalloc(sizeof(*udc), GFP_KERNEL);
- if (!udc)
- goto err1;
-
dev_set_name(&gadget->dev, "gadget");
INIT_WORK(&gadget->work, usb_gadget_state_work);
gadget->dev.parent = parent;
@@ -1150,7 +1146,13 @@ int usb_add_gadget_udc_release(struct de
else
gadget->dev.release = usb_udc_nop_release;

- ret = device_register(&gadget->dev);
+ device_initialize(&gadget->dev);
+
+ udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ goto err1;
+
+ ret = device_add(&gadget->dev);
if (ret)
goto err2;

@@ -1197,10 +1199,10 @@ err3:
device_del(&gadget->dev);

err2:
- put_device(&gadget->dev);
kfree(udc);

err1:
+ put_device(&gadget->dev);
return ret;
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);