Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af224ca2 authored by Peter Hurley's avatar Peter Hurley Committed by Greg Kroah-Hartman
Browse files

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: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9ed19428
Loading
Loading
Loading
Loading
+20 −10
Original line number Diff line number Diff line
@@ -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 @@ static void uart_set_termios(struct tty_struct *tty,
 * 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,11 +1646,13 @@ static void uart_port_shutdown(struct tty_port *port)
	/*
	 * Free the IRQ and disable the port.
	 */
	if (uport)
		uport->ops->shutdown(uport);

	/*
	 * Ensure that the IRQ handler isn't running on another CPU.
	 */
	if (uport)
		synchronize_irq(uport->irq);
}