serial: core: fix TIOCSSERIAL and TIOCSERCONFIG on uninitialised ports

Commit 8ca59f7bae ("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 8ca59f7bae ("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: 8ca59f7bae ("serial: fix ioctl hangup race")
Cc: stable@kernel.org
Signed-off-by: Hengyu Liang <hengyul@cs.unc.edu>
Tested-by: Johan Hovold <johan@kernel.org>
Reviewed-by: Johan Hovold <johan@kernel.org>
Link: https://patch.msgid.link/20261010034616.3915864-1-hengyul@cs.unc.edu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Hengyu Liang 2026-10-09 23:46:16 -04:00 committed by Greg Kroah-Hartman
parent ae588038e3
commit e1f46a09df

View File

@ -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)