Fix a memory leak in the usb_store_new_id() error paths. When bailing out
due to sanity checks, the function left the already allocated usb_dynid
struct in place. This regression was introduced by the following commits:

c63fe8f6 (usb: core: add sanity checks when using bInterfaceClass with new_id)
1b9fb31f (usb: core: check for valid id_table when using the RefId feature)
52a6966c (usb: core: bail out if user gives an unknown RefId when using new_id)

Detected by Coverity: CID 1162604.

Signed-off-by: Christian Engelmayer <cenge...@gmx.at>
---
v2: Added changes suggested by Sergei Shtylyov:

    * Fixed coding style violation regarding usage of braces in if-statements
      if only one branch of a conditional statement is a single statement.
    * Keep the information on commits introducing the issue in the changelog.

v3: Added changes suggested by Wolfram Sang:

    * Change the function to use a centralized exit and cleanup path.
---
 drivers/usb/core/driver.c | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 5d01558..ab90a01 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -63,8 +63,10 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
        dynid->id.idProduct = idProduct;
        dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE;
        if (fields > 2 && bInterfaceClass) {
-               if (bInterfaceClass > 255)
-                       return -EINVAL;
+               if (bInterfaceClass > 255) {
+                       retval = -EINVAL;
+                       goto fail;
+               }
 
                dynid->id.bInterfaceClass = (u8)bInterfaceClass;
                dynid->id.match_flags |= USB_DEVICE_ID_MATCH_INT_CLASS;
@@ -73,17 +75,21 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
        if (fields > 4) {
                const struct usb_device_id *id = id_table;
 
-               if (!id)
-                       return -ENODEV;
+               if (!id) {
+                       retval = -ENODEV;
+                       goto fail;
+               }
 
                for (; id->match_flags; id++)
                        if (id->idVendor == refVendor && id->idProduct == 
refProduct)
                                break;
 
-               if (id->match_flags)
+               if (id->match_flags) {
                        dynid->id.driver_info = id->driver_info;
-               else
-                       return -ENODEV;
+               } else {
+                       retval = -ENODEV;
+                       goto fail;
+               }
        }
 
        spin_lock(&dynids->lock);
@@ -95,6 +101,10 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
        if (retval)
                return retval;
        return count;
+
+fail:
+       kfree(dynid);
+       return retval;
 }
 EXPORT_SYMBOL_GPL(usb_store_new_id);
 
-- 
1.8.3.2
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to