git: 2520c8bd2c42 - stable/15 - tty: do not recurse on ttydev_close()

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Sun, 16 Aug 2026 02:46:22 UTC
The branch stable/15 has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=2520c8bd2c42f4333bc1f4b94a78efa625bc97c9

commit 2520c8bd2c42f4333bc1f4b94a78efa625bc97c9
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-08-07 19:48:31 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-08-16 02:41:17 +0000

    tty: do not recurse on ttydev_close()
    
    (cherry picked from commit e2cfbd498af88a211b0b347861cfd989e57cd1fb)
---
 sys/kern/tty.c | 30 +++++++++++++++++++++---------
 sys/sys/tty.h  |  1 +
 2 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index 5f6afa49b70d..09ed1c6d0c5d 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -71,7 +71,7 @@
 
 static MALLOC_DEFINE(M_TTY, "tty", "tty device");
 
-static void tty_rel_free(struct tty *tp);
+static void tty_rel_free(struct tty *tp, bool inttydevclose);
 
 static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list);
 static struct sx tty_list_sx;
@@ -229,13 +229,15 @@ ttydev_enter(struct tty *tp)
 }
 
 static void
-ttydev_leave(struct tty *tp)
+ttydev_leave(struct tty *tp, bool inttydevclose)
 {
 
 	tty_assert_locked(tp);
 
 	if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) {
 		/* Device is still opened somewhere. */
+		if (inttydevclose)
+			tp->t_flags &= ~TF_INDEVCLOSE;
 		tty_unlock(tp);
 		return;
 	}
@@ -262,7 +264,7 @@ ttydev_leave(struct tty *tp)
 
 	tp->t_flags &= ~TF_OPENCLOSE;
 	cv_broadcast(&tp->t_dcdwait);
-	tty_rel_free(tp);
+	tty_rel_free(tp, inttydevclose);
 }
 
 /*
@@ -363,7 +365,7 @@ ttydev_open(struct cdev *dev, int oflags, int devtype __unused,
 
 done:	tp->t_flags &= ~TF_OPENCLOSE;
 	cv_broadcast(&tp->t_dcdwait);
-	ttydev_leave(tp);
+	ttydev_leave(tp, false);
 
 	return (error);
 }
@@ -375,6 +377,11 @@ ttydev_close(struct cdev *dev, int fflag, int devtype __unused,
 	struct tty *tp = dev->si_drv1;
 
 	tty_lock(tp);
+	if ((tp->t_flags & TF_INDEVCLOSE) != 0) {
+		tty_unlock(tp);
+		return (0);
+	}
+	tp->t_flags |= TF_INDEVCLOSE;
 
 	/*
 	 * Don't actually close the device if it is being used as the
@@ -388,6 +395,7 @@ ttydev_close(struct cdev *dev, int fflag, int devtype __unused,
 		tp->t_flags &= ~(TF_OPENED_IN|TF_OPENED_OUT);
 
 	if (tp->t_flags & TF_OPENED) {
+		tp->t_flags &= ~TF_INDEVCLOSE;
 		tty_unlock(tp);
 		return (0);
 	}
@@ -407,7 +415,7 @@ ttydev_close(struct cdev *dev, int fflag, int devtype __unused,
 	cv_broadcast(&tp->t_bgwait);
 	cv_broadcast(&tp->t_dcdwait);
 
-	ttydev_leave(tp);
+	ttydev_leave(tp, true);
 
 	return (0);
 }
@@ -1144,7 +1152,7 @@ tty_dealloc(void *arg)
 }
 
 static void
-tty_rel_free(struct tty *tp)
+tty_rel_free(struct tty *tp, bool inttydevclose)
 {
 	struct cdev *dev;
 
@@ -1153,6 +1161,8 @@ tty_rel_free(struct tty *tp)
 #define	TF_ACTIVITY	(TF_GONE|TF_OPENED|TF_HOOK|TF_OPENCLOSE)
 	if (tp->t_sessioncnt != 0 || (tp->t_flags & TF_ACTIVITY) != TF_GONE) {
 		/* TTY is still in use. */
+		if (inttydevclose)
+			tp->t_flags &= ~TF_INDEVCLOSE;
 		tty_unlock(tp);
 		return;
 	}
@@ -1163,6 +1173,8 @@ tty_rel_free(struct tty *tp)
 	/* TTY can be deallocated. */
 	dev = tp->t_dev;
 	tp->t_dev = NULL;
+	if (inttydevclose)
+		tp->t_flags &= ~TF_INDEVCLOSE;
 	tty_unlock(tp);
 
 	if (dev != NULL) {
@@ -1199,7 +1211,7 @@ tty_rel_sess(struct tty *tp, struct session *sess)
 		MPASS(tp->t_pgrp == NULL);
 	}
 	tp->t_sessioncnt--;
-	tty_rel_free(tp);
+	tty_rel_free(tp, false);
 }
 
 void
@@ -1218,7 +1230,7 @@ tty_rel_gone(struct tty *tp)
 	cv_broadcast(&tp->t_dcdwait);
 
 	tp->t_flags |= TF_GONE;
-	tty_rel_free(tp);
+	tty_rel_free(tp, false);
 }
 
 static int
@@ -2226,7 +2238,7 @@ ttyhook_unregister(struct tty *tp)
 	ttydisc_optimize(tp);
 
 	/* Maybe deallocate the TTY as well. */
-	tty_rel_free(tp);
+	tty_rel_free(tp, false);
 }
 
 /*
diff --git a/sys/sys/tty.h b/sys/sys/tty.h
index f1b2646c942d..6a58908d97a4 100644
--- a/sys/sys/tty.h
+++ b/sys/sys/tty.h
@@ -87,6 +87,7 @@ struct tty {
 #define	TF_BUSY_IN	0x20000	/* Process busy in read() -- not supported. */
 #define	TF_BUSY_OUT	0x40000	/* Process busy in write(). */
 #define	TF_BUSY		(TF_BUSY_IN|TF_BUSY_OUT)
+#define	TF_INDEVCLOSE	0x80000	/* In ttydev_close() */
 	unsigned int	t_revokecnt;	/* (t) revoke() count. */
 
 	/* Buffering mechanisms. */