Skip to content

Commit 8a8dabf

Browse files
Alan Coxgregkh
authored andcommitted
tty: handle the case where we cannot restore a line discipline
Historically the N_TTY driver could never fail but this has become broken over time. Rather than trying to rewrite half the ldisc layer to fix the breakage introduce a second level of fallback with an N_NULL ldisc which cannot fail, and thus restore the guarantees required by the ldisc layer. We still try and fail to N_TTY first. It's much more useful to find yourself back in your old ldisc (first attempt) or in N_TTY (second attempt), and while I'm not aware of any code out there that makes those assumptions it's good to drive(r) defensively. Signed-off-by: Alan Cox <[email protected]> Reported-by: Dmitry Vyukov <[email protected]> Tested-by: Dmitry Vyukov <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 47f58e3 commit 8a8dabf

File tree

4 files changed

+113
-15
lines changed

4 files changed

+113
-15
lines changed

drivers/tty/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
obj-$(CONFIG_TTY) += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
22
tty_buffer.o tty_port.o tty_mutex.o \
3-
tty_ldsem.o tty_baudrate.o tty_jobctrl.o
3+
tty_ldsem.o tty_baudrate.o tty_jobctrl.o \
4+
n_null.o
45
obj-$(CONFIG_LEGACY_PTYS) += pty.o
56
obj-$(CONFIG_UNIX98_PTYS) += pty.o
67
obj-$(CONFIG_AUDIT) += tty_audit.o

drivers/tty/n_null.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include <linux/types.h>
2+
#include <linux/errno.h>
3+
#include <linux/tty.h>
4+
#include <linux/module.h>
5+
6+
/*
7+
* n_null.c - Null line discipline used in the failure path
8+
*
9+
* Copyright (C) Intel 2017
10+
*
11+
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12+
*
13+
* This program is free software; you can redistribute it and/or modify
14+
* it under the terms of the GNU General Public License version 2
15+
* as published by the Free Software Foundation.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU General Public License for more details.
21+
*
22+
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23+
*/
24+
25+
static int n_null_open(struct tty_struct *tty)
26+
{
27+
return 0;
28+
}
29+
30+
static void n_null_close(struct tty_struct *tty)
31+
{
32+
}
33+
34+
static ssize_t n_null_read(struct tty_struct *tty, struct file *file,
35+
unsigned char __user * buf, size_t nr)
36+
{
37+
return -EOPNOTSUPP;
38+
}
39+
40+
static ssize_t n_null_write(struct tty_struct *tty, struct file *file,
41+
const unsigned char *buf, size_t nr)
42+
{
43+
return -EOPNOTSUPP;
44+
}
45+
46+
static void n_null_receivebuf(struct tty_struct *tty,
47+
const unsigned char *cp, char *fp,
48+
int cnt)
49+
{
50+
}
51+
52+
static struct tty_ldisc_ops null_ldisc = {
53+
.owner = THIS_MODULE,
54+
.magic = TTY_LDISC_MAGIC,
55+
.name = "n_null",
56+
.open = n_null_open,
57+
.close = n_null_close,
58+
.read = n_null_read,
59+
.write = n_null_write,
60+
.receive_buf = n_null_receivebuf
61+
};
62+
63+
static int __init n_null_init(void)
64+
{
65+
BUG_ON(tty_register_ldisc(N_NULL, &null_ldisc));
66+
return 0;
67+
}
68+
69+
static void __exit n_null_exit(void)
70+
{
71+
tty_unregister_ldisc(N_NULL);
72+
}
73+
74+
module_init(n_null_init);
75+
module_exit(n_null_exit);
76+
77+
MODULE_LICENSE("GPL");
78+
MODULE_AUTHOR("Alan Cox");
79+
MODULE_ALIAS_LDISC(N_NULL);
80+
MODULE_DESCRIPTION("Null ldisc driver");

drivers/tty/tty_ldisc.c

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,29 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
491491
tty_ldisc_debug(tty, "%p: closed\n", ld);
492492
}
493493

494+
/**
495+
* tty_ldisc_failto - helper for ldisc failback
496+
* @tty: tty to open the ldisc on
497+
* @ld: ldisc we are trying to fail back to
498+
*
499+
* Helper to try and recover a tty when switching back to the old
500+
* ldisc fails and we need something attached.
501+
*/
502+
503+
static int tty_ldisc_failto(struct tty_struct *tty, int ld)
504+
{
505+
struct tty_ldisc *disc = tty_ldisc_get(tty, ld);
506+
int r;
507+
508+
if (IS_ERR(disc))
509+
return PTR_ERR(disc);
510+
tty->ldisc = disc;
511+
tty_set_termios_ldisc(tty, ld);
512+
if ((r = tty_ldisc_open(tty, disc)) < 0)
513+
tty_ldisc_put(disc);
514+
return r;
515+
}
516+
494517
/**
495518
* tty_ldisc_restore - helper for tty ldisc change
496519
* @tty: tty to recover
@@ -502,27 +525,20 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
502525

503526
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
504527
{
505-
struct tty_ldisc *new_ldisc;
506-
int r;
507-
508528
/* There is an outstanding reference here so this is safe */
509529
old = tty_ldisc_get(tty, old->ops->num);
510530
WARN_ON(IS_ERR(old));
511531
tty->ldisc = old;
512532
tty_set_termios_ldisc(tty, old->ops->num);
513533
if (tty_ldisc_open(tty, old) < 0) {
514534
tty_ldisc_put(old);
515-
/* This driver is always present */
516-
new_ldisc = tty_ldisc_get(tty, N_TTY);
517-
if (IS_ERR(new_ldisc))
518-
panic("n_tty: get");
519-
tty->ldisc = new_ldisc;
520-
tty_set_termios_ldisc(tty, N_TTY);
521-
r = tty_ldisc_open(tty, new_ldisc);
522-
if (r < 0)
523-
panic("Couldn't open N_TTY ldisc for "
524-
"%s --- error %d.",
525-
tty_name(tty), r);
535+
/* The traditional behaviour is to fall back to N_TTY, we
536+
want to avoid falling back to N_NULL unless we have no
537+
choice to avoid the risk of breaking anything */
538+
if (tty_ldisc_failto(tty, N_TTY) < 0 &&
539+
tty_ldisc_failto(tty, N_NULL) < 0)
540+
panic("Couldn't open N_NULL ldisc for %s.",
541+
tty_name(tty));
526542
}
527543
}
528544

include/uapi/linux/tty.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,6 @@
3636
#define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */
3737
#define N_NCI 25 /* NFC NCI UART */
3838
#define N_SPEAKUP 26 /* Speakup communication with synths */
39+
#define N_NULL 27 /* Null ldisc used for error handling */
3940

4041
#endif /* _UAPI_LINUX_TTY_H */

0 commit comments

Comments
 (0)