Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89dec01b authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB (12489): pwc - fix few use-after-free and memory leaks



I just happen to peek inside the PWC driver and did not like what I saw
there. Please consider applying the patch below.

Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 35c3057b
Loading
Loading
Loading
Loading
+35 −43
Original line number Original line Diff line number Diff line
@@ -1057,7 +1057,8 @@ static int pwc_create_sysfs_files(struct video_device *vdev)
		goto err;
		goto err;
	if (pdev->features & FEATURE_MOTOR_PANTILT) {
	if (pdev->features & FEATURE_MOTOR_PANTILT) {
		rc = device_create_file(&vdev->dev, &dev_attr_pan_tilt);
		rc = device_create_file(&vdev->dev, &dev_attr_pan_tilt);
		if (rc) goto err_button;
		if (rc)
			goto err_button;
	}
	}


	return 0;
	return 0;
@@ -1072,6 +1073,7 @@ static int pwc_create_sysfs_files(struct video_device *vdev)
static void pwc_remove_sysfs_files(struct video_device *vdev)
static void pwc_remove_sysfs_files(struct video_device *vdev)
{
{
	struct pwc_device *pdev = video_get_drvdata(vdev);
	struct pwc_device *pdev = video_get_drvdata(vdev);

	if (pdev->features & FEATURE_MOTOR_PANTILT)
	if (pdev->features & FEATURE_MOTOR_PANTILT)
		device_remove_file(&vdev->dev, &dev_attr_pan_tilt);
		device_remove_file(&vdev->dev, &dev_attr_pan_tilt);
	device_remove_file(&vdev->dev, &dev_attr_button);
	device_remove_file(&vdev->dev, &dev_attr_button);
@@ -1229,13 +1231,11 @@ static void pwc_cleanup(struct pwc_device *pdev)
	video_unregister_device(pdev->vdev);
	video_unregister_device(pdev->vdev);


#ifdef CONFIG_USB_PWC_INPUT_EVDEV
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
	if (pdev->button_dev) {
	if (pdev->button_dev)
		input_unregister_device(pdev->button_dev);
		input_unregister_device(pdev->button_dev);
		input_free_device(pdev->button_dev);
		kfree(pdev->button_dev->phys);
		pdev->button_dev = NULL;
	}
#endif
#endif

	kfree(pdev);
}
}


/* Note that all cleanup is done in the reverse order as in _open */
/* Note that all cleanup is done in the reverse order as in _open */
@@ -1281,8 +1281,6 @@ static int pwc_video_close(struct file *file)
		PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen);
		PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen);
	} else {
	} else {
		pwc_cleanup(pdev);
		pwc_cleanup(pdev);
		/* Free memory (don't set pdev to 0 just yet) */
		kfree(pdev);
		/* search device_hint[] table if we occupy a slot, by any chance */
		/* search device_hint[] table if we occupy a slot, by any chance */
		for (hint = 0; hint < MAX_DEV_HINTS; hint++)
		for (hint = 0; hint < MAX_DEV_HINTS; hint++)
			if (device_hint[hint].pdev == pdev)
			if (device_hint[hint].pdev == pdev)
@@ -1499,13 +1497,10 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
	struct usb_device *udev = interface_to_usbdev(intf);
	struct usb_device *udev = interface_to_usbdev(intf);
	struct pwc_device *pdev = NULL;
	struct pwc_device *pdev = NULL;
	int vendor_id, product_id, type_id;
	int vendor_id, product_id, type_id;
	int i, hint, rc;
	int hint, rc;
	int features = 0;
	int features = 0;
	int video_nr = -1; /* default: use next available device */
	int video_nr = -1; /* default: use next available device */
	char serial_number[30], *name;
	char serial_number[30], *name;
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
	char *phys = NULL;
#endif


	vendor_id = le16_to_cpu(udev->descriptor.idVendor);
	vendor_id = le16_to_cpu(udev->descriptor.idVendor);
	product_id = le16_to_cpu(udev->descriptor.idProduct);
	product_id = le16_to_cpu(udev->descriptor.idProduct);
@@ -1757,8 +1752,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
	pdev->vframes = default_fps;
	pdev->vframes = default_fps;
	strcpy(pdev->serial, serial_number);
	strcpy(pdev->serial, serial_number);
	pdev->features = features;
	pdev->features = features;
	if (vendor_id == 0x046D && product_id == 0x08B5)
	if (vendor_id == 0x046D && product_id == 0x08B5) {
	{
		/* Logitech QuickCam Orbit
		/* Logitech QuickCam Orbit
		   The ranges have been determined experimentally; they may differ from cam to cam.
		   The ranges have been determined experimentally; they may differ from cam to cam.
		   Also, the exact ranges left-right and up-down are different for my cam
		   Also, the exact ranges left-right and up-down are different for my cam
@@ -1780,8 +1774,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
	pdev->vdev = video_device_alloc();
	pdev->vdev = video_device_alloc();
	if (!pdev->vdev) {
	if (!pdev->vdev) {
		PWC_ERROR("Err, cannot allocate video_device struture. Failing probe.");
		PWC_ERROR("Err, cannot allocate video_device struture. Failing probe.");
		kfree(pdev);
		rc = -ENOMEM;
		return -ENOMEM;
		goto err_free_mem;
	}
	}
	memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
	memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
	pdev->vdev->parent = &intf->dev;
	pdev->vdev->parent = &intf->dev;
@@ -1806,15 +1800,13 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
	}
	}


	pdev->vdev->release = video_device_release;
	pdev->vdev->release = video_device_release;
	i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
	rc = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
	if (i < 0) {
	if (rc < 0) {
		PWC_ERROR("Failed to register as video device (%d).\n", i);
		PWC_ERROR("Failed to register as video device (%d).\n", rc);
		rc = i;
		goto err_video_release;
		goto err;
	}
	}
	else {

	PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->num);
	PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->num);
	}


	/* occupy slot */
	/* occupy slot */
	if (hint < MAX_DEV_HINTS)
	if (hint < MAX_DEV_HINTS)
@@ -1824,7 +1816,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
	usb_set_intfdata(intf, pdev);
	usb_set_intfdata(intf, pdev);
	rc = pwc_create_sysfs_files(pdev->vdev);
	rc = pwc_create_sysfs_files(pdev->vdev);
	if (rc)
	if (rc)
		goto err_unreg;
		goto err_video_unreg;


	/* Set the leds off */
	/* Set the leds off */
	pwc_set_leds(pdev, 0, 0);
	pwc_set_leds(pdev, 0, 0);
@@ -1835,16 +1827,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
	pdev->button_dev = input_allocate_device();
	pdev->button_dev = input_allocate_device();
	if (!pdev->button_dev) {
	if (!pdev->button_dev) {
		PWC_ERROR("Err, insufficient memory for webcam snapshot button device.");
		PWC_ERROR("Err, insufficient memory for webcam snapshot button device.");
		return -ENOMEM;
		rc = -ENOMEM;
		pwc_remove_sysfs_files(pdev->vdev);
		goto err_video_unreg;
	}
	}


	usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys));
	strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys));

	pdev->button_dev->name = "PWC snapshot button";
	pdev->button_dev->name = "PWC snapshot button";
	phys = kasprintf(GFP_KERNEL,"usb-%s-%s", pdev->udev->bus->bus_name, pdev->udev->devpath);
	pdev->button_dev->phys = pdev->button_phys;
	if (!phys) {
		input_free_device(pdev->button_dev);
		return -ENOMEM;
	}
	pdev->button_dev->phys = phys;
	usb_to_input_id(pdev->udev, &pdev->button_dev->id);
	usb_to_input_id(pdev->udev, &pdev->button_dev->id);
	pdev->button_dev->dev.parent = &pdev->udev->dev;
	pdev->button_dev->dev.parent = &pdev->udev->dev;
	pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY);
	pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY);
