FAILED: patch "[PATCH] serial: fix ioctl hangup race" failed to apply to 6.18-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x 8ca59f7baee7c72cf7729950b4ab2e7f910c9549
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026100554-renewable-overpower-2a0c@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 8ca59f7baee7c72cf7729950b4ab2e7f910c9549 Mon Sep 17 00:00:00 2001
From: Johan Hovold <[email protected]>
Date: Thu, 10 Sep 2026 15:08:14 +0200
Subject: [PATCH] serial: fix ioctl hangup race
The tty ioctls can race with hangup and end up calling into a tty driver for a device that is already gone or powered down.
Add the missing checks to make sure the port has not been hung up before accessing the hardware to avoid issues like kernel panic due to unclocked accesses.
Note that TIOCGSERIAL, TIOCGICOUNT do not access hardware and are therefore not affected by the race.
Also note that the initialised check in uart_set_ldisc() added by commit 2f70e49ed860 ("serial_core: Check for port state when tty is in error state") should have been done under the port lock to avoid racing with uart_do_autoconfig().
drivers/tty/serial/serial_core.c +29 -12
@@ -896,7 +896,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,upf_t old_flags, new_flags;int retval; -if (!uport) +if (!uport ||tty_io_error (tty))return -EIO ; new_port = new_info->port;
@@ -1119,7 +1119,7 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state)guard (mutex)(&port->mutex); uport =uart_port_check (state); -if (!uport) +if (!uport ||tty_io_error (tty))return -EIO ;if (uport->type !=PORT_UNKNOWN && uport->ops->break_ctl)
@@ -1144,7 +1144,7 @@ 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) +if (!uport ||tty_io_error (tty))return -EIO ;if (tty_port_users (port) !=1 )
@@ -1199,7 +1199,7 @@ static void uart_enable_ms(struct uart_port *uport)* FIXME: This wants extracting into a common all driver implementation * of TIOCMWAIT using tty_port. */ -static int uart_wait_modem_status (struct uart_state *state,unsigned long arg) +static int uart_wait_modem_status (struct tty_struct *tty,struct uart_state *state,unsigned long arg) {struct uart_port *uport;struct tty_port *port = &state->port;
@@ -1213,11 +1213,21 @@ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) uport =uart_port_ref (state);if (!uport)return -EIO ; + +mutex_lock (&port->mutex); +if (tty_io_error (tty)) { +mutex_unlock (&port->mutex); + ret = -EIO ; + goto out_deref; + } +uart_port_lock_irq (uport);memcpy (&cprev, &uport->icount,sizeof (struct uart_icount ));uart_enable_ms (uport);uart_port_unlock_irq (uport); +mutex_unlock (&port->mutex); +add_wait_queue (&port->delta_msr_wait, &wait);for (;;) {uart_port_lock_irq (uport);
@@ -1246,6 +1256,7 @@ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) }__set_current_state (TASK_RUNNING );remove_wait_queue (&port->delta_msr_wait, &wait); +out_deref:uart_port_deref (uport);return ret;
@@ -1568,7 +1579,7 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)/* This should only be used when the hardware is present. */ if (cmd ==TIOCMIWAIT ) -return uart_wait_modem_status (state, arg); +return uart_wait_modem_status (tty, state, arg);/* rs485_config requires more locking than others */ if (cmd ==TIOCSRS485 )
@@ -1624,14 +1635,13 @@ static void uart_set_ldisc(struct tty_struct *tty) {struct uart_state *state = tty->driver_data;struct uart_port *uport; -struct tty_port *port = &state->port; - -if (!tty_port_initialized (port)) -return ;guard (mutex)(&state->port.mutex); uport =uart_port_check (state); -if (uport && uport->ops->set_ldisc) +if (!uport ||tty_io_error (tty)) +return ; + +if (uport->ops->set_ldisc) uport->ops->set_ldisc (uport, &tty->termios); }
@@ -1647,7 +1657,7 @@ static void uart_set_termios(struct tty_struct *tty,guard (mutex)(&state->port.mutex); uport =uart_port_check (state); -if (!uport) +if (!uport ||tty_io_error (tty))return ;/*
@@ -1799,7 +1809,14 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)* 'timeout' / 'expire' give us the maximum amount of time * we wait. */ -while (!port->ops->tx_empty (port)) { +for (;;) { +mutex_lock (&state->port.mutex); +if (tty_io_error (tty) || port->ops->tx_empty (port)) { +mutex_unlock (&state->port.mutex); +break ; + } +mutex_unlock (&state->port.mutex); +msleep_interruptible (jiffies_to_msecs (char_time));if (signal_pending (current))break ;
Sasha Levin
maintainerPATCH 1/2 serial: serial_core: simplify uart_ioctl() returns
From: "Jiri Slaby (SUSE)" <[email protected]>
[ Upstream commit f374a33e90e6cc17b05629636e68a012dc8347ed ]
Neither uart_do_autoconfig(), nor uart_wait_modem_status() can return -ENOIOCTLCMD. The ENOIOCTLCMD checks are there to check if 'cmd' matched against TIOCSERCONFIG, and TIOCMIWAIT respectively. (With 0 or error in 'ret', it does not matter.)
Therefore, the code can simply return from the TIOCSERCONFIG and TIOCMIWAIT spots immediately.
To be more explicit, use 'if' instead of switch-case for those single values.
And return without jumping to the 'out' label -- it can be removed too.
Stable-dep-of: 8ca59f7baee7 ("serial: fix ioctl hangup race")
--- drivers/tty/serial/serial_core.c | 35 ++++++++------------------------ 1 file changed, 9 insertions(+), 26 deletions(-)
drivers/tty/serial/serial_core.c +9 -26
@@ -1564,37 +1564,20 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)void __user *uarg = (void __user *)arg;int ret = -ENOIOCTLCMD ; - -/* -* These ioctls don't rely on the hardware to be present. -*/ -switch (cmd) { -case TIOCSERCONFIG : +/* This ioctl doesn't rely on the hardware to be present. */ +if (cmd ==TIOCSERCONFIG ) {down_write (&tty->termios_rwsem); ret =uart_do_autoconfig (tty, state);up_write (&tty->termios_rwsem); -break ; - } - -if (ret != -ENOIOCTLCMD ) - goto out; - -if (tty_io_error (tty)) { - ret = -EIO ; - goto out; +return ret; } -/* -* The following should only be used when hardware is present. -*/ -switch (cmd) { -case TIOCMIWAIT : - ret =uart_wait_modem_status (state, arg); -break ; - } +if (tty_io_error (tty)) +return -EIO ; -if (ret != -ENOIOCTLCMD ) - goto out; +/* This should only be used when the hardware is present. */ +if (cmd ==TIOCMIWAIT ) +return uart_wait_modem_status (state, arg);/* rs485_config requires more locking than others */ if (cmd ==TIOCSRS485 )
@@ -1642,7 +1625,7 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)mutex_unlock (&port->mutex);if (cmd ==TIOCSRS485 )up_write (&tty->termios_rwsem); -out: +return ret; }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 serial: fix ioctl hangup race
From: Johan Hovold <[email protected]>
[ Upstream commit 8ca59f7baee7c72cf7729950b4ab2e7f910c9549 ]
The tty ioctls can race with hangup and end up calling into a tty driver for a device that is already gone or powered down.
Add the missing checks to make sure the port has not been hung up before accessing the hardware to avoid issues like kernel panic due to unclocked accesses.
Note that TIOCGSERIAL, TIOCGICOUNT do not access hardware and are therefore not affected by the race.
Also note that the initialised check in uart_set_ldisc() added by commit 2f70e49ed860 ("serial_core: Check for port state when tty is in error state") should have been done under the port lock to avoid racing with uart_do_autoconfig().
--- drivers/tty/serial/serial_core.c | 41 ++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-)
drivers/tty/serial/serial_core.c +29 -12
@@ -899,7 +899,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,upf_t old_flags, new_flags;int retval; -if (!uport) +if (!uport ||tty_io_error (tty))return -EIO ; new_port = new_info->port;
@@ -1126,7 +1126,7 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state)guard (mutex)(&port->mutex); uport =uart_port_check (state); -if (!uport) +if (!uport ||tty_io_error (tty))return -EIO ;if (uport->type !=PORT_UNKNOWN && uport->ops->break_ctl)
@@ -1151,7 +1151,7 @@ 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) +if (!uport ||tty_io_error (tty))return -EIO ;if (tty_port_users (port) !=1 )
@@ -1206,7 +1206,7 @@ static void uart_enable_ms(struct uart_port *uport)* FIXME: This wants extracting into a common all driver implementation * of TIOCMWAIT using tty_port. */ -static int uart_wait_modem_status (struct uart_state *state,unsigned long arg) +static int uart_wait_modem_status (struct tty_struct *tty,struct uart_state *state,unsigned long arg) {struct uart_port *uport;struct tty_port *port = &state->port;
@@ -1220,11 +1220,21 @@ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) uport =uart_port_ref (state);if (!uport)return -EIO ; + +mutex_lock (&port->mutex); +if (tty_io_error (tty)) { +mutex_unlock (&port->mutex); + ret = -EIO ; + goto out_deref; + } +uart_port_lock_irq (uport);memcpy (&cprev, &uport->icount,sizeof (struct uart_icount ));uart_enable_ms (uport);uart_port_unlock_irq (uport); +mutex_unlock (&port->mutex); +add_wait_queue (&port->delta_msr_wait, &wait);for (;;) {set_current_state (TASK_INTERRUPTIBLE );
@@ -1253,6 +1263,7 @@ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) }__set_current_state (TASK_RUNNING );remove_wait_queue (&port->delta_msr_wait, &wait); +out_deref:uart_port_deref (uport);return ret;
@@ -1577,7 +1588,7 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)/* This should only be used when the hardware is present. */ if (cmd ==TIOCMIWAIT ) -return uart_wait_modem_status (state, arg); +return uart_wait_modem_status (tty, state, arg);/* rs485_config requires more locking than others */ if (cmd ==TIOCSRS485 )
@@ -1633,14 +1644,13 @@ static void uart_set_ldisc(struct tty_struct *tty) {struct uart_state *state = tty->driver_data;struct uart_port *uport; -struct tty_port *port = &state->port; - -if (!tty_port_initialized (port)) -return ;mutex_lock (&state->port.mutex); uport =uart_port_check (state); -if (uport && uport->ops->set_ldisc) +if (!uport ||tty_io_error (tty)) +return ; + +if (uport->ops->set_ldisc) uport->ops->set_ldisc (uport, &tty->termios);mutex_unlock (&state->port.mutex); }
@@ -1657,7 +1667,7 @@ static void uart_set_termios(struct tty_struct *tty,guard (mutex)(&state->port.mutex); uport =uart_port_check (state); -if (!uport) +if (!uport ||tty_io_error (tty))return ;/*
@@ -1810,7 +1820,14 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)* 'timeout' / 'expire' give us the maximum amount of time * we wait. */ -while (!port->ops->tx_empty (port)) { +for (;;) { +mutex_lock (&state->port.mutex); +if (tty_io_error (tty) || port->ops->tx_empty (port)) { +mutex_unlock (&state->port.mutex); +break ; + } +mutex_unlock (&state->port.mutex); +msleep_interruptible (jiffies_to_msecs (char_time));if (signal_pending (current))break ;
Signature · 1 line
2.53.0