[media] soc-camera: protect against racing open(2) and rmmod
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Wed, 9 Jan 2013 12:14:48 +0000 (09:14 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 4 Apr 2013 22:38:27 +0000 (19:38 -0300)
To protect against open() racing with rmmod, hold the list_lock also while
obtaining a reference to the camera host driver and check that the video
device hasn't been unregistered yet.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/platform/soc_camera/soc_camera.c

index bf55abfb7cbcd1f80acf26481963f9a00fb3f549..eea832c5fd010e18ca4daffb3c434a5907bcc52c 100644 (file)
@@ -508,36 +508,49 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd,
 static int soc_camera_open(struct file *file)
 {
        struct video_device *vdev = video_devdata(file);
-       struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
-       struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+       struct soc_camera_device *icd;
        struct soc_camera_host *ici;
        int ret;
 
-       if (!to_soc_camera_control(icd))
-               /* No device driver attached */
-               return -ENODEV;
-
        /*
         * Don't mess with the host during probe: wait until the loop in
-        * scan_add_host() completes
+        * scan_add_host() completes. Also protect against a race with
+        * soc_camera_host_unregister().
         */
        if (mutex_lock_interruptible(&list_lock))
                return -ERESTARTSYS;
+
+       if (!vdev || !video_is_registered(vdev)) {
+               mutex_unlock(&list_lock);
+               return -ENODEV;
+       }
+
+       icd = dev_get_drvdata(vdev->parent);
        ici = to_soc_camera_host(icd->parent);
+
+       ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV;
        mutex_unlock(&list_lock);
 
-       if (mutex_lock_interruptible(&ici->host_lock))
-               return -ERESTARTSYS;
-       if (!try_module_get(ici->ops->owner)) {
+       if (ret < 0) {
                dev_err(icd->pdev, "Couldn't lock capture bus driver.\n");
-               ret = -EINVAL;
-               goto emodule;
+               return ret;
+       }
+
+       if (!to_soc_camera_control(icd)) {
+               /* No device driver attached */
+               ret = -ENODEV;
+               goto econtrol;
        }
 
+       if (mutex_lock_interruptible(&ici->host_lock)) {
+               ret = -ERESTARTSYS;
+               goto elockhost;
+       }
        icd->use_count++;
 
        /* Now we really have to activate the camera */
        if (icd->use_count == 1) {
+               struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
                /* Restore parameters before the last close() per V4L2 API */
                struct v4l2_format f = {
                        .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
@@ -609,9 +622,10 @@ epower:
        ici->ops->remove(icd);
 eiciadd:
        icd->use_count--;
-       module_put(ici->ops->owner);
-emodule:
        mutex_unlock(&ici->host_lock);
+elockhost:
+econtrol:
+       module_put(ici->ops->owner);
 
        return ret;
 }