From af224ca2df297440448b9d407d21b50982c6d532 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 9 Apr 2016 18:56:35 -0700 Subject: [PATCH] serial: core: Prevent unsafe uart port access, part 3 For tty operations which may expect uart port to have been removed but still have other necessary work to accomplish, check for NULL uart port; specifically uart_close(), uart_hangup() and sub-functions (uart_shutdown() and uart_port_shutdown()). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 1887f9c71f85..d529b5ce66c7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -257,6 +257,8 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. Calls to * uart_shutdown are serialised by the per-port semaphore. + * + * uport == NULL if uart_port has already been removed */ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) { @@ -275,7 +277,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) /* * Turn off DTR and RTS early. */ - if (uart_console(uport) && tty) + if (uport && uart_console(uport) && tty) uport->cons->cflag = tty->termios.c_cflag; if (!tty || C_HUPCL(tty)) @@ -1460,7 +1462,6 @@ out: * Calls to uart_close() are serialised via the tty_lock in * drivers/tty/tty_io.c:tty_release() * drivers/tty/tty_io.c:do_tty_hangup() - * This runs from a workqueue and can sleep for a _short_ time only. */ static void uart_close(struct tty_struct *tty, struct file *filp) { @@ -1479,18 +1480,21 @@ static void uart_close(struct tty_struct *tty, struct file *filp) return; } - uport = state->uart_port; port = &state->port; pr_debug("uart_close(%d) called\n", tty->index); - if (!port->count || tty_port_close_start(port, tty, filp) == 0) + if (tty_port_close_start(port, tty, filp) == 0) return; + mutex_lock(&port->mutex); + uport = uart_port_check(state); + /* * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ - if (tty_port_initialized(port)) { + if (tty_port_initialized(port) && + !WARN(!uport, "detached port still initialized!\n")) { spin_lock_irq(&uport->lock); uport->ops->stop_rx(uport); spin_unlock_irq(&uport->lock); @@ -1502,7 +1506,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) uart_wait_until_sent(tty, uport->timeout); } - mutex_lock(&port->mutex); uart_shutdown(tty, state); tty_port_tty_set(port, NULL); @@ -1513,7 +1516,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) if (port->close_delay) msleep_interruptible(jiffies_to_msecs(port->close_delay)); spin_lock_irq(&port->lock); - } else if (!uart_console(uport)) { + } else if (uport && !uart_console(uport)) { spin_unlock_irq(&port->lock); uart_change_pm(state, UART_PM_STATE_OFF); spin_lock_irq(&port->lock); @@ -1600,11 +1603,15 @@ static void uart_hangup(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; + struct uart_port *uport; unsigned long flags; pr_debug("uart_hangup(%d)\n", tty->index); mutex_lock(&port->mutex); + uport = uart_port_check(state); + WARN(!uport, "hangup of detached port!\n"); + if (tty_port_active(port)) { uart_flush_buffer(tty); uart_shutdown(tty, state); @@ -1613,7 +1620,7 @@ static void uart_hangup(struct tty_struct *tty) spin_unlock_irqrestore(&port->lock, flags); tty_port_set_active(port, 0); tty_port_tty_set(port, NULL); - if (!uart_console(state->uart_port)) + if (uport && !uart_console(uport)) uart_change_pm(state, UART_PM_STATE_OFF); wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->delta_msr_wait); @@ -1621,6 +1628,7 @@ static void uart_hangup(struct tty_struct *tty) mutex_unlock(&port->mutex); } +/* uport == NULL if uart_port has already been removed */ static void uart_port_shutdown(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); @@ -1638,12 +1646,14 @@ static void uart_port_shutdown(struct tty_port *port) /* * Free the IRQ and disable the port. */ - uport->ops->shutdown(uport); + if (uport) + uport->ops->shutdown(uport); /* * Ensure that the IRQ handler isn't running on another CPU. */ - synchronize_irq(uport->irq); + if (uport) + synchronize_irq(uport->irq); } static int uart_carrier_raised(struct tty_port *port) -- 2.20.1