tty: add the option to have a tty reject a new ldisc
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 23 Apr 2024 16:33:39 +0000 (09:33 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 4 May 2024 16:45:11 +0000 (18:45 +0200)
... and use it to limit the virtual terminals to just N_TTY.  They are
kind of special, and in particular, the "con_write()" routine violates
the "writes cannot sleep" rule that some ldiscs rely on.

This avoids the

   BUG: sleeping function called from invalid context at kernel/printk/printk.c:2659

when N_GSM has been attached to a virtual console, and gsmld_write()
calls con_write() while holding a spinlock, and con_write() then tries
to get the console lock.

Tested-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Cc: Jiri Slaby <jirislaby@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Daniel Starke <daniel.starke@siemens.com>
Reported-by: syzbot <syzbot+dbac96d8e73b61aa559c@syzkaller.appspotmail.com>
Closes: https://syzkaller.appspot.com/bug?extid=dbac96d8e73b61aa559c
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/r/20240423163339.59780-1-torvalds@linux-foundation.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/tty_ldisc.c
drivers/tty/vt/vt.c
include/linux/tty_driver.h

index 3f68e213df1f70853ee19480a522ecaaaf420e33..d80e9d4c974b4f94dcef36963f03c4c7b1ff020b 100644 (file)
@@ -545,6 +545,12 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
                goto out;
        }
 
+       if (tty->ops->ldisc_ok) {
+               retval = tty->ops->ldisc_ok(tty, disc);
+               if (retval)
+                       goto out;
+       }
+
        old_ldisc = tty->ldisc;
 
        /* Shutdown the old discipline. */
index 9b5b98dfc8b4017289db11d3a42780760cad38fe..cd87e3d1291edcddf0f672bb71e5eecd3a2bbe3d 100644 (file)
@@ -3576,6 +3576,15 @@ static void con_cleanup(struct tty_struct *tty)
        tty_port_put(&vc->port);
 }
 
+/*
+ * We can't deal with anything but the N_TTY ldisc,
+ * because we can sleep in our write() routine.
+ */
+static int con_ldisc_ok(struct tty_struct *tty, int ldisc)
+{
+       return ldisc == N_TTY ? 0 : -EINVAL;
+}
+
 static int default_color           = 7; /* white */
 static int default_italic_color    = 2; // green (ASCII)
 static int default_underline_color = 3; // cyan (ASCII)
@@ -3695,6 +3704,7 @@ static const struct tty_operations con_ops = {
        .resize = vt_resize,
        .shutdown = con_shutdown,
        .cleanup = con_cleanup,
+       .ldisc_ok = con_ldisc_ok,
 };
 
 static struct cdev vc0_cdev;
index 7372124fbf90b3dc724e5ea40c7f0168191cd275..dd4b31ce6d5d4249b3aa9cf2f08f6716c6d57b5c 100644 (file)
@@ -154,6 +154,13 @@ struct serial_struct;
  *
  *     Optional. Called under the @tty->termios_rwsem. May sleep.
  *
+ * @ldisc_ok: ``int ()(struct tty_struct *tty, int ldisc)``
+ *
+ *     This routine allows the @tty driver to decide if it can deal
+ *     with a particular @ldisc.
+ *
+ *     Optional. Called under the @tty->ldisc_sem and @tty->termios_rwsem.
+ *
  * @set_ldisc: ``void ()(struct tty_struct *tty)``
  *
  *     This routine allows the @tty driver to be notified when the device's
@@ -372,6 +379,7 @@ struct tty_operations {
        void (*hangup)(struct tty_struct *tty);
        int (*break_ctl)(struct tty_struct *tty, int state);
        void (*flush_buffer)(struct tty_struct *tty);
+       int (*ldisc_ok)(struct tty_struct *tty, int ldisc);
        void (*set_ldisc)(struct tty_struct *tty);
        void (*wait_until_sent)(struct tty_struct *tty, int timeout);
        void (*send_xchar)(struct tty_struct *tty, u8 ch);