diff options
author | Oliver Neukum <oneukum@suse.de> | 2007-08-21 01:10:42 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-08-22 17:27:58 -0400 |
commit | 85237f202d46d55c1bffe0c5b1aa3ddc0f1dce4d (patch) | |
tree | 261871504ea83df81488ac36e567e36de08c9b7a /drivers/media/video/pwc/pwc-if.c | |
parent | c39772d82ad453647ea4bf9d793010d86ef5e597 (diff) |
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>
Diffstat (limited to 'drivers/media/video/pwc/pwc-if.c')
-rw-r--r-- | drivers/media/video/pwc/pwc-if.c | 52 |
1 files changed, 35 insertions, 17 deletions
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 9c0e8d18c2f6..3d81966d8c42 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c | |||
@@ -1196,12 +1196,19 @@ static int pwc_video_open(struct inode *inode, struct file *file) | |||
1196 | return 0; | 1196 | return 0; |
1197 | } | 1197 | } |
1198 | 1198 | ||
1199 | |||
1200 | static void pwc_cleanup(struct pwc_device *pdev) | ||
1201 | { | ||
1202 | pwc_remove_sysfs_files(pdev->vdev); | ||
1203 | video_unregister_device(pdev->vdev); | ||
1204 | } | ||
1205 | |||
1199 | /* Note that all cleanup is done in the reverse order as in _open */ | 1206 | /* Note that all cleanup is done in the reverse order as in _open */ |
1200 | static int pwc_video_close(struct inode *inode, struct file *file) | 1207 | static int pwc_video_close(struct inode *inode, struct file *file) |
1201 | { | 1208 | { |
1202 | struct video_device *vdev = file->private_data; | 1209 | struct video_device *vdev = file->private_data; |
1203 | struct pwc_device *pdev; | 1210 | struct pwc_device *pdev; |
1204 | int i; | 1211 | int i, hint; |
1205 | 1212 | ||
1206 | PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); | 1213 | PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); |
1207 | 1214 | ||
@@ -1224,8 +1231,9 @@ static int pwc_video_close(struct inode *inode, struct file *file) | |||
1224 | pwc_isoc_cleanup(pdev); | 1231 | pwc_isoc_cleanup(pdev); |
1225 | pwc_free_buffers(pdev); | 1232 | pwc_free_buffers(pdev); |
1226 | 1233 | ||
1234 | lock_kernel(); | ||
1227 | /* Turn off LEDS and power down camera, but only when not unplugged */ | 1235 | /* Turn off LEDS and power down camera, but only when not unplugged */ |
1228 | if (pdev->error_status != EPIPE) { | 1236 | if (!pdev->unplugged) { |
1229 | /* Turn LEDs off */ | 1237 | /* Turn LEDs off */ |
1230 | if (pwc_set_leds(pdev, 0, 0) < 0) | 1238 | if (pwc_set_leds(pdev, 0, 0) < 0) |
1231 | PWC_DEBUG_MODULE("Failed to set LED on/off time.\n"); | 1239 | 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 | if (i < 0) | 1242 | if (i < 0) |
1235 | PWC_ERROR("Failed to power down camera (%d)\n", i); | 1243 | PWC_ERROR("Failed to power down camera (%d)\n", i); |
1236 | } | 1244 | } |
1245 | pdev->vopen--; | ||
1246 | PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", i); | ||
1247 | } else { | ||
1248 | pwc_cleanup(pdev); | ||
1249 | /* Free memory (don't set pdev to 0 just yet) */ | ||
1250 | kfree(pdev); | ||
1251 | /* search device_hint[] table if we occupy a slot, by any chance */ | ||
1252 | for (hint = 0; hint < MAX_DEV_HINTS; hint++) | ||
1253 | if (device_hint[hint].pdev == pdev) | ||
1254 | device_hint[hint].pdev = NULL; | ||
1237 | } | 1255 | } |
1238 | pdev->vopen--; | 1256 | unlock_kernel(); |
1239 | PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen); | 1257 | |
1240 | return 0; | 1258 | return 0; |
1241 | } | 1259 | } |
1242 | 1260 | ||
@@ -1791,21 +1809,21 @@ static void usb_pwc_disconnect(struct usb_interface *intf) | |||
1791 | /* Alert waiting processes */ | 1809 | /* Alert waiting processes */ |
1792 | wake_up_interruptible(&pdev->frameq); | 1810 | wake_up_interruptible(&pdev->frameq); |
1793 | /* Wait until device is closed */ | 1811 | /* Wait until device is closed */ |
1794 | while (pdev->vopen) | 1812 | if(pdev->vopen) { |
1795 | schedule(); | 1813 | pdev->unplugged = 1; |
1796 | /* Device is now closed, so we can safely unregister it */ | 1814 | } else { |
1797 | PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n"); | 1815 | /* Device is closed, so we can safely unregister it */ |
1798 | pwc_remove_sysfs_files(pdev->vdev); | 1816 | PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n"); |
1799 | video_unregister_device(pdev->vdev); | 1817 | pwc_cleanup(pdev); |
1800 | 1818 | /* Free memory (don't set pdev to 0 just yet) */ | |
1801 | /* Free memory (don't set pdev to 0 just yet) */ | 1819 | kfree(pdev); |
1802 | kfree(pdev); | ||
1803 | 1820 | ||
1804 | disconnect_out: | 1821 | disconnect_out: |
1805 | /* search device_hint[] table if we occupy a slot, by any chance */ | 1822 | /* search device_hint[] table if we occupy a slot, by any chance */ |
1806 | for (hint = 0; hint < MAX_DEV_HINTS; hint++) | 1823 | for (hint = 0; hint < MAX_DEV_HINTS; hint++) |
1807 | if (device_hint[hint].pdev == pdev) | 1824 | if (device_hint[hint].pdev == pdev) |
1808 | device_hint[hint].pdev = NULL; | 1825 | device_hint[hint].pdev = NULL; |
1826 | } | ||
1809 | 1827 | ||
1810 | unlock_kernel(); | 1828 | unlock_kernel(); |
1811 | } | 1829 | } |