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

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
...@@ -1196,12 +1196,19 @@ static int pwc_video_open(struct inode *inode, struct file *file) ...@@ -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) ...@@ -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) ...@@ -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--;
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;
} }
pdev->vopen--; unlock_kernel();
PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen);
return 0; return 0;
} }
...@@ -1791,21 +1809,21 @@ static void usb_pwc_disconnect(struct usb_interface *intf) ...@@ -1791,21 +1809,21 @@ 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 {
PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n"); /* Device is closed, so we can safely unregister it */
pwc_remove_sysfs_files(pdev->vdev); PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n");
video_unregister_device(pdev->vdev); pwc_cleanup(pdev);
/* Free memory (don't set pdev to 0 just yet) */
/* Free memory (don't set pdev to 0 just yet) */ kfree(pdev);
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 */
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();
} }
......
...@@ -193,6 +193,7 @@ struct pwc_device ...@@ -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];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment