FAILED: patch "[PATCH] tty: port: fix tty_port_tty_vhangup() race" failed to apply to 6.6-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.6-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.6.y
git checkout FETCH_HEAD
git cherry-pick -x b48820de8c1a596c5b99775524dfce73c6576c5d
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026100550-nest-aviator-8a3f@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From b48820de8c1a596c5b99775524dfce73c6576c5d Mon Sep 17 00:00:00 2001
From: Johan Hovold <[email protected]>
Date: Thu, 10 Sep 2026 14:51:14 +0200
Subject: [PATCH] tty: port: fix tty_port_tty_vhangup() race
Drivers rely on tty_port_tty_vhangup() to synchronously hang up their ports before tearing down resources as part of deregistration (e.g. on physical disconnect).
If tty_port_tty_vhangup() races with another hangup it may however return before the port has been shut down. This is in turn can lead to use-after-free and NULL-pointer dereferences in racing ioctls (including a synchronous hangup) and in processing of racing asynchronous hangups (carrier loss).
Add the missing serialisation to tty_port_tty_vhangup() and tty_port_hangup() so that the former does not return until the port has been shut down also when there is a racing hangup.
Note that serial core does not use tty_port_hangup(), but it already holds the port mutex when clearing port->tty and shutting down the port in uart_hangup().
drivers/tty/tty_port.c +58 -29
@@ -340,19 +340,9 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) }EXPORT_SYMBOL (tty_port_tty_set); -/** -* tty_port_shutdown - internal helper to shutdown the device -* @port: tty port to be shut down -* @tty: the associated tty -* -* It is used by tty_port_hangup() and tty_port_close(). Its task is to -* shutdown the device if it was initialized (note consoles remain -* functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes -* @port->ops->shutdown(). -*/ -static void tty_port_shutdown (struct tty_port *port,struct tty_struct *tty) +static void tty_port_shutdown_locked (struct tty_port *port,struct tty_struct *tty) { -guard (mutex)(&port->mutex); +lockdep_assert_held (&port->mutex);if (port->console)return ;
@@ -372,6 +362,23 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) port->ops->shutdown (port); } +/** +* tty_port_shutdown - internal helper to shutdown the device +* @port: tty port to be shut down +* @tty: the associated tty +* +* It is used by tty_port_hangup() and tty_port_close(). Its task is to +* shutdown the device if it was initialized (note consoles remain +* functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes +* @port->ops->shutdown(). +*/ +static void tty_port_shutdown (struct tty_port *port,struct tty_struct *tty) +{ +guard (mutex)(&port->mutex); + +tty_port_shutdown_locked (port, tty); +} +/** * tty_port_hangup - hangup helper * @port: tty port
@@ -385,36 +392,58 @@ void tty_port_hangup(struct tty_port *port) {struct tty_struct *tty; - scoped_guard(spinlock_irqsave, &port->lock) { - port->count =0 ; - tty = port->tty; -if (tty) -set_bit (TTY_IO_ERROR , &tty->flags); - port->tty =NULL ; - } + scoped_guard(mutex, &port->mutex) { + scoped_guard(spinlock_irqsave, &port->lock) { + port->count =0 ; + tty = port->tty; +if (tty) +set_bit (TTY_IO_ERROR , &tty->flags); + port->tty =NULL ; + } -tty_port_set_active (port, false); -tty_port_shutdown (port, tty); +tty_port_set_active (port, false); +tty_port_shutdown_locked (port, tty); + }tty_kref_put (tty);wake_up_interruptible (&port->open_wait);wake_up_interruptible (&port->delta_msr_wait); }EXPORT_SYMBOL (tty_port_hangup); -void __tty_port_tty_hangup (struct tty_port *port,bool check_clocal,bool async) +/** +* tty_port_tty_hangup - helper to hang up a tty asynchronously +* @port: tty port +* @check_clocal: hang only ttys with %CLOCAL unset? +*/ +void tty_port_tty_hangup (struct tty_port *port,bool check_clocal) { scoped_guard(tty_port_tty, port) {struct tty_struct *tty =scoped_tty (); -if (!check_clocal || !C_CLOCAL (tty)) { -if (async) -tty_hangup (tty); -else -tty_vhangup (tty); - } +if (!check_clocal || !C_CLOCAL (tty)) +tty_hangup (tty); } } -EXPORT_SYMBOL_GPL (__tty_port_tty_hangup); +EXPORT_SYMBOL_GPL (tty_port_tty_hangup); + +/** +* tty_port_tty_vhangup - helper to hang up a tty synchronously +* @port: tty port +*/ +void tty_port_tty_vhangup (struct tty_port *port) +{ +struct tty_struct *tty; + +mutex_lock (&port->mutex); + tty =tty_port_tty_get (port); +mutex_unlock (&port->mutex); + +if (tty) { +tty_vhangup (tty); +tty_kref_put (tty); + } +} +EXPORT_SYMBOL_GPL (tty_port_tty_vhangup);/** * tty_port_tty_wakeup - helper to wake up a tty
include/linux/tty_port.h +2 -20
@@ -245,7 +245,8 @@ bool tty_port_carrier_raised(struct tty_port *port);void tty_port_raise_dtr_rts (struct tty_port *port);void tty_port_lower_dtr_rts (struct tty_port *port);void tty_port_hangup (struct tty_port *port); -void __tty_port_tty_hangup (struct tty_port *port,bool check_clocal,bool async); +void tty_port_tty_hangup (struct tty_port *port,bool check_clocal); +void tty_port_tty_vhangup (struct tty_port *port);void tty_port_tty_wakeup (struct tty_port *port);int tty_port_block_til_ready (struct tty_port *port,struct tty_struct *tty,struct file *filp);
@@ -264,25 +265,6 @@ static inline int tty_port_users(struct tty_port *port)return port->count + port->blocked_open; } -/** -* tty_port_tty_hangup - helper to hang up a tty asynchronously -* @port: tty port -* @check_clocal: hang only ttys with %CLOCAL unset? -*/ -static inline void tty_port_tty_hangup (struct tty_port *port,bool check_clocal) -{ -__tty_port_tty_hangup (port, check_clocal, true); -} - -/** -* tty_port_tty_vhangup - helper to hang up a tty synchronously -* @port: tty port -*/ -static inline void tty_port_tty_vhangup (struct tty_port *port) -{ -__tty_port_tty_hangup (port, false, false); -} -#ifdef CONFIG_TTY void tty_kref_put (struct tty_struct *tty);__DEFINE_CLASS_IS_CONDITIONAL (tty_port_tty, true);
PATCH 1/1 tty: port: fix tty_port_tty_vhangup() race
From: Johan Hovold <[email protected]>
[ Upstream commit b48820de8c1a596c5b99775524dfce73c6576c5d ]
Drivers rely on tty_port_tty_vhangup() to synchronously hang up their ports before tearing down resources as part of deregistration (e.g. on physical disconnect).
If tty_port_tty_vhangup() races with another hangup it may however return before the port has been shut down. This is in turn can lead to use-after-free and NULL-pointer dereferences in racing ioctls (including a synchronous hangup) and in processing of racing asynchronous hangups (carrier loss).
Add the missing serialisation to tty_port_tty_vhangup() and tty_port_hangup() so that the former does not return until the port has been shut down also when there is a racing hangup.
Note that serial core does not use tty_port_hangup(), but it already holds the port mutex when clearing port->tty and shutting down the port in uart_hangup().
[ Ting-Han: use mutex_lock()/spin_lock_irqsave() instead of guard(),
scoped_guard() and the tty_port_tty class, matching the existing code in this version of tty_port.c ]
--- drivers/tty/tty_port.c | 74 +++++++++++++++++++++++++++------------- include/linux/tty_port.h | 22 ++---------- 2 files changed, 53 insertions(+), 43 deletions(-)
drivers/tty/tty_port.c +51 -23
@@ -348,21 +348,12 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) }EXPORT_SYMBOL (tty_port_tty_set); -/** -* tty_port_shutdown - internal helper to shutdown the device -* @port: tty port to be shut down -* @tty: the associated tty -* -* It is used by tty_port_hangup() and tty_port_close(). Its task is to -* shutdown the device if it was initialized (note consoles remain -* functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes -* @port->ops->shutdown(). -*/ -static void tty_port_shutdown (struct tty_port *port,struct tty_struct *tty) +static void tty_port_shutdown_locked (struct tty_port *port,struct tty_struct *tty) { -mutex_lock (&port->mutex); +lockdep_assert_held (&port->mutex); +if (port->console) - goto out; +return ;if (tty_port_initialized (port)) {tty_port_set_initialized (port, false);
@@ -376,7 +367,22 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)if (port->ops->shutdown) port->ops->shutdown (port); } -out: +} + +/** +* tty_port_shutdown - internal helper to shutdown the device +* @port: tty port to be shut down +* @tty: the associated tty +* +* It is used by tty_port_hangup() and tty_port_close(). Its task is to +* shutdown the device if it was initialized (note consoles remain +* functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes +* @port->ops->shutdown(). +*/ +static void tty_port_shutdown (struct tty_port *port,struct tty_struct *tty) +{ +mutex_lock (&port->mutex); +tty_port_shutdown_locked (port, tty);mutex_unlock (&port->mutex); }
@@ -394,6 +400,7 @@ void tty_port_hangup(struct tty_port *port)struct tty_struct *tty;unsigned long flags; +mutex_lock (&port->mutex);spin_lock_irqsave (&port->lock, flags); port->count =0 ; tty = port->tty;
@@ -402,26 +409,47 @@ void tty_port_hangup(struct tty_port *port) port->tty =NULL ;spin_unlock_irqrestore (&port->lock, flags);tty_port_set_active (port, false); -tty_port_shutdown (port, tty); +tty_port_shutdown_locked (port, tty); +mutex_unlock (&port->mutex);tty_kref_put (tty);wake_up_interruptible (&port->open_wait);wake_up_interruptible (&port->delta_msr_wait); }EXPORT_SYMBOL (tty_port_hangup); -void __tty_port_tty_hangup (struct tty_port *port,bool check_clocal,bool async) +/** +* tty_port_tty_hangup - helper to hang up a tty asynchronously +* @port: tty port +* @check_clocal: hang only ttys with %CLOCAL unset? +*/ +void tty_port_tty_hangup (struct tty_port *port,bool check_clocal) {struct tty_struct *tty =tty_port_tty_get (port); -if (tty && (!check_clocal || !C_CLOCAL (tty))) { -if (async) -tty_hangup (tty); -else -tty_vhangup (tty); - } +if (tty && (!check_clocal || !C_CLOCAL (tty))) +tty_hangup (tty);tty_kref_put (tty); } -EXPORT_SYMBOL_GPL (__tty_port_tty_hangup); +EXPORT_SYMBOL_GPL (tty_port_tty_hangup); + +/** +* tty_port_tty_vhangup - helper to hang up a tty synchronously +* @port: tty port +*/ +void tty_port_tty_vhangup (struct tty_port *port) +{ +struct tty_struct *tty; + +mutex_lock (&port->mutex); + tty =tty_port_tty_get (port); +mutex_unlock (&port->mutex); + +if (tty) { +tty_vhangup (tty); +tty_kref_put (tty); + } +} +EXPORT_SYMBOL_GPL (tty_port_tty_vhangup);/** * tty_port_tty_wakeup - helper to wake up a tty
include/linux/tty_port.h +2 -20
@@ -235,7 +235,8 @@ bool tty_port_carrier_raised(struct tty_port *port);void tty_port_raise_dtr_rts (struct tty_port *port);void tty_port_lower_dtr_rts (struct tty_port *port);void tty_port_hangup (struct tty_port *port); -void __tty_port_tty_hangup (struct tty_port *port,bool check_clocal,bool async); +void tty_port_tty_hangup (struct tty_port *port,bool check_clocal); +void tty_port_tty_vhangup (struct tty_port *port);void tty_port_tty_wakeup (struct tty_port *port);int tty_port_block_til_ready (struct tty_port *port,struct tty_struct *tty,struct file *filp);
@@ -254,23 +255,4 @@ static inline int tty_port_users(struct tty_port *port) return port->count + port->blocked_open; } -/** -* tty_port_tty_hangup - helper to hang up a tty asynchronously -* @port: tty port -* @check_clocal: hang only ttys with %CLOCAL unset? -*/ -static inline void tty_port_tty_hangup (struct tty_port *port,bool check_clocal) -{ -__tty_port_tty_hangup (port, check_clocal, true); -} - -/** -* tty_port_tty_vhangup - helper to hang up a tty synchronously -* @port: tty port -*/ -static inline void tty_port_tty_vhangup (struct tty_port *port) -{ -__tty_port_tty_hangup (port, false, false); -} -#endif
Signature · 1 line
2.43.0
Re: [PATCH 6.6.y] tty: port: fix tty_port_tty_vhangup() race
Quoted 3 lines
Add the missing serialisation to
tty_port_tty_vhangup()andtty_port_hangup()so that the former does not return until the port has been shut down also when there is a racing hangup.
Queued for 6.12 and 6.6, thanks.
Signature · 2 lines
Thanks, Sasha