diff options
author | Oliver Neukum <oneukum@suse.de> | 2007-10-27 23:36:46 +0200 |
---|---|---|
committer | Adrian Bunk <bunk@kernel.org> | 2007-11-01 03:57:52 +0100 |
commit | 402a199dab7513f1f38fe3a0e0e8dd72a2de83f5 (patch) | |
tree | eaa7be117ac0a29a3b8810abfe5bff2e0c0ba382 /drivers/usb | |
parent | 9c456953203c95a8a9f959aa8d650a9be32d3e48 (diff) |
USB: fix DoS in pwc USB video driver (CVE-2007-5093)
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.
Adrian Bunk:
Backported to 2.6.16.
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Adrian Bunk <bunk@kernel.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/media/pwc/pwc-if.c | 50 | ||||
-rw-r--r-- | drivers/usb/media/pwc/pwc.h | 1 |
2 files changed, 35 insertions, 16 deletions
diff --git a/drivers/usb/media/pwc/pwc-if.c b/drivers/usb/media/pwc/pwc-if.c index 4f9b0dc6fd7..8e55391fe2e 100644 --- a/drivers/usb/media/pwc/pwc-if.c +++ b/drivers/usb/media/pwc/pwc-if.c @@ -1100,12 +1100,18 @@ static int pwc_video_open(struct inode *inode, struct file *file) return 0; } + +static void pwc_cleanup(struct pwc_device *pdev) +{ + 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; Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev); @@ -1140,8 +1146,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) Info("Failed to set LED on/off time.\n"); @@ -1150,9 +1157,19 @@ static int pwc_video_close(struct inode *inode, struct file *file) if (i < 0) Err("Failed to power down camera (%d)\n", i); } + pdev->vopen = 0; + Trace(TRACE_OPEN, "<< video_close()\n"); + } 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 = 0; - Trace(TRACE_OPEN, "<< video_close()\n"); + unlock_kernel(); + return 0; } @@ -1989,20 +2006,21 @@ 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 */ - Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); - 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 */ + Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); + 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(); } diff --git a/drivers/usb/media/pwc/pwc.h b/drivers/usb/media/pwc/pwc.h index 6dd76bb3dff..bb888e80514 100644 --- a/drivers/usb/media/pwc/pwc.h +++ b/drivers/usb/media/pwc/pwc.h @@ -149,6 +149,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]; |