1 From 28b0f8a6962a24ed21737578f3b1b07424635c9e Mon Sep 17 00:00:00 2001
2 From: Tejun Heo <tj@kernel.org>
3 Date: Tue, 13 Feb 2018 07:38:08 -0800
4 Subject: tty: make n_tty_read() always abort if hangup is in progress
6 From: Tejun Heo <tj@kernel.org>
8 commit 28b0f8a6962a24ed21737578f3b1b07424635c9e upstream.
10 A tty is hung up by __tty_hangup() setting file->f_op to
11 hung_up_tty_fops, which is skipped on ttys whose write operation isn't
12 tty_write(). This means that, for example, /dev/console whose write
13 op is redirected_tty_write() is never actually marked hung up.
15 Because n_tty_read() uses the hung up status to decide whether to
16 abort the waiting readers, the lack of hung-up marking can lead to the
19 1. A session contains two processes. The leader and its child. The
22 2. The leader exits and starts disassociating from the controlling
23 terminal (/dev/console).
25 3. __tty_hangup() skips setting f_op to hung_up_tty_fops.
27 4. SIGHUP is delivered and ignored.
29 5. tty_ldisc_hangup() is invoked. It wakes up the waits which should
30 clear the read lockers of tty->ldisc_sem.
32 6. The reader wakes up but because tty_hung_up_p() is false, it
33 doesn't abort and goes back to sleep while read-holding
36 7. The leader progresses to tty_ldisc_lock() in tty_ldisc_hangup()
37 and is now stuck in D sleep indefinitely waiting for
40 The following is Alan's explanation on why some ttys aren't hung up.
42 http://lkml.kernel.org/r/20171101170908.6ad08580@alans-desktop
44 1. It broke the serial consoles because they would hang up and close
45 down the hardware. With tty_port that *should* be fixable properly
46 for any cases remaining.
48 2. The console layer was (and still is) completely broken and doens't
49 refcount properly. So if you turn on console hangups it breaks (as
50 indeed does freeing consoles and half a dozen other things).
52 As neither can be fixed quickly, this patch works around the problem
53 by introducing a new flag, TTY_HUPPING, which is used solely to tell
54 n_tty_read() that hang-up is in progress for the console and the
55 readers should be aborted regardless of the hung-up status of the
58 The following is a sample hung task warning caused by this issue.
60 INFO: task agetty:2662 blocked for more than 120 seconds.
61 Not tainted 4.11.3-dbg-tty-lockup-02478-gfd6c7ee-dirty #28
62 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
65 __schedule+0x267/0x890
67 schedule_timeout+0x23c/0x2e0
68 ldsem_down_write+0xce/0x1f6
69 tty_ldisc_lock+0x16/0x30
70 tty_ldisc_hangup+0xb3/0x1b0
71 __tty_hangup+0x300/0x410
72 disassociate_ctty+0x6c/0x290
74 do_group_exit+0x3f/0xa0
75 get_signal+0x1b3/0x5d0
77 exit_to_usermode_loop+0x46/0x86
78 do_syscall_64+0x9c/0xb0
79 entry_SYSCALL64_slow_path+0x25/0x25
81 The following is the repro. Run "$PROG /dev/console". The parent
82 process hangs in D state.
84 #include <sys/types.h>
87 #include <sys/ioctl.h>
97 int main(int argc, char **argv)
99 struct sigaction sact = { .sa_handler = SIG_IGN };
100 struct timespec ts1s = { .tv_sec = 1 };
105 fprintf(stderr, "test-hung-tty /dev/$TTY\n");
109 /* fork a child to ensure that it isn't already the session leader */
117 /* top parent, wait for everyone */
118 while (waitpid(-1, NULL, 0) >= 0)
125 /* new session, start a new session and set the controlling tty */
131 fd = open(argv[1], O_RDWR);
137 if (ioctl(fd, TIOCSCTTY, 1) < 0) {
142 /* fork a child, sleep a bit and exit */
150 nanosleep(&ts1s, NULL);
151 printf("Session leader exiting\n");
156 * The child ignores SIGHUP and keeps reading from the controlling
157 * tty. Because SIGHUP is ignored, the child doesn't get killed on
158 * parent exit and the bug in n_tty makes the read(2) block the
159 * parent's control terminal hangup attempt. The parent ends up in
160 * D sleep until the child is explicitly killed.
162 sigaction(SIGHUP, &sact, NULL);
163 printf("Child reading tty\n");
167 if (read(fd, buf, sizeof(buf)) < 0) {
176 Signed-off-by: Tejun Heo <tj@kernel.org>
177 Cc: Alan Cox <alan@llwyncelyn.cymru>
178 Cc: stable@vger.kernel.org
179 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
182 drivers/tty/n_tty.c | 6 ++++++
183 drivers/tty/tty_io.c | 9 +++++++++
184 include/linux/tty.h | 1 +
185 3 files changed, 16 insertions(+)
187 --- a/drivers/tty/n_tty.c
188 +++ b/drivers/tty/n_tty.c
189 @@ -2238,6 +2238,12 @@ static ssize_t n_tty_read(struct tty_str
191 if (tty_hung_up_p(file))
194 + * Abort readers for ttys which never actually
195 + * get hung up. See __tty_hangup().
197 + if (test_bit(TTY_HUPPING, &tty->flags))
201 if (file->f_flags & O_NONBLOCK) {
202 --- a/drivers/tty/tty_io.c
203 +++ b/drivers/tty/tty_io.c
204 @@ -702,6 +702,14 @@ static void __tty_hangup(struct tty_stru
209 + * Some console devices aren't actually hung up for technical and
210 + * historical reasons, which can lead to indefinite interruptible
211 + * sleep in n_tty_read(). The following explicitly tells
212 + * n_tty_read() to abort readers.
214 + set_bit(TTY_HUPPING, &tty->flags);
216 /* inuse_filps is protected by the single tty lock,
217 this really needs to change if we want to flush the
218 workqueue with the lock held */
219 @@ -757,6 +765,7 @@ static void __tty_hangup(struct tty_stru
220 * can't yet guarantee all that.
222 set_bit(TTY_HUPPED, &tty->flags);
223 + clear_bit(TTY_HUPPING, &tty->flags);
227 --- a/include/linux/tty.h
228 +++ b/include/linux/tty.h
229 @@ -342,6 +342,7 @@ struct tty_file_private {
230 #define TTY_PTY_LOCK 16 /* pty private */
231 #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */
232 #define TTY_HUPPED 18 /* Post driver->hangup() */
233 +#define TTY_HUPPING 19 /* Hangup in progress */
234 #define TTY_LDISC_HALTED 22 /* Line discipline is halted */
236 #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))