diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-05-22 11:46:41 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-12 19:29:48 -0400 |
commit | d4ead16f50f9ad30bdc7276ec8fee7a24c72f294 (patch) | |
tree | e1905abbc393cc4d73180dd7b9e1cf860378b590 /drivers/usb/misc/idmouse.c | |
parent | 55e5fdfa541ec7bf1b1613624ed4dd8cdacaa841 (diff) |
USB: prevent char device open/deregister race
This patch (as908) adds central protection in usbcore for the
prototypical race between opening and unregistering a char device.
The spinlock used to protect the minor-numbers array is replaced with
an rwsem, which can remain locked across a call to a driver's open()
method. This guarantees that open() and deregister() will be mutually
exclusive.
The private locks currently used in several individual drivers for
this purpose are no longer necessary, and the patch removes them. The
following USB drivers are affected: usblcd, idmouse, auerswald,
legousbtower, sisusbvga/sisusb, ldusb, adutux, iowarrior, and
usb-skeleton.
As a side effect of this change, usb_deregister_dev() must not be
called while holding a lock that is acquired by open(). Unfortunately
a number of drivers do this, but luckily the solution is simple: call
usb_deregister_dev() before acquiring the lock.
In addition to these changes (and their consequent code
simplifications), the patch fixes a use-after-free bug in adutux and a
race between open() and release() in iowarrior.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/misc/idmouse.c')
-rw-r--r-- | drivers/usb/misc/idmouse.c | 54 |
1 files changed, 15 insertions, 39 deletions
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 8d0e360636e..e6fd024024f 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c | |||
@@ -119,9 +119,6 @@ static struct usb_driver idmouse_driver = { | |||
119 | .id_table = idmouse_table, | 119 | .id_table = idmouse_table, |
120 | }; | 120 | }; |
121 | 121 | ||
122 | /* prevent races between open() and disconnect() */ | ||
123 | static DEFINE_MUTEX(disconnect_mutex); | ||
124 | |||
125 | static int idmouse_create_image(struct usb_idmouse *dev) | 122 | static int idmouse_create_image(struct usb_idmouse *dev) |
126 | { | 123 | { |
127 | int bytes_read; | 124 | int bytes_read; |
@@ -211,21 +208,15 @@ static int idmouse_open(struct inode *inode, struct file *file) | |||
211 | struct usb_interface *interface; | 208 | struct usb_interface *interface; |
212 | int result; | 209 | int result; |
213 | 210 | ||
214 | /* prevent disconnects */ | ||
215 | mutex_lock(&disconnect_mutex); | ||
216 | |||
217 | /* get the interface from minor number and driver information */ | 211 | /* get the interface from minor number and driver information */ |
218 | interface = usb_find_interface (&idmouse_driver, iminor (inode)); | 212 | interface = usb_find_interface (&idmouse_driver, iminor (inode)); |
219 | if (!interface) { | 213 | if (!interface) |
220 | mutex_unlock(&disconnect_mutex); | ||
221 | return -ENODEV; | 214 | return -ENODEV; |
222 | } | 215 | |
223 | /* get the device information block from the interface */ | 216 | /* get the device information block from the interface */ |
224 | dev = usb_get_intfdata(interface); | 217 | dev = usb_get_intfdata(interface); |
225 | if (!dev) { | 218 | if (!dev) |
226 | mutex_unlock(&disconnect_mutex); | ||
227 | return -ENODEV; | 219 | return -ENODEV; |
228 | } | ||
229 | 220 | ||
230 | /* lock this device */ | 221 | /* lock this device */ |
231 | down(&dev->sem); | 222 | down(&dev->sem); |
@@ -255,9 +246,6 @@ error: | |||
255 | 246 | ||
256 | /* unlock this device */ | 247 | /* unlock this device */ |
257 | up(&dev->sem); | 248 | up(&dev->sem); |
258 | |||
259 | /* unlock the disconnect semaphore */ | ||
260 | mutex_unlock(&disconnect_mutex); | ||
261 | return result; | 249 | return result; |
262 | } | 250 | } |
263 | 251 | ||
@@ -265,15 +253,10 @@ static int idmouse_release(struct inode *inode, struct file *file) | |||
265 | { | 253 | { |
266 | struct usb_idmouse *dev; | 254 | struct usb_idmouse *dev; |
267 | 255 | ||
268 | /* prevent a race condition with open() */ | ||
269 | mutex_lock(&disconnect_mutex); | ||
270 | |||
271 | dev = file->private_data; | 256 | dev = file->private_data; |
272 | 257 | ||
273 | if (dev == NULL) { | 258 | if (dev == NULL) |
274 | mutex_unlock(&disconnect_mutex); | ||
275 | return -ENODEV; | 259 | return -ENODEV; |
276 | } | ||
277 | 260 | ||
278 | /* lock our device */ | 261 | /* lock our device */ |
279 | down(&dev->sem); | 262 | down(&dev->sem); |
@@ -281,7 +264,6 @@ static int idmouse_release(struct inode *inode, struct file *file) | |||
281 | /* are we really open? */ | 264 | /* are we really open? */ |
282 | if (dev->open <= 0) { | 265 | if (dev->open <= 0) { |
283 | up(&dev->sem); | 266 | up(&dev->sem); |
284 | mutex_unlock(&disconnect_mutex); | ||
285 | return -ENODEV; | 267 | return -ENODEV; |
286 | } | 268 | } |
287 | 269 | ||
@@ -291,12 +273,9 @@ static int idmouse_release(struct inode *inode, struct file *file) | |||
291 | /* the device was unplugged before the file was released */ | 273 | /* the device was unplugged before the file was released */ |
292 | up(&dev->sem); | 274 | up(&dev->sem); |
293 | idmouse_delete(dev); | 275 | idmouse_delete(dev); |
294 | mutex_unlock(&disconnect_mutex); | 276 | } else { |
295 | return 0; | 277 | up(&dev->sem); |
296 | } | 278 | } |
297 | |||
298 | up(&dev->sem); | ||
299 | mutex_unlock(&disconnect_mutex); | ||
300 | return 0; | 279 | return 0; |
301 | } | 280 | } |
302 | 281 | ||
@@ -391,30 +370,27 @@ static void idmouse_disconnect(struct usb_interface *interface) | |||
391 | { | 370 | { |
392 | struct usb_idmouse *dev; | 371 | struct usb_idmouse *dev; |
393 | 372 | ||
394 | /* prevent races with open() */ | ||
395 | mutex_lock(&disconnect_mutex); | ||
396 | |||
397 | /* get device structure */ | 373 | /* get device structure */ |
398 | dev = usb_get_intfdata(interface); | 374 | dev = usb_get_intfdata(interface); |
399 | usb_set_intfdata(interface, NULL); | 375 | usb_set_intfdata(interface, NULL); |
400 | 376 | ||
401 | /* lock it */ | ||
402 | down(&dev->sem); | ||
403 | |||
404 | /* give back our minor */ | 377 | /* give back our minor */ |
405 | usb_deregister_dev(interface, &idmouse_class); | 378 | usb_deregister_dev(interface, &idmouse_class); |
406 | 379 | ||
380 | /* lock it */ | ||
381 | down(&dev->sem); | ||
382 | |||
407 | /* prevent device read, write and ioctl */ | 383 | /* prevent device read, write and ioctl */ |
408 | dev->present = 0; | 384 | dev->present = 0; |
409 | 385 | ||
410 | /* unlock */ | ||
411 | up(&dev->sem); | ||
412 | |||
413 | /* if the device is opened, idmouse_release will clean this up */ | 386 | /* if the device is opened, idmouse_release will clean this up */ |
414 | if (!dev->open) | 387 | if (!dev->open) { |
388 | up(&dev->sem); | ||
415 | idmouse_delete(dev); | 389 | idmouse_delete(dev); |
416 | 390 | } else { | |
417 | mutex_unlock(&disconnect_mutex); | 391 | /* unlock */ |
392 | up(&dev->sem); | ||
393 | } | ||
418 | 394 | ||
419 | info("%s disconnected", DRIVER_DESC); | 395 | info("%s disconnected", DRIVER_DESC); |
420 | } | 396 | } |