aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/misc/idmouse.c
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-05-22 11:46:41 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-12 19:29:48 -0400
commitd4ead16f50f9ad30bdc7276ec8fee7a24c72f294 (patch)
treee1905abbc393cc4d73180dd7b9e1cf860378b590 /drivers/usb/misc/idmouse.c
parent55e5fdfa541ec7bf1b1613624ed4dd8cdacaa841 (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.c54
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() */
123static DEFINE_MUTEX(disconnect_mutex);
124
125static int idmouse_create_image(struct usb_idmouse *dev) 122static 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}