summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorOliver Neukum <oliver@neukum.org>2008-01-22 15:54:54 +0100
committerGreg Kroah-Hartman <gregkh@suse.de>2008-02-01 14:35:04 -0800
commit9e3b1d8e3d5d135ac7be43f6710b7a67b569c292 (patch)
treed49c28ab6eb379e9cdc7cd9d28110c0967790486 /drivers/usb
parent0b3f5fe673d4626ea91816ca9c486b43b39a2fd1 (diff)
downloadlinux-9e3b1d8e3d5d135ac7be43f6710b7a67b569c292.tar.gz
linux-9e3b1d8e3d5d135ac7be43f6710b7a67b569c292.tar.bz2
linux-9e3b1d8e3d5d135ac7be43f6710b7a67b569c292.zip
USB: stop abuse of intfdata in cypress_m8
this driver uses usb_get_intfdata() == NULL as a test for disconnect(). You must not do that as this races with probe(). By the time you test your erstwhile interface may already be somebody else's interface. This fixes the close() method of cypress_m8 to use the recently introduced flag and use locking against disconnect() where required in close(). Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/serial/cypress_m8.c25
1 files changed, 16 insertions, 9 deletions
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index 163386336a5d..08c65c1a3771 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -682,7 +682,6 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp)
{
struct cypress_private *priv = usb_get_serial_port_data(port);
unsigned int c_cflag;
- unsigned long flags;
int bps;
long timeout;
wait_queue_t wait;
@@ -690,7 +689,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
/* wait for data to drain from buffer */
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock);
timeout = CYPRESS_CLOSING_WAIT;
init_waitqueue_entry(&wait, current);
add_wait_queue(&port->tty->write_wait, &wait);
@@ -698,18 +697,25 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp)
set_current_state(TASK_INTERRUPTIBLE);
if (cypress_buf_data_avail(priv->buf) == 0
|| timeout == 0 || signal_pending(current)
- || !usb_get_intfdata(port->serial->interface))
+ /* without mutex, allowed due to harmless failure mode */
+ || port->serial->disconnected)
break;
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irq(&priv->lock);
timeout = schedule_timeout(timeout);
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->tty->write_wait, &wait);
/* clear out any remaining data in the buffer */
cypress_buf_clear(priv->buf);
- spin_unlock_irqrestore(&priv->lock, flags);
-
+ spin_unlock_irq(&priv->lock);
+
+ /* writing is potentially harmful, lock must be taken */
+ mutex_lock(&port->serial->disc_mutex);
+ if (port->serial->disconnected) {
+ mutex_unlock(&port->serial->disc_mutex);
+ return;
+ }
/* wait for characters to drain from device */
bps = tty_get_baud_rate(port->tty);
if (bps > 1200)
@@ -727,10 +733,10 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp)
if (c_cflag & HUPCL) {
/* drop dtr and rts */
priv = usb_get_serial_port_data(port);
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock);
priv->line_control = 0;
priv->cmd_ctrl = 1;
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irq(&priv->lock);
cypress_write(port, NULL, 0);
}
}
@@ -738,6 +744,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp)
if (stats)
dev_info (&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n",
priv->bytes_in, priv->bytes_out, priv->cmd_count);
+ mutex_unlock(&port->serial->disc_mutex);
} /* cypress_close */