From oneukum@suse.de Fri Mar 23 06:30:18 2007 From: Oliver Neukum To: Greg KH Subject: USB: fix race in ftdio_write Date: Fri, 23 Mar 2007 14:30:16 +0100 Message-Id: <200703231430.17011.oneukum@suse.de> 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 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) --- 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 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 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 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 /* 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 */