]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/media/usb/uvc/uvc_driver.c
media: uvcvideo: Fix uvc_alloc_entity() allocation alignment
[mirror_ubuntu-bionic-kernel.git] / drivers / media / usb / uvc / uvc_driver.c
index 28b91b7d756f5f42968da0e6322a740242eb0d54..7044db636881ecf51a276a51d64b2761e754b125 100644 (file)
@@ -903,7 +903,7 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
        unsigned int size;
        unsigned int i;
 
-       extra_size = ALIGN(extra_size, sizeof(*entity->pads));
+       extra_size = roundup(extra_size, sizeof(*entity->pads));
        num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1;
        size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
             + num_inputs;
@@ -1054,11 +1054,19 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                        return -EINVAL;
                }
 
-               /* Make sure the terminal type MSB is not null, otherwise it
-                * could be confused with a unit.
+               /*
+                * Reject invalid terminal types that would cause issues:
+                *
+                * - The high byte must be non-zero, otherwise it would be
+                *   confused with a unit.
+                *
+                * - Bit 15 must be 0, as we use it internally as a terminal
+                *   direction flag.
+                *
+                * Other unknown types are accepted.
                 */
                type = get_unaligned_le16(&buffer[4]);
-               if ((type & 0xff00) == 0) {
+               if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) {
                        uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
                                "interface %d INPUT_TERMINAL %d has invalid "
                                "type 0x%04x, skipping\n", udev->devnum,
@@ -1813,11 +1821,7 @@ static void uvc_delete(struct kref *kref)
        usb_put_intf(dev->intf);
        usb_put_dev(dev->udev);
 
-       if (dev->vdev.dev)
-               v4l2_device_unregister(&dev->vdev);
 #ifdef CONFIG_MEDIA_CONTROLLER
-       if (media_devnode_is_registered(dev->mdev.devnode))
-               media_device_unregister(&dev->mdev);
        media_device_cleanup(&dev->mdev);
 #endif
 
@@ -1865,13 +1869,6 @@ static void uvc_unregister_video(struct uvc_device *dev)
 {
        struct uvc_streaming *stream;
 
-       /* Unregistering all video devices might result in uvc_delete() being
-        * called from inside the loop if there's no open file handle. To avoid
-        * that, increment the refcount before iterating over the streams and
-        * decrement it when done.
-        */
-       kref_get(&dev->ref);
-
        list_for_each_entry(stream, &dev->streams, list) {
                if (!video_is_registered(&stream->vdev))
                        continue;
@@ -1881,7 +1878,14 @@ static void uvc_unregister_video(struct uvc_device *dev)
                uvc_debugfs_cleanup_stream(stream);
        }
 
-       kref_put(&dev->ref, uvc_delete);
+       uvc_status_unregister(dev);
+
+       if (dev->vdev.dev)
+               v4l2_device_unregister(&dev->vdev);
+#ifdef CONFIG_MEDIA_CONTROLLER
+       if (media_devnode_is_registered(dev->mdev.devnode))
+               media_device_unregister(&dev->mdev);
+#endif
 }
 
 static int uvc_register_video(struct uvc_device *dev,
@@ -2129,6 +2133,7 @@ static int uvc_probe(struct usb_interface *intf,
 
 error:
        uvc_unregister_video(dev);
+       kref_put(&dev->ref, uvc_delete);
        return -ENODEV;
 }
 
@@ -2146,6 +2151,7 @@ static void uvc_disconnect(struct usb_interface *intf)
                return;
 
        uvc_unregister_video(dev);
+       kref_put(&dev->ref, uvc_delete);
 }
 
 static int uvc_suspend(struct usb_interface *intf, pm_message_t message)