]> err.no Git - linux-2.6/commitdiff
synchronization in usb_serial_put
authorBenny Halevy <bhalevy@panasas.com>
Tue, 15 May 2007 08:15:27 +0000 (11:15 +0300)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 12 Jul 2007 23:34:31 +0000 (16:34 -0700)
I think there is a race between usb_serial_put() and
usb_serial_get_by_index() (and get_free_serial()) with regards
to handling the serial port refcount.

usb_serial_get_by_index() gets a reference on the serial port under
table_lock while return_serial releases all the returned ports
from the table under the same lock.  However, the table_lock is not
taken around the call to kref_put, theoretically allowing to sneak
in and grab a reference after kref_put has already determined that
the reference count is zero (and before calling destroy_serial)
causing use after free.

Signed-off-by: Benny Halevy <bhalevy@ns1.bhalevy.com>
Cc: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/usb-serial.c

index e3e3728e16e320f300b818d062fb93b3ee28bf58..a3665659d13b32636914368429c0be0d6998e35d 100644 (file)
@@ -122,11 +122,9 @@ static void return_serial(struct usb_serial *serial)
        if (serial == NULL)
                return;
 
-       spin_lock(&table_lock);
        for (i = 0; i < serial->num_ports; ++i) {
                serial_table[serial->minor + i] = NULL;
        }
-       spin_unlock(&table_lock);
 }
 
 static void destroy_serial(struct kref *kref)
@@ -174,7 +172,9 @@ static void destroy_serial(struct kref *kref)
 
 void usb_serial_put(struct usb_serial *serial)
 {
+       spin_lock(&table_lock);
        kref_put(&serial->kref, destroy_serial);
+       spin_unlock(&table_lock);
 }
 
 /*****************************************************************************