[2.6.20.21 review 14/35] USB: fix DoS in pwc USB video driver

Willy Tarreau
2007-10-13T14:45:06Z

[-- Attachment #1: 0047-USB-fix-DoS-in-pwc-USB-video-driver.patch --]
[-- Type: text/plain, Size: 3929 bytes --]

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
drivers/media/video/pwc/pwc-if.c | 52 +++++++++++++++++++++++++------------
drivers/media/video/pwc/pwc.h | 1 +
2 files changed, 36 insertions(+), 17 deletions(-)

Index: 2.6/drivers/media/video/pwc/pwc-if.c
===================================================================
--- 2.6.orig/drivers/media/video/pwc/pwc-if.c
+++ 2.6/drivers/media/video/pwc/pwc-if.c
@@ -1196,12 +1196,19 @@ static int pwc_video_open(struct inode *
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).
", vdev);

@@ -1224,8 +1231,9 @@ static int pwc_video_close(struct inode
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.
");
@@ -1234,9 +1242,19 @@ static int pwc_video_close(struct inode
if (i < 0)
PWC_ERROR("Failed to power down camera (%d)
", i);
}
+ pdev->vopen--;
+ PWC_DEBUG_OPEN("<< video_close() vopen=%d
", 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--;
- PWC_DEBUG_OPEN("<< video_close() vopen=%d
", pdev->vopen);
+ unlock_kernel();
+
return 0;
}

@@ -1783,21 +1801,21 @@ static void usb_pwc_disconnect(struct us
/* 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 */
- PWC_DEBUG_PROBE("Unregistering video device in disconnect().
");
- pwc_remove_sysfs_files(pdev->vdev);
- video_unregister_device(pdev->vdev);
-
- /* Free memory (don't set pdev to 0 just yet) */
- kfree(pdev);
+ if(pdev->vopen) {
+ pdev->unplugged = 1;
+ } else {
+ /* Device is closed, so we can safely unregister it */
+ PWC_DEBUG_PROBE("Unregistering video device in disconnect().
");
+ pwc_cleanup(pdev);
+ /* Free memory (don't set pdev to 0 just yet) */
+ kfree(pdev);

disconnect_out:
- /* 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;
+ /* 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();
}
Index: 2.6/drivers/media/video/pwc/pwc.h
===================================================================
--- 2.6.orig/drivers/media/video/pwc/pwc.h
+++ 2.6/drivers/media/video/pwc/pwc.h
@@ -198,6 +198,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];

--