]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/4.4.129/tty-make-n_tty_read-always-abort-if-hangup-is-in-progress.patch
3.18-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 4.4.129 / tty-make-n_tty_read-always-abort-if-hangup-is-in-progress.patch
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
5
6 From: Tejun Heo <tj@kernel.org>
7
8 commit 28b0f8a6962a24ed21737578f3b1b07424635c9e upstream.
9
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.
14
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
17 following scenario.
18
19 1. A session contains two processes. The leader and its child. The
20 child ignores SIGHUP.
21
22 2. The leader exits and starts disassociating from the controlling
23 terminal (/dev/console).
24
25 3. __tty_hangup() skips setting f_op to hung_up_tty_fops.
26
27 4. SIGHUP is delivered and ignored.
28
29 5. tty_ldisc_hangup() is invoked. It wakes up the waits which should
30 clear the read lockers of tty->ldisc_sem.
31
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
34 tty->ldisc_sem.
35
36 7. The leader progresses to tty_ldisc_lock() in tty_ldisc_hangup()
37 and is now stuck in D sleep indefinitely waiting for
38 tty->ldisc_sem.
39
40 The following is Alan's explanation on why some ttys aren't hung up.
41
42 http://lkml.kernel.org/r/20171101170908.6ad08580@alans-desktop
43
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.
47
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).
51
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
56 device.
57
58 The following is a sample hung task warning caused by this issue.
59
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.
63 0 2662 1 0x00000086
64 Call Trace:
65 __schedule+0x267/0x890
66 schedule+0x36/0x80
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
73 do_exit+0x7ef/0xb00
74 do_group_exit+0x3f/0xa0
75 get_signal+0x1b3/0x5d0
76 do_signal+0x28/0x660
77 exit_to_usermode_loop+0x46/0x86
78 do_syscall_64+0x9c/0xb0
79 entry_SYSCALL64_slow_path+0x25/0x25
80
81 The following is the repro. Run "$PROG /dev/console". The parent
82 process hangs in D state.
83
84 #include <sys/types.h>
85 #include <sys/stat.h>
86 #include <sys/wait.h>
87 #include <sys/ioctl.h>
88 #include <fcntl.h>
89 #include <unistd.h>
90 #include <stdio.h>
91 #include <stdlib.h>
92 #include <errno.h>
93 #include <signal.h>
94 #include <time.h>
95 #include <termios.h>
96
97 int main(int argc, char **argv)
98 {
99 struct sigaction sact = { .sa_handler = SIG_IGN };
100 struct timespec ts1s = { .tv_sec = 1 };
101 pid_t pid;
102 int fd;
103
104 if (argc < 2) {
105 fprintf(stderr, "test-hung-tty /dev/$TTY\n");
106 return 1;
107 }
108
109 /* fork a child to ensure that it isn't already the session leader */
110 pid = fork();
111 if (pid < 0) {
112 perror("fork");
113 return 1;
114 }
115
116 if (pid > 0) {
117 /* top parent, wait for everyone */
118 while (waitpid(-1, NULL, 0) >= 0)
119 ;
120 if (errno != ECHILD)
121 perror("waitpid");
122 return 0;
123 }
124
125 /* new session, start a new session and set the controlling tty */
126 if (setsid() < 0) {
127 perror("setsid");
128 return 1;
129 }
130
131 fd = open(argv[1], O_RDWR);
132 if (fd < 0) {
133 perror("open");
134 return 1;
135 }
136
137 if (ioctl(fd, TIOCSCTTY, 1) < 0) {
138 perror("ioctl");
139 return 1;
140 }
141
142 /* fork a child, sleep a bit and exit */
143 pid = fork();
144 if (pid < 0) {
145 perror("fork");
146 return 1;
147 }
148
149 if (pid > 0) {
150 nanosleep(&ts1s, NULL);
151 printf("Session leader exiting\n");
152 exit(0);
153 }
154
155 /*
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.
161 */
162 sigaction(SIGHUP, &sact, NULL);
163 printf("Child reading tty\n");
164 while (1) {
165 char buf[1024];
166
167 if (read(fd, buf, sizeof(buf)) < 0) {
168 perror("read");
169 return 1;
170 }
171 }
172
173 return 0;
174 }
175
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>
180
181 ---
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(+)
186
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
190 }
191 if (tty_hung_up_p(file))
192 break;
193 + /*
194 + * Abort readers for ttys which never actually
195 + * get hung up. See __tty_hangup().
196 + */
197 + if (test_bit(TTY_HUPPING, &tty->flags))
198 + break;
199 if (!timeout)
200 break;
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
205 return;
206 }
207
208 + /*
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.
213 + */
214 + set_bit(TTY_HUPPING, &tty->flags);
215 +
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.
221 */
222 set_bit(TTY_HUPPED, &tty->flags);
223 + clear_bit(TTY_HUPPING, &tty->flags);
224 tty_unlock(tty);
225
226 if (f)
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 */
235
236 #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))