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: Oliver Neukum <oneukum@suse.de> CC: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
c39772d82a
commit
85237f202d
2 changed files with 36 additions and 17 deletions
|
@ -1196,12 +1196,19 @@ static int pwc_video_open(struct inode *inode, struct file *file)
|
|||
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 */
|
||||
static int pwc_video_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct video_device *vdev = file->private_data;
|
||||
struct pwc_device *pdev;
|
||||
int i;
|
||||
int i, hint;
|
||||
|
||||
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_free_buffers(pdev);
|
||||
|
||||
lock_kernel();
|
||||
/* Turn off LEDS and power down camera, but only when not unplugged */
|
||||
if (pdev->error_status != EPIPE) {
|
||||
if (!pdev->unplugged) {
|
||||
/* Turn LEDs off */
|
||||
if (pwc_set_leds(pdev, 0, 0) < 0)
|
||||
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)
|
||||
PWC_ERROR("Failed to power down camera (%d)\n", i);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1791,13 +1809,12 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
|
|||
/* Alert waiting processes */
|
||||
wake_up_interruptible(&pdev->frameq);
|
||||
/* Wait until device is closed */
|
||||
while (pdev->vopen)
|
||||
schedule();
|
||||
/* Device is now closed, so we can safely unregister it */
|
||||
if(pdev->vopen) {
|
||||
pdev->unplugged = 1;
|
||||
} else {
|
||||
/* Device is closed, so we can safely unregister it */
|
||||
PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n");
|
||||
pwc_remove_sysfs_files(pdev->vdev);
|
||||
video_unregister_device(pdev->vdev);
|
||||
|
||||
pwc_cleanup(pdev);
|
||||
/* Free memory (don't set pdev to 0 just yet) */
|
||||
kfree(pdev);
|
||||
|
||||
|
@ -1806,6 +1823,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
|
|||
for (hint = 0; hint < MAX_DEV_HINTS; hint++)
|
||||
if (device_hint[hint].pdev == pdev)
|
||||
device_hint[hint].pdev = NULL;
|
||||
}
|
||||
|
||||
unlock_kernel();
|
||||
}
|
||||
|
|
|
@ -193,6 +193,7 @@ struct pwc_device
|
|||
char vsnapshot; /* snapshot mode */
|
||||
char vsync; /* used by isoc handler */
|
||||
char vmirror; /* for ToUCaM series */
|
||||
char unplugged;
|
||||
|
||||
int cmd_len;
|
||||
unsigned char cmd_buf[13];
|
||||
|
|
Loading…
Reference in a new issue