Commit afd7fd81 authored by Alan Stern's avatar Alan Stern Committed by Felipe Balbi

USB: Gadget core: fix inconsistency in the interface tousb_add_gadget_udc_release()

The usb_add_gadget_udc_release() routine in the USB gadget core will
sometimes but not always call the gadget's release function when an
error occurs.  More specifically, if the struct usb_udc allocation
fails then the release function is not called, and for other errors it
is.

As a result, users of this routine cannot know whether they need to
deallocate the memory containing the gadget structure following an
error.  This leads to unavoidable memory leaks or double frees.

This patch fixes the problem by splitting the existing
device_register() call into device_initialize() and device_add(), and
doing the udc allocation in between.  That way, even if the allocation
fails it is still possible to call device_del(), and so the release
function will be always called following an error.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Reported-by: default avatarAlexey Khoroshilov <khoroshilov@ispras.ru>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent c37a9cd5
...@@ -1133,6 +1133,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc) ...@@ -1133,6 +1133,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc)
* @release: a gadget release function. * @release: a gadget release function.
* *
* Returns zero on success, negative errno otherwise. * Returns zero on success, negative errno otherwise.
* Calls the gadget release function in the latter case.
*/ */
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev)) void (*release)(struct device *dev))
...@@ -1140,10 +1141,6 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, ...@@ -1140,10 +1141,6 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
struct usb_udc *udc; struct usb_udc *udc;
int ret = -ENOMEM; int ret = -ENOMEM;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
if (!udc)
goto err1;
dev_set_name(&gadget->dev, "gadget"); dev_set_name(&gadget->dev, "gadget");
INIT_WORK(&gadget->work, usb_gadget_state_work); INIT_WORK(&gadget->work, usb_gadget_state_work);
gadget->dev.parent = parent; gadget->dev.parent = parent;
...@@ -1153,7 +1150,13 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, ...@@ -1153,7 +1150,13 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
else else
gadget->dev.release = usb_udc_nop_release; 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) if (ret)
goto err2; goto err2;
...@@ -1200,10 +1203,10 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, ...@@ -1200,10 +1203,10 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
device_del(&gadget->dev); device_del(&gadget->dev);
err2: err2:
put_device(&gadget->dev);
kfree(udc); kfree(udc);
err1: err1:
put_device(&gadget->dev);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment