OSDN Git Service

USB: ftdi_sio: fix use-after-free in TIOCMIWAIT
[android-x86/kernel.git] / drivers / usb / serial / ftdi_sio.c
index edd162d..d4809d5 100644 (file)
@@ -69,9 +69,7 @@ struct ftdi_private {
        int flags;              /* some ASYNC_xxxx flags are supported */
        unsigned long last_dtr_rts;     /* saved modem control outputs */
        struct async_icount     icount;
-       wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
        char prev_status;        /* Used for TIOCMIWAIT */
-       bool dev_gone;        /* Used to abort TIOCMIWAIT */
        char transmit_empty;    /* If transmitter is empty or not */
        __u16 interface;        /* FT2232C, FT2232H or FT4232H port interface
                                   (0 for FT232/245) */
@@ -1691,10 +1689,8 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
 
        kref_init(&priv->kref);
        mutex_init(&priv->cfg_lock);
-       init_waitqueue_head(&priv->delta_msr_wait);
 
        priv->flags = ASYNC_LOW_LATENCY;
-       priv->dev_gone = false;
 
        if (quirk && quirk->port_probe)
                quirk->port_probe(priv);
@@ -1840,8 +1836,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
 {
        struct ftdi_private *priv = usb_get_serial_port_data(port);
 
-       priv->dev_gone = true;
-       wake_up_interruptible_all(&priv->delta_msr_wait);
+       wake_up_interruptible(&port->delta_msr_wait);
 
        remove_sysfs_attrs(port);
 
@@ -1989,7 +1984,7 @@ static int ftdi_process_packet(struct usb_serial_port *port,
                if (diff_status & FTDI_RS0_RLSD)
                        priv->icount.dcd++;
 
-               wake_up_interruptible_all(&priv->delta_msr_wait);
+               wake_up_interruptible(&port->delta_msr_wait);
                priv->prev_status = status;
        }
 
@@ -2440,11 +2435,15 @@ static int ftdi_ioctl(struct tty_struct *tty,
         */
        case TIOCMIWAIT:
                cprev = priv->icount;
-               while (!priv->dev_gone) {
-                       interruptible_sleep_on(&priv->delta_msr_wait);
+               for (;;) {
+                       interruptible_sleep_on(&port->delta_msr_wait);
                        /* see if a signal did it */
                        if (signal_pending(current))
                                return -ERESTARTSYS;
+
+                       if (port->serial->disconnected)
+                               return -EIO;
+
                        cnow = priv->icount;
                        if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
                            ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
@@ -2454,8 +2453,6 @@ static int ftdi_ioctl(struct tty_struct *tty,
                        }
                        cprev = cnow;
                }
-               return -EIO;
-               break;
        case TIOCSERGETLSR:
                return get_lsr_info(port, (struct serial_struct __user *)arg);
                break;