The state->xmit.buf can be freed, and set to NULL, by uart_shutdown while uart_put_char and uart_write is waiting to acquire the uart_port_lock causing a NULL pointer dereference. Only observed through uart_write, but similar issue could happen if uart_put_char was called. The issue was triggered on an older kernel, version 4.4, but is present in current code as well. Crash report: Unable to handle kernel NULL pointer dereference at virtual address 00000196 ... Workqueue: events_unbound flush_to_ldisc task: ffffffc1eb1cdb00 ti: ffffffc1eac6c000 task.ti: ffffffc1eac6c000 PC is at __memcpy+0xa0/0x180 LR is at uart_write+0x158/0x240 ... Call trace: [<ffffffc0004104a0>] __memcpy+0xa0/0x180 [<ffffffc00057f1c0>] do_output_char+0xa8/0x1d8 [<ffffffc00057f4bc>] __process_echoes+0x1cc/0x280 [<ffffffc00057f66c>] commit_echoes+0x6c/0xb0 [<ffffffc000581818>] n_tty_receive_char_special+0x7b8/0xa98 [<ffffffc000582388>] n_tty_receive_buf_common+0x520/0xa38 [<ffffffc0005828e0>] n_tty_receive_buf2+0x40/0x50 [<ffffffc000585ae0>] flush_to_ldisc+0xd0/0x168 [<ffffffc0000c1784>] process_one_work+0x2b4/0x4d8 [<ffffffc0000c1c70>] worker_thread+0x2c8/0x4d8 [<ffffffc0000c8944>] kthread+0x104/0x118 [<ffffffc000084fa0>] ret_from_fork+0x10/0x30 ---[ end trace 8fd3b56af7a64339 ]--- v2: drop 'if (!circ->buf)' before taking lock Signed-off-by: Ole Bjørn Midtbø <omidtbo@xxxxxxxxx> --- drivers/tty/serial/serial_core.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 80bb56facfb6..088395c62912 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -539,12 +539,9 @@ static int uart_put_char(struct tty_struct *tty, unsigned char c) unsigned long flags; int ret = 0; - circ = &state->xmit; - if (!circ->buf) - return 0; - port = uart_port_lock(state, flags); - if (port && uart_circ_chars_free(circ) != 0) { + circ = &state->xmit; + if (port && circ->buf && uart_circ_chars_free(circ) != 0) { circ->buf[circ->head] = c; circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); ret = 1; @@ -576,11 +573,13 @@ static int uart_write(struct tty_struct *tty, return -EL3HLT; } + port = uart_port_lock(state, flags); circ = &state->xmit; - if (!circ->buf) + if (!circ->buf) { + uart_port_unlock(port, flags); return 0; + } - port = uart_port_lock(state, flags); while (port) { c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); if (count < c) -- 2.10.2