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

Commit 85237f20 authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman
Browse files

USB: fix DoS in pwc USB video driver



the pwc driver has a disconnect method that waits for user space to
close the device. This opens up an opportunity for a DoS attack,
blocking the USB subsystem and making khubd's task busy wait in
kernel space. This patch shifts freeing resources to close if an opened
device is disconnected.

Signed-off-by: default avatarOliver Neukum <oneukum@suse.de>
CC: stable <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent c39772d8
Loading
Loading
Loading
Loading
+35 −17
Original line number Original line Diff line number Diff line
@@ -1196,12 +1196,19 @@ static int pwc_video_open(struct inode *inode, struct file *file)
	return 0;
	return 0;
}
}



static void pwc_cleanup(struct pwc_device *pdev)
{
	pwc_remove_sysfs_files(pdev->vdev);
	video_unregister_device(pdev->vdev);
}

/* 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 */
static int pwc_video_close(struct inode *inode, struct file *file)
static int pwc_video_close(struct inode *inode, struct file *file)
{
{
	struct video_device *vdev = file->private_data;
	struct video_device *vdev = file->private_data;
	struct pwc_device *pdev;
	struct pwc_device *pdev;
	int i;
	int i, hint;


	PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);
	PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);


@@ -1224,8 +1231,9 @@ static int pwc_video_close(struct inode *inode, struct file *file)
	pwc_isoc_cleanup(pdev);
	pwc_isoc_cleanup(pdev);
	pwc_free_buffers(pdev);
	pwc_free_buffers(pdev);


	lock_kernel();
	/* Turn off LEDS and power down camera, but only when not unplugged */
	/* Turn off LEDS and power down camera, but only when not unplugged */
	if (pdev->error_status != EPIPE) {
	if (!pdev->unplugged) {
		/* Turn LEDs off */
		/* Turn LEDs off */
		if (pwc_set_leds(pdev, 0, 0) < 0)
		if (pwc_set_leds(pdev, 0, 0) < 0)
			PWC_DEBUG_MODULE("Failed to set LED on/off time.\n");
			PWC_DEBUG_MODULE("Failed to set LED on/off time.\n");
@@ -1234,9 +1242,19 @@ static int pwc_video_close(struct inode *inode, struct file *file)
			if (i < 0)
			if (i < 0)
				PWC_ERROR("Failed to power down camera (%d)\n", i);
				PWC_ERROR("Failed to power down camera (%d)\n", i);
		}
		}
	}
		pdev->vopen--;
		pdev->vopen--;
	PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen);
		PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", i);
	} else {
		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 */
		for (hint = 0; hint < MAX_DEV_HINTS; hint++)
			if (device_hint[hint].pdev == pdev)
				device_hint[hint].pdev = NULL;
	}
	unlock_kernel();

	return 0;
	return 0;
}
}


@@ -1791,13 +1809,12 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
	/* Alert waiting processes */
	/* Alert waiting processes */
	wake_up_interruptible(&pdev->frameq);
	wake_up_interruptible(&pdev->frameq);
	/* Wait until device is closed */
	/* Wait until device is closed */
	while (pdev->vopen)
	if(pdev->vopen) {
		schedule();
		pdev->unplugged = 1;
	/* Device is now closed, so we can safely unregister it */
	} else {
		/* 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_remove_sysfs_files(pdev->vdev);
		pwc_cleanup(pdev);
	video_unregister_device(pdev->vdev);

		/* Free memory (don't set pdev to 0 just yet) */
		/* Free memory (don't set pdev to 0 just yet) */
		kfree(pdev);
		kfree(pdev);


@@ -1806,6 +1823,7 @@ disconnect_out:
		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)
				device_hint[hint].pdev = NULL;
				device_hint[hint].pdev = NULL;
	}


	unlock_kernel();
	unlock_kernel();
}
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -193,6 +193,7 @@ struct pwc_device
   char vsnapshot;		/* snapshot mode */
   char vsnapshot;		/* snapshot mode */
   char vsync;			/* used by isoc handler */
   char vsync;			/* used by isoc handler */
   char vmirror;		/* for ToUCaM series */
   char vmirror;		/* for ToUCaM series */
	char unplugged;


   int cmd_len;
   int cmd_len;
   unsigned char cmd_buf[13];
   unsigned char cmd_buf[13];