summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2015-07-12 21:05:26 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-08-10 12:21:56 -0700
commitdf86527517e8cd14f7ebec7302e70590543a49b5 (patch)
tree52a16cd74a549900829cd760d00b9050db01f4b0
parent4eede03b97bf6d89681905668e983f99b5847827 (diff)
serial: core: Fix crashes while echoing when closing
commit e144c58cad6667876173dd76977e9e6557e34941 upstream. While closing, new rx data may be received after the input buffers have been flushed but before stop_rx() halts receiving [1]. The new data might not be processed by flush_to_ldisc() until after uart_shutdown() and normal input processing is re-enabled (ie., tty->closing = 0). The race is outlined below: CPU 0 | CPU 1 | uart_close() | tty_port_close_start() | tty->closing = 1 | tty_ldisc_flush() | | => IRQ | while (LSR & data ready) | uart_insert_char() | tty_flip_buffer_push() | <= EOI stop_rx() | . uart_shutdown() | . free xmit.buf | . tty_port_tty_set(NULL) | . tty->closing = 0 | . | flush_to_ldisc() | n_tty_receive_buf_common() | __receive_buf() | ... | commit_echoes() | uart_flush_chars() | __uart_start() | ** OOPS on port.tty deref ** tty_ldisc_flush() | Input processing must be prevented from echoing (tty->closing = 1) until _after_ the input buffers have been flushed again at the end of uart_close(). [1] In fact, some input may actually be buffered _after_ stop_rx() since the rx interrupt may have already triggered but not yet been handled when stop_rx() disables rx interrupts. Fixes: 2e758910832d ("serial: core: Flush ldisc after dropping port mutex in uart_close()") Reported-by: Robert Elliott <elliott@hp.com> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/serial/serial_core.c3
1 files changed, 2 insertions, 1 deletions
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 0b7bb12dfc68..ec540445bb71 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1409,7 +1409,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
mutex_lock(&port->mutex);
uart_shutdown(tty, state);
tty_port_tty_set(port, NULL);
- tty->closing = 0;
+
spin_lock_irqsave(&port->lock, flags);
if (port->blocked_open) {
@@ -1435,6 +1435,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
mutex_unlock(&port->mutex);
tty_ldisc_flush(tty);
+ tty->closing = 0;
}
static void uart_wait_until_sent(struct tty_struct *tty, int timeout)