aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorAlan Cox <alan@linux.intel.com>2012-09-19 10:35:46 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-09-21 12:51:09 -0400
commit43eca0aef73cc6f0d37ad139f1cbb810e62e409d (patch)
treecbfe2836fa83eacbb4f08f31cb982fa20c8a8d80 /drivers/tty
parent84c3b84860440a9e3a3666c14112f41311b8f623 (diff)
serial_core: Fix race in uart_handle_dcd_change
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>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/serial_core.c11
1 files changed, 7 insertions, 4 deletions
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 046279ce3e8d..78036c510ccc 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2501,9 +2501,12 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
2501{ 2501{
2502 struct uart_state *state = uport->state; 2502 struct uart_state *state = uport->state;
2503 struct tty_port *port = &state->port; 2503 struct tty_port *port = &state->port;
2504 struct tty_ldisc *ld = tty_ldisc_ref(port->tty); 2504 struct tty_ldisc *ld = NULL;
2505 struct pps_event_time ts; 2505 struct pps_event_time ts;
2506 struct tty_struct *tty = port->tty;
2506 2507
2508 if (tty)
2509 ld = tty_ldisc_ref(tty);
2507 if (ld && ld->ops->dcd_change) 2510 if (ld && ld->ops->dcd_change)
2508 pps_get_ts(&ts); 2511 pps_get_ts(&ts);
2509 2512
@@ -2516,12 +2519,12 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
2516 if (port->flags & ASYNC_CHECK_CD) { 2519 if (port->flags & ASYNC_CHECK_CD) {
2517 if (status) 2520 if (status)
2518 wake_up_interruptible(&port->open_wait); 2521 wake_up_interruptible(&port->open_wait);
2519 else if (port->tty) 2522 else if (tty)
2520 tty_hangup(port->tty); 2523 tty_hangup(tty);
2521 } 2524 }
2522 2525
2523 if (ld && ld->ops->dcd_change) 2526 if (ld && ld->ops->dcd_change)
2524 ld->ops->dcd_change(port->tty, status, &ts); 2527 ld->ops->dcd_change(tty, status, &ts);
2525 if (ld) 2528 if (ld)
2526 tty_ldisc_deref(ld); 2529 tty_ldisc_deref(ld);
2527} 2530}