From e1f46a09df93650288babb23de6e34bff73e80e6 Mon Sep 17 00:00:00 2001 From: Hengyu Liang Date: Fri, 9 Oct 2026 23:46:16 -0400 Subject: [PATCH] serial: core: fix TIOCSSERIAL and TIOCSERCONFIG on uninitialised ports Commit 8ca59f7baee7 ("serial: fix ioctl hangup race") made uart_set_info() and uart_do_autoconfig() return -EIO when TTY_IO_ERROR is set, so that they do not run on a port that has been hung up. However, TTY_IO_ERROR is also set on a port that could not be started, for example because its type is unknown, and by uart_shutdown(), which both functions call. As of now, setserial cannot configure a port that the kernel did not detect, and an autoconfig that follows a port, irq or type change in the same setserial call fails on any port. The issue can be reproduced with setserial (busybox) on a port without a detected UART, for example ttyS3 of a QEMU guest: setserial /dev/ttyS3 uart 16550A setserial -g /dev/ttyS3 Before commit 8ca59f7baee7 ("serial: fix ioctl hangup race"), the result is: /dev/ttyS3, UART: 16550A, Port: 0x02e8, IRQ: 3 After that commit, the result is: setserial: can't set serial info: Input/output error /dev/ttyS3, UART: unknown, Port: 0x02e8, IRQ: 3 This patch will check tty_port_active() in these two functions instead. uart_hangup() clears it under the port mutex, and it stays set on an open port that could not be started. Fixes: 8ca59f7baee7 ("serial: fix ioctl hangup race") Cc: stable@kernel.org Signed-off-by: Hengyu Liang Tested-by: Johan Hovold Reviewed-by: Johan Hovold Link: https://patch.msgid.link/20261010034616.3915864-1-hengyul@cs.unc.edu Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 05450529871e..9eea8d1a0e8a 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -895,7 +895,11 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, upf_t old_flags, new_flags; int retval; - if (!uport || tty_io_error(tty)) + /* + * TTY_IO_ERROR is also set on a port that could not be started, and + * this is how such a port is configured. Only refuse a hung up port. + */ + if (!uport || !tty_port_active(port)) return -EIO; new_port = new_info->port; @@ -1143,7 +1147,8 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) */ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &port->mutex) { uport = uart_port_check(state); - if (!uport || tty_io_error(tty)) + /* As in uart_set_info(), only refuse a hung up port. */ + if (!uport || !tty_port_active(port)) return -EIO; if (tty_port_users(port) != 1)