aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorJohan Hovold <jhovold@gmail.com>2013-03-19 04:21:13 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-21 18:59:03 -0400
commit356050d8b1e526db093e9d2c78daf49d6bf418e3 (patch)
tree091d162599ad794ea532afed538b734f6a96695a /drivers/usb
parentfa1e11d5231c001c80a479160b5832933c5d35fb (diff)
USB: cypress_m8: fix use-after-free in TIOCMIWAIT
Use the port wait queue and make sure to check the serial disconnected flag before accessing private port data after waking up. This is is needed as the private port data (including the wait queue itself) can be gone when waking up after a disconnect. Also remove bogus test for private data pointer being NULL as it is never assigned in the loop. Cc: stable <stable@vger.kernel.org> Signed-off-by: Johan Hovold <jhovold@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/serial/cypress_m8.c14
1 files changed, 8 insertions, 6 deletions
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index 8efa19d0e9fb..ba7352e4187e 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -111,7 +111,6 @@ struct cypress_private {
111 int baud_rate; /* stores current baud rate in 111 int baud_rate; /* stores current baud rate in
112 integer form */ 112 integer form */
113 int isthrottled; /* if throttled, discard reads */ 113 int isthrottled; /* if throttled, discard reads */
114 wait_queue_head_t delta_msr_wait; /* used for TIOCMIWAIT */
115 char prev_status, diff_status; /* used for TIOCMIWAIT */ 114 char prev_status, diff_status; /* used for TIOCMIWAIT */
116 /* we pass a pointer to this as the argument sent to 115 /* we pass a pointer to this as the argument sent to
117 cypress_set_termios old_termios */ 116 cypress_set_termios old_termios */
@@ -449,7 +448,6 @@ static int cypress_generic_port_probe(struct usb_serial_port *port)
449 kfree(priv); 448 kfree(priv);
450 return -ENOMEM; 449 return -ENOMEM;
451 } 450 }
452 init_waitqueue_head(&priv->delta_msr_wait);
453 451
454 usb_reset_configuration(serial->dev); 452 usb_reset_configuration(serial->dev);
455 453
@@ -868,12 +866,16 @@ static int cypress_ioctl(struct tty_struct *tty,
868 switch (cmd) { 866 switch (cmd) {
869 /* This code comes from drivers/char/serial.c and ftdi_sio.c */ 867 /* This code comes from drivers/char/serial.c and ftdi_sio.c */
870 case TIOCMIWAIT: 868 case TIOCMIWAIT:
871 while (priv != NULL) { 869 for (;;) {
872 interruptible_sleep_on(&priv->delta_msr_wait); 870 interruptible_sleep_on(&port->delta_msr_wait);
873 /* see if a signal did it */ 871 /* see if a signal did it */
874 if (signal_pending(current)) 872 if (signal_pending(current))
875 return -ERESTARTSYS; 873 return -ERESTARTSYS;
876 else { 874
875 if (port->serial->disconnected)
876 return -EIO;
877
878 {
877 char diff = priv->diff_status; 879 char diff = priv->diff_status;
878 if (diff == 0) 880 if (diff == 0)
879 return -EIO; /* no change => error */ 881 return -EIO; /* no change => error */
@@ -1187,7 +1189,7 @@ static void cypress_read_int_callback(struct urb *urb)
1187 if (priv->current_status != priv->prev_status) { 1189 if (priv->current_status != priv->prev_status) {
1188 priv->diff_status |= priv->current_status ^ 1190 priv->diff_status |= priv->current_status ^
1189 priv->prev_status; 1191 priv->prev_status;
1190 wake_up_interruptible(&priv->delta_msr_wait); 1192 wake_up_interruptible(&port->delta_msr_wait);
1191 priv->prev_status = priv->current_status; 1193 priv->prev_status = priv->current_status;
1192 } 1194 }
1193 spin_unlock_irqrestore(&priv->lock, flags); 1195 spin_unlock_irqrestore(&priv->lock, flags);