aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2015-04-13 13:24:34 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-05-10 13:26:37 -0400
commit1a48632ffed61352a7810ce089dc5a8bcd505a60 (patch)
tree7b0e4c2879f2becc07c09d289f4f65b32114854a
parent8f9cfeed3eae86c70d3b04445a6f2036b27b6304 (diff)
pty: Fix input race when closing
A read() from a pty master may mistakenly indicate EOF (errno == -EIO) after the pty slave has closed, even though input data remains to be read. For example, pty slave | input worker | pty master | | | | n_tty_read() pty_write() | | input avail? no add data | | sleep schedule worker --->| | . |---> flush_to_ldisc() | . pty_close() | fill read buffer | . wait for worker | wakeup reader --->| . | read buffer full? |---> input avail ? yes |<--- yes - exit worker | copy 4096 bytes to user TTY_OTHER_CLOSED <---| |<--- kick worker | | **** New read() before worker starts **** | | n_tty_read() | | input avail? no | | TTY_OTHER_CLOSED? yes | | return -EIO Several conditions are required to trigger this race: 1. the ldisc read buffer must become full so the input worker exits 2. the read() count parameter must be >= 4096 so the ldisc read buffer is empty 3. the subsequent read() occurs before the kicked worker has processed more input However, the underlying cause of the race is that data is pipelined, while tty state is not; ie., data already written by the pty slave end is not yet visible to the pty master end, but state changes by the pty slave end are visible to the pty master end immediately. Pipeline the TTY_OTHER_CLOSED state through input worker to the reader. 1. Introduce TTY_OTHER_DONE which is set by the input worker when TTY_OTHER_CLOSED is set and either the input buffers are flushed or input processing has completed. Readers/polls are woken when TTY_OTHER_DONE is set. 2. Reader/poll checks TTY_OTHER_DONE instead of TTY_OTHER_CLOSED. 3. A new input worker is started from pty_close() after setting TTY_OTHER_CLOSED, which ensures the TTY_OTHER_DONE state will be set if the last input worker is already finished (or just about to exit). Remove tty_flush_to_ldisc(); no in-tree callers. Fixes: 52bce7f8d4fc ("pty, n_tty: Simplify input processing on final close") Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=96311 BugLink: http://bugs.launchpad.net/bugs/1429756 Cc: <stable@vger.kernel.org> # 3.19+ Reported-by: Andy Whitcroft <apw@canonical.com> Reported-by: H.J. Lu <hjl.tools@gmail.com> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--Documentation/serial/tty.txt3
-rw-r--r--drivers/tty/n_hdlc.c4
-rw-r--r--drivers/tty/n_tty.c22
-rw-r--r--drivers/tty/pty.c5
-rw-r--r--drivers/tty/tty_buffer.c41
-rw-r--r--include/linux/tty.h2
6 files changed, 54 insertions, 23 deletions
diff --git a/Documentation/serial/tty.txt b/Documentation/serial/tty.txt
index 1e52d67d0abf..dbe6623fed1c 100644
--- a/Documentation/serial/tty.txt
+++ b/Documentation/serial/tty.txt
@@ -198,6 +198,9 @@ TTY_IO_ERROR If set, causes all subsequent userspace read/write
198 198
199TTY_OTHER_CLOSED Device is a pty and the other side has closed. 199TTY_OTHER_CLOSED Device is a pty and the other side has closed.
200 200
201TTY_OTHER_DONE Device is a pty and the other side has closed and
202 all pending input processing has been completed.
203
201TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into 204TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into
202 smaller chunks. 205 smaller chunks.
203 206
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c
index 644ddb841d9f..bbc4ce66c2c1 100644
--- a/drivers/tty/n_hdlc.c
+++ b/drivers/tty/n_hdlc.c
@@ -600,7 +600,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
600 add_wait_queue(&tty->read_wait, &wait); 600 add_wait_queue(&tty->read_wait, &wait);
601 601
602 for (;;) { 602 for (;;) {
603 if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { 603 if (test_bit(TTY_OTHER_DONE, &tty->flags)) {
604 ret = -EIO; 604 ret = -EIO;
605 break; 605 break;
606 } 606 }
@@ -828,7 +828,7 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
828 /* set bits for operations that won't block */ 828 /* set bits for operations that won't block */
829 if (n_hdlc->rx_buf_list.head) 829 if (n_hdlc->rx_buf_list.head)
830 mask |= POLLIN | POLLRDNORM; /* readable */ 830 mask |= POLLIN | POLLRDNORM; /* readable */
831 if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) 831 if (test_bit(TTY_OTHER_DONE, &tty->flags))
832 mask |= POLLHUP; 832 mask |= POLLHUP;
833 if (tty_hung_up_p(filp)) 833 if (tty_hung_up_p(filp))
834 mask |= POLLHUP; 834 mask |= POLLHUP;
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index cf6e0f2e1331..cc57a3a6b02b 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1949,6 +1949,18 @@ static inline int input_available_p(struct tty_struct *tty, int poll)
1949 return ldata->commit_head - ldata->read_tail >= amt; 1949 return ldata->commit_head - ldata->read_tail >= amt;
1950} 1950}
1951 1951
1952static inline int check_other_done(struct tty_struct *tty)
1953{
1954 int done = test_bit(TTY_OTHER_DONE, &tty->flags);
1955 if (done) {
1956 /* paired with cmpxchg() in check_other_closed(); ensures
1957 * read buffer head index is not stale
1958 */
1959 smp_mb__after_atomic();
1960 }
1961 return done;
1962}
1963
1952/** 1964/**
1953 * copy_from_read_buf - copy read data directly 1965 * copy_from_read_buf - copy read data directly
1954 * @tty: terminal device 1966 * @tty: terminal device
@@ -2167,7 +2179,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
2167 struct n_tty_data *ldata = tty->disc_data; 2179 struct n_tty_data *ldata = tty->disc_data;
2168 unsigned char __user *b = buf; 2180 unsigned char __user *b = buf;
2169 DEFINE_WAIT_FUNC(wait, woken_wake_function); 2181 DEFINE_WAIT_FUNC(wait, woken_wake_function);
2170 int c; 2182 int c, done;
2171 int minimum, time; 2183 int minimum, time;
2172 ssize_t retval = 0; 2184 ssize_t retval = 0;
2173 long timeout; 2185 long timeout;
@@ -2235,8 +2247,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
2235 ((minimum - (b - buf)) >= 1)) 2247 ((minimum - (b - buf)) >= 1))
2236 ldata->minimum_to_wake = (minimum - (b - buf)); 2248 ldata->minimum_to_wake = (minimum - (b - buf));
2237 2249
2250 done = check_other_done(tty);
2251
2238 if (!input_available_p(tty, 0)) { 2252 if (!input_available_p(tty, 0)) {
2239 if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { 2253 if (done) {
2240 retval = -EIO; 2254 retval = -EIO;
2241 break; 2255 break;
2242 } 2256 }
@@ -2443,12 +2457,12 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
2443 2457
2444 poll_wait(file, &tty->read_wait, wait); 2458 poll_wait(file, &tty->read_wait, wait);
2445 poll_wait(file, &tty->write_wait, wait); 2459 poll_wait(file, &tty->write_wait, wait);
2460 if (check_other_done(tty))
2461 mask |= POLLHUP;
2446 if (input_available_p(tty, 1)) 2462 if (input_available_p(tty, 1))
2447 mask |= POLLIN | POLLRDNORM; 2463 mask |= POLLIN | POLLRDNORM;
2448 if (tty->packet && tty->link->ctrl_status) 2464 if (tty->packet && tty->link->ctrl_status)
2449 mask |= POLLPRI | POLLIN | POLLRDNORM; 2465 mask |= POLLPRI | POLLIN | POLLRDNORM;
2450 if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
2451 mask |= POLLHUP;
2452 if (tty_hung_up_p(file)) 2466 if (tty_hung_up_p(file))
2453 mask |= POLLHUP; 2467 mask |= POLLHUP;
2454 if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { 2468 if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) {
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index e72ee629cead..4d5e8409769c 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -53,9 +53,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
53 /* Review - krefs on tty_link ?? */ 53 /* Review - krefs on tty_link ?? */
54 if (!tty->link) 54 if (!tty->link)
55 return; 55 return;
56 tty_flush_to_ldisc(tty->link);
57 set_bit(TTY_OTHER_CLOSED, &tty->link->flags); 56 set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
58 wake_up_interruptible(&tty->link->read_wait); 57 tty_flip_buffer_push(tty->link->port);
59 wake_up_interruptible(&tty->link->write_wait); 58 wake_up_interruptible(&tty->link->write_wait);
60 if (tty->driver->subtype == PTY_TYPE_MASTER) { 59 if (tty->driver->subtype == PTY_TYPE_MASTER) {
61 set_bit(TTY_OTHER_CLOSED, &tty->flags); 60 set_bit(TTY_OTHER_CLOSED, &tty->flags);
@@ -243,7 +242,9 @@ static int pty_open(struct tty_struct *tty, struct file *filp)
243 goto out; 242 goto out;
244 243
245 clear_bit(TTY_IO_ERROR, &tty->flags); 244 clear_bit(TTY_IO_ERROR, &tty->flags);
245 /* TTY_OTHER_CLOSED must be cleared before TTY_OTHER_DONE */
246 clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); 246 clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
247 clear_bit(TTY_OTHER_DONE, &tty->link->flags);
247 set_bit(TTY_THROTTLED, &tty->flags); 248 set_bit(TTY_THROTTLED, &tty->flags);
248 return 0; 249 return 0;
249 250
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index 75661641f5fe..2f78b77f0f81 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -37,6 +37,28 @@
37 37
38#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) 38#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF)
39 39
40/*
41 * If all tty flip buffers have been processed by flush_to_ldisc() or
42 * dropped by tty_buffer_flush(), check if the linked pty has been closed.
43 * If so, wake the reader/poll to process
44 */
45static inline void check_other_closed(struct tty_struct *tty)
46{
47 unsigned long flags, old;
48
49 /* transition from TTY_OTHER_CLOSED => TTY_OTHER_DONE must be atomic */
50 for (flags = ACCESS_ONCE(tty->flags);
51 test_bit(TTY_OTHER_CLOSED, &flags);
52 ) {
53 old = flags;
54 __set_bit(TTY_OTHER_DONE, &flags);
55 flags = cmpxchg(&tty->flags, old, flags);
56 if (old == flags) {
57 wake_up_interruptible(&tty->read_wait);
58 break;
59 }
60 }
61}
40 62
41/** 63/**
42 * tty_buffer_lock_exclusive - gain exclusive access to buffer 64 * tty_buffer_lock_exclusive - gain exclusive access to buffer
@@ -229,6 +251,8 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
229 if (ld && ld->ops->flush_buffer) 251 if (ld && ld->ops->flush_buffer)
230 ld->ops->flush_buffer(tty); 252 ld->ops->flush_buffer(tty);
231 253
254 check_other_closed(tty);
255
232 atomic_dec(&buf->priority); 256 atomic_dec(&buf->priority);
233 mutex_unlock(&buf->lock); 257 mutex_unlock(&buf->lock);
234} 258}
@@ -471,8 +495,10 @@ static void flush_to_ldisc(struct work_struct *work)
471 smp_rmb(); 495 smp_rmb();
472 count = head->commit - head->read; 496 count = head->commit - head->read;
473 if (!count) { 497 if (!count) {
474 if (next == NULL) 498 if (next == NULL) {
499 check_other_closed(tty);
475 break; 500 break;
501 }
476 buf->head = next; 502 buf->head = next;
477 tty_buffer_free(port, head); 503 tty_buffer_free(port, head);
478 continue; 504 continue;
@@ -489,19 +515,6 @@ static void flush_to_ldisc(struct work_struct *work)
489} 515}
490 516
491/** 517/**
492 * tty_flush_to_ldisc
493 * @tty: tty to push
494 *
495 * Push the terminal flip buffers to the line discipline.
496 *
497 * Must not be called from IRQ context.
498 */
499void tty_flush_to_ldisc(struct tty_struct *tty)
500{
501 flush_work(&tty->port->buf.work);
502}
503
504/**
505 * tty_flip_buffer_push - terminal 518 * tty_flip_buffer_push - terminal
506 * @port: tty port to push 519 * @port: tty port to push
507 * 520 *
diff --git a/include/linux/tty.h b/include/linux/tty.h
index fe5623c9af71..d76631f615c2 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -339,6 +339,7 @@ struct tty_file_private {
339#define TTY_EXCLUSIVE 3 /* Exclusive open mode */ 339#define TTY_EXCLUSIVE 3 /* Exclusive open mode */
340#define TTY_DEBUG 4 /* Debugging */ 340#define TTY_DEBUG 4 /* Debugging */
341#define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */ 341#define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */
342#define TTY_OTHER_DONE 6 /* Closed pty has completed input processing */
342#define TTY_LDISC_OPEN 11 /* Line discipline is open */ 343#define TTY_LDISC_OPEN 11 /* Line discipline is open */
343#define TTY_PTY_LOCK 16 /* pty private */ 344#define TTY_PTY_LOCK 16 /* pty private */
344#define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ 345#define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */
@@ -462,7 +463,6 @@ extern int tty_hung_up_p(struct file *filp);
462extern void do_SAK(struct tty_struct *tty); 463extern void do_SAK(struct tty_struct *tty);
463extern void __do_SAK(struct tty_struct *tty); 464extern void __do_SAK(struct tty_struct *tty);
464extern void no_tty(void); 465extern void no_tty(void);
465extern void tty_flush_to_ldisc(struct tty_struct *tty);
466extern void tty_buffer_free_all(struct tty_port *port); 466extern void tty_buffer_free_all(struct tty_port *port);
467extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld); 467extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld);
468extern void tty_buffer_init(struct tty_port *port); 468extern void tty_buffer_init(struct tty_port *port);