summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Neukum <oneukum@suse.de>2007-03-23 14:30:16 +0100
committerGreg Kroah-Hartman <gregkh@suse.de>2007-04-27 13:28:38 -0700
commit62127a585cc72509a0bfae502cc16f9935a4cda6 (patch)
tree96515ced9943c82c36103cf2e48d859cb4c237e6
parentb80349b17c6e1236a24616f71e59ed31279de25a (diff)
downloadlinux-62127a585cc72509a0bfae502cc16f9935a4cda6.tar.gz
linux-62127a585cc72509a0bfae502cc16f9935a4cda6.tar.bz2
linux-62127a585cc72509a0bfae502cc16f9935a4cda6.zip
USB: fix race in ftdio_write
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>
-rw-r--r--drivers/usb/serial/ftdi_sio.c22
1 files changed, 16 insertions, 6 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8ff9d54b21e6..8fb8e2f64545 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -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 */