USB: fix race in ftdio_write
authorOliver Neukum <oneukum@suse.de>
Fri, 23 Mar 2007 13:30:16 +0000 (14:30 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 27 Apr 2007 20:28:38 +0000 (13:28 -0700)
this has the same race as the visor driver. The counter must be incremented
under the lock it is checked under.

Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/ftdi_sio.c

index 8ff9d54b21e669ebca6a521964427873d118ba81..8fb8e2f645456da3178a9e9283cbd1dd8845db86 100644 (file)
@@ -1433,6 +1433,7 @@ static int ftdi_write (struct usb_serial_port *port,
                dbg("%s - write limit hit\n", __FUNCTION__);
                return 0;
        }
+       priv->tx_outstanding_urbs++;
        spin_unlock_irqrestore(&priv->tx_lock, flags);
 
        data_offset = priv->write_offset;
@@ -1450,14 +1451,15 @@ static int ftdi_write (struct usb_serial_port *port,
        buffer = kmalloc (transfer_size, GFP_ATOMIC);
        if (!buffer) {
                err("%s ran out of kernel memory for urb ...", __FUNCTION__);
-               return -ENOMEM;
+               count = -ENOMEM;
+               goto error_no_buffer;
        }
 
        urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!urb) {
                err("%s - no more free urbs", __FUNCTION__);
-               kfree (buffer);
-               return -ENOMEM;
+               count = -ENOMEM;
+               goto error_no_urb;
        }
 
        /* Copy data */
@@ -1499,10 +1501,9 @@ static int ftdi_write (struct usb_serial_port *port,
        if (status) {
                err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
                count = status;
-               kfree (buffer);
+               goto error;
        } else {
                spin_lock_irqsave(&priv->tx_lock, flags);
-               ++priv->tx_outstanding_urbs;
                priv->tx_outstanding_bytes += count;
                priv->tx_bytes += count;
                spin_unlock_irqrestore(&priv->tx_lock, flags);
@@ -1510,10 +1511,19 @@ static int ftdi_write (struct usb_serial_port *port,
 
        /* we are done with this urb, so let the host driver
         * really free it when it is finished with it */
-       usb_free_urb (urb);
+       usb_free_urb(urb);
 
        dbg("%s write returning: %d", __FUNCTION__, count);
        return count;
+error:
+       usb_free_urb(urb);
+error_no_urb:
+       kfree (buffer);
+error_no_buffer:
+       spin_lock_irqsave(&priv->tx_lock, flags);
+       priv->tx_outstanding_urbs--;
+       spin_unlock_irqrestore(&priv->tx_lock, flags);
+       return count;
 } /* ftdi_write */