diff options
author | Alan Cox <alan@linux.intel.com> | 2012-09-19 10:35:46 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-09-21 12:51:09 -0400 |
commit | 43eca0aef73cc6f0d37ad139f1cbb810e62e409d (patch) | |
tree | cbfe2836fa83eacbb4f08f31cb982fa20c8a8d80 /drivers/tty | |
parent | 84c3b84860440a9e3a3666c14112f41311b8f623 (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.c | 11 |
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 | } |