Skip to content

Commit 40d5e09

Browse files
peterhurleygregkh
authored andcommitted
n_tty: Fix EOF push handling
In canonical mode, an EOF which is not the first character of the line causes read() to complete and return the number of characters read so far (commonly referred to as EOF push). However, if the previous read() returned because the user buffer was full _and_ the next character is an EOF not at the beginning of the line, read() must not return 0, thus mistakenly indicating the end-of-file condition. The TTY_PUSH flag is used to indicate an EOF was received which is not at the beginning of the line. Because the EOF push condition is evaluated by a thread other than the read(), multiple EOF pushes can cause a premature end-of-file to be indicated. Instead, discover the 'EOF push as first read character' condition from the read() thread itself, and restart the i/o loop if detected. Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 9dfd16d commit 40d5e09

File tree

3 files changed

+17
-19
lines changed

3 files changed

+17
-19
lines changed

drivers/tty/n_tty.c

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ struct n_tty_data {
113113

114114
/* consumer-published */
115115
size_t read_tail;
116+
size_t line_start;
116117

117118
/* protected by output lock */
118119
unsigned int column;
@@ -337,6 +338,7 @@ static void reset_buffer_flags(struct n_tty_data *ldata)
337338
{
338339
ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
339340
ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
341+
ldata->line_start = 0;
340342

341343
ldata->erasing = 0;
342344
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
@@ -1396,8 +1398,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
13961398
if (c == EOF_CHAR(tty)) {
13971399
if (read_cnt(ldata) >= N_TTY_BUF_SIZE)
13981400
return;
1399-
if (ldata->canon_head != ldata->read_head)
1400-
set_bit(TTY_PUSH, &tty->flags);
14011401
c = __DISABLED_CHAR;
14021402
goto handle_newline;
14031403
}
@@ -1604,6 +1604,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
16041604
canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;
16051605
if (canon_change) {
16061606
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
1607+
ldata->line_start = 0;
16071608
ldata->canon_head = ldata->read_tail;
16081609
ldata->erasing = 0;
16091610
ldata->lnext = 0;
@@ -1837,6 +1838,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
18371838
size_t eol;
18381839
size_t tail;
18391840
int ret, found = 0;
1841+
bool eof_push = 0;
18401842

18411843
/* N.B. avoid overrun if nr == 0 */
18421844
n = min(*nr, read_cnt(ldata));
@@ -1863,8 +1865,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
18631865
n = (found + eol + size) & (N_TTY_BUF_SIZE - 1);
18641866
c = n;
18651867

1866-
if (found && read_buf(ldata, eol) == __DISABLED_CHAR)
1868+
if (found && read_buf(ldata, eol) == __DISABLED_CHAR) {
18671869
n--;
1870+
eof_push = !n && ldata->read_tail != ldata->line_start;
1871+
}
18681872

18691873
n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n",
18701874
__func__, eol, found, n, c, size, more);
@@ -1887,9 +1891,11 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
18871891
smp_mb__after_clear_bit();
18881892
ldata->read_tail += c;
18891893

1890-
if (found)
1894+
if (found) {
1895+
ldata->line_start = ldata->read_tail;
18911896
tty_audit_push(tty);
1892-
return 0;
1897+
}
1898+
return eof_push ? -EAGAIN : 0;
18931899
}
18941900

18951901
extern ssize_t redirected_tty_write(struct file *, const char __user *,
@@ -1964,12 +1970,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
19641970
int c;
19651971
int minimum, time;
19661972
ssize_t retval = 0;
1967-
ssize_t size;
19681973
long timeout;
19691974
unsigned long flags;
19701975
int packet;
19711976

1972-
do_it_again:
19731977
c = job_control(tty, file);
19741978
if (c < 0)
19751979
return c;
@@ -2076,7 +2080,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
20762080

20772081
if (ldata->icanon && !L_EXTPROC(tty)) {
20782082
retval = canon_copy_from_read_buf(tty, &b, &nr);
2079-
if (retval)
2083+
if (retval == -EAGAIN) {
2084+
retval = 0;
2085+
continue;
2086+
} else if (retval)
20802087
break;
20812088
} else {
20822089
int uncopied;
@@ -2104,15 +2111,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
21042111
ldata->minimum_to_wake = minimum;
21052112

21062113
__set_current_state(TASK_RUNNING);
2107-
size = b - buf;
2108-
if (size) {
2109-
retval = size;
2110-
if (nr)
2111-
clear_bit(TTY_PUSH, &tty->flags);
2112-
} else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) {
2113-
up_read(&tty->termios_rwsem);
2114-
goto do_it_again;
2115-
}
2114+
if (b - buf)
2115+
retval = b - buf;
21162116

21172117
n_tty_set_room(tty);
21182118
up_read(&tty->termios_rwsem);

drivers/tty/tty_io.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
664664

665665
spin_lock_irq(&tty->ctrl_lock);
666666
clear_bit(TTY_THROTTLED, &tty->flags);
667-
clear_bit(TTY_PUSH, &tty->flags);
668667
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
669668
put_pid(tty->session);
670669
put_pid(tty->pgrp);

include/linux/tty.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,6 @@ struct tty_file_private {
304304
#define TTY_EXCLUSIVE 3 /* Exclusive open mode */
305305
#define TTY_DEBUG 4 /* Debugging */
306306
#define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */
307-
#define TTY_PUSH 6 /* n_tty private */
308307
#define TTY_CLOSING 7 /* ->close() in progress */
309308
#define TTY_LDISC_OPEN 11 /* Line discipline is open */
310309
#define TTY_PTY_LOCK 16 /* pty private */

0 commit comments

Comments
 (0)