• Peter Hurley's avatar
    serial: core: Fix crashes while echoing when closing · e144c58c
    Peter Hurley authored
    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: 2e758910 ("serial: core: Flush ldisc after dropping port
    mutex in uart_close()")
    Reported-by: default avatarRobert Elliott <elliott@hp.com>
    Signed-off-by: default avatarPeter Hurley <peter@hurleysoftware.com>
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    e144c58c
serial_core.c 73.3 KB