Skip to content

Commit

Permalink
n_tty: Fix EOF push handling
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Peter Hurley authored and Greg Kroah-Hartman committed Jul 24, 2013
1 parent 9dfd16d commit 40d5e09
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 19 deletions.
34 changes: 17 additions & 17 deletions drivers/tty/n_tty.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ struct n_tty_data {

/* consumer-published */
size_t read_tail;
size_t line_start;

/* protected by output lock */
unsigned int column;
Expand Down Expand Up @@ -337,6 +338,7 @@ static void reset_buffer_flags(struct n_tty_data *ldata)
{
ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
ldata->line_start = 0;

ldata->erasing = 0;
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
Expand Down Expand Up @@ -1396,8 +1398,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
if (c == EOF_CHAR(tty)) {
if (read_cnt(ldata) >= N_TTY_BUF_SIZE)
return;
if (ldata->canon_head != ldata->read_head)
set_bit(TTY_PUSH, &tty->flags);
c = __DISABLED_CHAR;
goto handle_newline;
}
Expand Down Expand Up @@ -1604,6 +1604,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;
if (canon_change) {
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
ldata->line_start = 0;
ldata->canon_head = ldata->read_tail;
ldata->erasing = 0;
ldata->lnext = 0;
Expand Down Expand Up @@ -1837,6 +1838,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
size_t eol;
size_t tail;
int ret, found = 0;
bool eof_push = 0;

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

if (found && read_buf(ldata, eol) == __DISABLED_CHAR)
if (found && read_buf(ldata, eol) == __DISABLED_CHAR) {
n--;
eof_push = !n && ldata->read_tail != ldata->line_start;
}

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

if (found)
if (found) {
ldata->line_start = ldata->read_tail;
tty_audit_push(tty);
return 0;
}
return eof_push ? -EAGAIN : 0;
}

extern ssize_t redirected_tty_write(struct file *, const char __user *,
Expand Down Expand Up @@ -1964,12 +1970,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
int c;
int minimum, time;
ssize_t retval = 0;
ssize_t size;
long timeout;
unsigned long flags;
int packet;

do_it_again:
c = job_control(tty, file);
if (c < 0)
return c;
Expand Down Expand Up @@ -2076,7 +2080,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,

if (ldata->icanon && !L_EXTPROC(tty)) {
retval = canon_copy_from_read_buf(tty, &b, &nr);
if (retval)
if (retval == -EAGAIN) {
retval = 0;
continue;
} else if (retval)
break;
} else {
int uncopied;
Expand Down Expand Up @@ -2104,15 +2111,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
ldata->minimum_to_wake = minimum;

__set_current_state(TASK_RUNNING);
size = b - buf;
if (size) {
retval = size;
if (nr)
clear_bit(TTY_PUSH, &tty->flags);
} else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) {
up_read(&tty->termios_rwsem);
goto do_it_again;
}
if (b - buf)
retval = b - buf;

n_tty_set_room(tty);
up_read(&tty->termios_rwsem);
Expand Down
1 change: 0 additions & 1 deletion drivers/tty/tty_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)

spin_lock_irq(&tty->ctrl_lock);
clear_bit(TTY_THROTTLED, &tty->flags);
clear_bit(TTY_PUSH, &tty->flags);
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
put_pid(tty->session);
put_pid(tty->pgrp);
Expand Down
1 change: 0 additions & 1 deletion include/linux/tty.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,6 @@ struct tty_file_private {
#define TTY_EXCLUSIVE 3 /* Exclusive open mode */
#define TTY_DEBUG 4 /* Debugging */
#define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */
#define TTY_PUSH 6 /* n_tty private */
#define TTY_CLOSING 7 /* ->close() in progress */
#define TTY_LDISC_OPEN 11 /* Line discipline is open */
#define TTY_PTY_LOCK 16 /* pty private */
Expand Down

0 comments on commit 40d5e09

Please sign in to comment.