]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
usb: gadget: f_hid: don't call cdev_init while cdev in use
authorMichael Zimmermann <sigmaepsilon92@gmail.com>
Fri, 27 Mar 2026 19:22:09 +0000 (20:22 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 22 Apr 2026 11:32:18 +0000 (13:32 +0200)
commit 81ebd43cc0d6d106ce7b6ccbf7b5e40ca7f5503d upstream.

When calling unbind, then bind again, cdev_init reinitialized the cdev,
even though there may still be references to it. That's the case when
the /dev/hidg* device is still opened. This obviously unsafe behavior
like oopes.

This fixes this by using cdev_alloc to put the cdev on the heap. That
way, we can simply allocate a new one in hidg_bind.

Closes: https://lore.kernel.org/linux-usb/CAN9vWDKZn0Ts5JyV2_xcAmbnBEi0znMLg_USMFrShRryXrgWGQ@mail.gmail.com/T/#m2cb0dba3633b67b2a679c98499508267d1508881
Cc: stable <stable@kernel.org>
Signed-off-by: Michael Zimmermann <sigmaepsilon92@gmail.com>
Link: https://patch.msgid.link/20260327192209.59945-1-sigmaepsilon92@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/function/f_hid.c

index e5ccaec7750cdc3e264f105d52280fe46f4295b2..e0c3f39ee95e7b0576b2a36f8cdea5f20e044856 100644 (file)
@@ -106,7 +106,7 @@ struct f_hidg {
        struct list_head                report_list;
 
        struct device                   dev;
-       struct cdev                     cdev;
+       struct cdev                     *cdev;
        struct usb_function             func;
 
        struct usb_ep                   *in_ep;
@@ -749,8 +749,9 @@ static int f_hidg_release(struct inode *inode, struct file *fd)
 
 static int f_hidg_open(struct inode *inode, struct file *fd)
 {
+       struct kobject *parent = inode->i_cdev->kobj.parent;
        struct f_hidg *hidg =
-               container_of(inode->i_cdev, struct f_hidg, cdev);
+               container_of(parent, struct f_hidg, dev.kobj);
 
        fd->private_data = hidg;
 
@@ -1276,8 +1277,12 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
        }
 
        /* create char device */
-       cdev_init(&hidg->cdev, &f_hidg_fops);
-       status = cdev_device_add(&hidg->cdev, &hidg->dev);
+       hidg->cdev = cdev_alloc();
+       if (!hidg->cdev)
+               goto fail_free_all;
+       hidg->cdev->ops = &f_hidg_fops;
+
+       status = cdev_device_add(hidg->cdev, &hidg->dev);
        if (status)
                goto fail_free_all;
 
@@ -1579,7 +1584,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
 {
        struct f_hidg *hidg = func_to_hidg(f);
 
-       cdev_device_del(&hidg->cdev, &hidg->dev);
+       cdev_device_del(hidg->cdev, &hidg->dev);
        destroy_workqueue(hidg->workqueue);
        usb_free_all_descriptors(f);
 }