The branch main has been updated by kib:

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

commit e2cfbd498af88a211b0b347861cfd989e57cd1fb
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2026-08-07 19:48:31 +0000
Commit:     Konstantin Belousov <[email protected]>
CommitDate: 2026-08-13 15:29:14 +0000

    tty: do not recurse on ttydev_close()
    
    When the terminal cdev is closed due to revoke, ttydev_close() destroys
    t_inpoll and t_outpoll selinfos.  Since corresponding knotes reference
    files pointing to the same tty cdev, it fdrop()s them.  But then the
    VOP_CLOSE() call would recurse into the ttydev_close() for the same tty.
    More, because the devfs vnode is already doomed, each close call gets
    the FREVOKE flag set.
    
    As result, the kernel is recursing as deep into the ttydev_close() as
    there are opened files referencing the same tty, which have the knotes
    installed.  Basically, the recursion level is controlled by userspace.
    
    Prevent it by marking the tty that is handled by ttydev_close(), with
    the TF_INDEVCLOSE flag.  Do nothing in ttydev_close() when the flag is
    already set, avoiding recursion.
    
    Fixes:  acd5638e268a ("tty: delete knotes when TTY is revoked")
    Reviewed by:    markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D58706
---
 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 3de1aabae36b..bf1ef6ea6c88 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -73,7 +73,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;
@@ -231,13 +231,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;
        }
@@ -264,7 +266,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);
 }
 
 /*
@@ -365,7 +367,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);
 }
@@ -377,6 +379,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
@@ -390,6 +397,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);
        }
@@ -409,7 +417,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);
 }
@@ -1146,7 +1154,7 @@ tty_dealloc(void *arg)
 }
 
 static void
-tty_rel_free(struct tty *tp)
+tty_rel_free(struct tty *tp, bool inttydevclose)
 {
        struct cdev *dev;
 
@@ -1155,6 +1163,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;
        }
@@ -1165,6 +1175,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) {
@@ -1201,7 +1213,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
@@ -1220,7 +1232,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
@@ -2231,7 +2243,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. */

Reply via email to