serial_core: Fix race in uart_handle_dcd_change
authorAlan Cox <alan@linux.intel.com>
Wed, 19 Sep 2012 14:35:46 +0000 (15:35 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Sep 2012 16:51:09 +0000 (09:51 -0700)
If a serial driver is called post hangup with a second DCD event then we
will attempt to get the ldisc of NULL. Check we have a tty before trying to
do anything with it.

This is still only safe within the uart layer if the caller holds the
relevant uart locks. We could do with a version where the tty is passed for
more general use.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/serial_core.c

index 046279c..78036c5 100644 (file)
@@ -2501,9 +2501,12 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
 {
        struct uart_state *state = uport->state;
        struct tty_port *port = &state->port;
-       struct tty_ldisc *ld = tty_ldisc_ref(port->tty);
+       struct tty_ldisc *ld = NULL;
        struct pps_event_time ts;
+       struct tty_struct *tty = port->tty;
 
+       if (tty)
+               ld = tty_ldisc_ref(tty);
        if (ld && ld->ops->dcd_change)
                pps_get_ts(&ts);
 
@@ -2516,12 +2519,12 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
        if (port->flags & ASYNC_CHECK_CD) {
                if (status)
                        wake_up_interruptible(&port->open_wait);
-               else if (port->tty)
-                       tty_hangup(port->tty);
+               else if (tty)
+                       tty_hangup(tty);
        }
 
        if (ld && ld->ops->dcd_change)
-               ld->ops->dcd_change(port->tty, status, &ts);
+               ld->ops->dcd_change(tty, status, &ts);
        if (ld)
                tty_ldisc_deref(ld);
 }