Commit 2400a1f8 authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Mauro Carvalho Chehab
Browse files

[media] soc-camera: protect against racing open(2) and rmmod



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: default avatarGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 53faa685
Loading
Loading
Loading
Loading
+28 −14
Original line number Diff line number Diff line
@@ -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;
}