Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: UDC: net2280: Fix memory leaks

As Anton and Evgeny have noted, the net2280 UDC driver has a problem
with leaking memory along some of its failure pathways. It also has
another problem, not previously noted, in that some of the failure
pathways will call usb_del_gadget_udc() without first calling
usb_add_gadget_udc_release(). And it leaks memory by calling kfree()
when it should call put_device().

Previous attempts to fix the problems have failed because of lack of
support in the UDC core for separately initializing and adding
gadgets, or for separately deleting and freeing gadgets. The previous
patch in this series adds the necessary support, making it possible to
fix the outstanding problems properly.

This patch adds an "added" flag to the net2280 structure to indicate
whether or not the gadget has been registered (and thus whether or not
to call usb_del_gadget()), and it fixes the deallocation issues by
calling usb_put_gadget() at the appropriate point.

A similar memory leak issue, apparently never before recognized, stems
from the fact that the driver never initializes the drvdata field in
the gadget's embedded struct device! Evidently this wasn't noticed
because the pointer is only ever used as an argument to kfree(), which
doesn't mind getting called with a NULL pointer. In fact, the drvdata
for gadget device will be written by usb_composite_dev structure if
any gadget class is loaded, so it needs to use usb_gadget structure
to get net2280 private data.

CC: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Reported-by: Anton Vasilyev <vasilyev@ispras.ru>
Reported-by: Evgeny Novikov <novikov@ispras.ru>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>

authored by

Alan Stern and committed by
Felipe Balbi
f770fbec 3301c215

+8 -4
+7 -4
drivers/usb/gadget/udc/net2280.c
··· 3561 3561 3562 3562 static void gadget_release(struct device *_dev) 3563 3563 { 3564 - struct net2280 *dev = dev_get_drvdata(_dev); 3564 + struct net2280 *dev = container_of(_dev, struct net2280, gadget.dev); 3565 3565 3566 3566 kfree(dev); 3567 3567 } ··· 3572 3572 { 3573 3573 struct net2280 *dev = pci_get_drvdata(pdev); 3574 3574 3575 - usb_del_gadget_udc(&dev->gadget); 3575 + if (dev->added) 3576 + usb_del_gadget(&dev->gadget); 3576 3577 3577 3578 BUG_ON(dev->driver); 3578 3579 ··· 3604 3603 device_remove_file(&pdev->dev, &dev_attr_registers); 3605 3604 3606 3605 ep_info(dev, "unbind\n"); 3606 + usb_put_gadget(&dev->gadget); 3607 3607 } 3608 3608 3609 3609 /* wrap this driver around the specified device, but ··· 3626 3624 } 3627 3625 3628 3626 pci_set_drvdata(pdev, dev); 3627 + usb_initialize_gadget(&pdev->dev, &dev->gadget, gadget_release); 3629 3628 spin_lock_init(&dev->lock); 3630 3629 dev->quirks = id->driver_data; 3631 3630 dev->pdev = pdev; ··· 3777 3774 if (retval) 3778 3775 goto done; 3779 3776 3780 - retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, 3781 - gadget_release); 3777 + retval = usb_add_gadget(&dev->gadget); 3782 3778 if (retval) 3783 3779 goto done; 3780 + dev->added = 1; 3784 3781 return 0; 3785 3782 3786 3783 done:
+1
drivers/usb/gadget/udc/net2280.h
··· 156 156 softconnect : 1, 157 157 got_irq : 1, 158 158 region:1, 159 + added:1, 159 160 u1_enable:1, 160 161 u2_enable:1, 161 162 ltm_enable:1,