@@ -1853,25 +1845,27 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
	rc = input_register_device(pdev->button_dev);
	rc = input_register_device(pdev->button_dev);
	if (rc) {
	if (rc) {
		input_free_device(pdev->button_dev);
		input_free_device(pdev->button_dev);
		kfree(pdev->button_dev->phys);
		pdev->button_dev = NULL;
		pdev->button_dev = NULL;
		return rc;
		pwc_remove_sysfs_files(pdev->vdev);
		goto err_video_unreg;
	}
	}
#endif
#endif


	return 0;
	return 0;


err_unreg:
err_video_unreg:
	if (hint < MAX_DEV_HINTS)
	if (hint < MAX_DEV_HINTS)
		device_hint[hint].pdev = NULL;
		device_hint[hint].pdev = NULL;
	video_unregister_device(pdev->vdev);
	video_unregister_device(pdev->vdev);
err:
	pdev->vdev = NULL;	/* So we don't try to release it below */
	video_device_release(pdev->vdev); /* Drip... drip... drip... */
err_video_release:
	kfree(pdev); /* Oops, no memory leaks please */
	video_device_release(pdev->vdev);
err_free_mem:
	kfree(pdev);
	return rc;
	return rc;
}
}


/* The user janked out the cable... */
/* The user yanked out the cable... */
static void usb_pwc_disconnect(struct usb_interface *intf)
static void usb_pwc_disconnect(struct usb_interface *intf)
{
{
	struct pwc_device *pdev;
	struct pwc_device *pdev;
@@ -1911,8 +1905,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
		/* Device is closed, so we can safely unregister it */
		/* Device is closed, so we can safely unregister it */
		PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n");
		PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n");
		pwc_cleanup(pdev);
		pwc_cleanup(pdev);
		/* Free memory (don't set pdev to 0 just yet) */
		kfree(pdev);


disconnect_out:
disconnect_out:
		/* search device_hint[] table if we occupy a slot, by any chance */
		/* search device_hint[] table if we occupy a slot, by any chance */
+1 −0
Original line number Original line Diff line number Diff line
@@ -253,6 +253,7 @@ struct pwc_device
   int snapshot_button_status;		/* set to 1 when the user push the button, reset to 0 when this value is read */
   int snapshot_button_status;		/* set to 1 when the user push the button, reset to 0 when this value is read */
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
   struct input_dev *button_dev;	/* webcam snapshot button input */
   struct input_dev *button_dev;	/* webcam snapshot button input */
   char button_phys[64];
#endif
#endif


   /*** Misc. data ***/
   /*** Misc. data ***/