aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/n_tty.c
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2013-12-10 17:12:02 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-12-17 12:29:01 -0500
commit4d0ed18277cc6f07513ee0b04475f19cd69e75ef (patch)
tree53f692c75eb90a50d4106bb3361a1833870817d0 /drivers/tty/n_tty.c
parent3c0f0f9f7d9b558089ce6701f72a932b63192384 (diff)
n_tty: Fix buffer overruns with larger-than-4k pastes
readline() inadvertently triggers an error recovery path when pastes larger than 4k overrun the line discipline buffer. The error recovery path discards input when the line discipline buffer is full and operating in canonical mode and no newline has been received. Because readline() changes the termios to non-canonical mode to read the line char-by-char, the line discipline buffer can become full, and then when readline() restores termios back to canonical mode for the caller, the now-full line discipline buffer triggers the error recovery. When changing termios from non-canon to canon mode and the read buffer contains data, simulate an EOF push _without_ the DISABLED_CHAR in the read buffer. Importantly for the readline() problem, the termios can be changed back to non-canonical mode without changes to the read buffer occurring; ie., as if the previous termios change had not happened (as long as no intervening read took place). Preserve existing userspace behavior which allows '\0's already received in non-canon mode to be read as '\0's in canon mode (rather than trigger add'l EOF pushes or an actual EOF). Patch based on original proposal and discussion here https://bugzilla.kernel.org/show_bug.cgi?id=55991 by Stas Sergeev <stsp@users.sourceforge.net> Reported-by: Margarita Manterola <margamanterola@gmail.com> Cc: Maximiliano Curia <maxy@gnuservers.com.ar> Cc: Pavel Machek <pavel@ucw.cz> Cc: Arkadiusz Miskiewicz <a.miskiewicz@gmail.com> Acked-by: Stas Sergeev <stsp@users.sourceforge.net> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/n_tty.c')
-rw-r--r--drivers/tty/n_tty.c26
1 files changed, 23 insertions, 3 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index fdc2ecde5ac2..961e6a996dff 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -104,6 +104,7 @@ struct n_tty_data {
104 104
105 /* must hold exclusive termios_rwsem to reset these */ 105 /* must hold exclusive termios_rwsem to reset these */
106 unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; 106 unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
107 unsigned char push:1;
107 108
108 /* shared by producer and consumer */ 109 /* shared by producer and consumer */
109 char read_buf[N_TTY_BUF_SIZE]; 110 char read_buf[N_TTY_BUF_SIZE];
@@ -341,6 +342,7 @@ static void reset_buffer_flags(struct n_tty_data *ldata)
341 342
342 ldata->erasing = 0; 343 ldata->erasing = 0;
343 bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); 344 bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
345 ldata->push = 0;
344} 346}
345 347
346static void n_tty_packet_mode_flush(struct tty_struct *tty) 348static void n_tty_packet_mode_flush(struct tty_struct *tty)
@@ -1745,7 +1747,16 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
1745 1747
1746 if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) { 1748 if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) {
1747 bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); 1749 bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
1748 ldata->line_start = ldata->canon_head = ldata->read_tail; 1750 ldata->line_start = ldata->read_tail;
1751 if (!L_ICANON(tty) || !read_cnt(ldata)) {
1752 ldata->canon_head = ldata->read_tail;
1753 ldata->push = 0;
1754 } else {
1755 set_bit((ldata->read_head - 1) & (N_TTY_BUF_SIZE - 1),
1756 ldata->read_flags);
1757 ldata->canon_head = ldata->read_head;
1758 ldata->push = 1;
1759 }
1749 ldata->erasing = 0; 1760 ldata->erasing = 0;
1750 ldata->lnext = 0; 1761 ldata->lnext = 0;
1751 } 1762 }
@@ -1951,6 +1962,12 @@ static int copy_from_read_buf(struct tty_struct *tty,
1951 * it copies one line of input up to and including the line-delimiting 1962 * it copies one line of input up to and including the line-delimiting
1952 * character into the user-space buffer. 1963 * character into the user-space buffer.
1953 * 1964 *
1965 * NB: When termios is changed from non-canonical to canonical mode and
1966 * the read buffer contains data, n_tty_set_termios() simulates an EOF
1967 * push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer.
1968 * This causes data already processed as input to be immediately available
1969 * as input although a newline has not been received.
1970 *
1954 * Called under the atomic_read_lock mutex 1971 * Called under the atomic_read_lock mutex
1955 * 1972 *
1956 * n_tty_read()/consumer path: 1973 * n_tty_read()/consumer path:
@@ -1997,7 +2014,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
1997 n += found; 2014 n += found;
1998 c = n; 2015 c = n;
1999 2016
2000 if (found && read_buf(ldata, eol) == __DISABLED_CHAR) { 2017 if (found && !ldata->push && read_buf(ldata, eol) == __DISABLED_CHAR) {
2001 n--; 2018 n--;
2002 eof_push = !n && ldata->read_tail != ldata->line_start; 2019 eof_push = !n && ldata->read_tail != ldata->line_start;
2003 } 2020 }
@@ -2024,7 +2041,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
2024 ldata->read_tail += c; 2041 ldata->read_tail += c;
2025 2042
2026 if (found) { 2043 if (found) {
2027 ldata->line_start = ldata->read_tail; 2044 if (!ldata->push)
2045 ldata->line_start = ldata->read_tail;
2046 else
2047 ldata->push = 0;
2028 tty_audit_push(tty); 2048 tty_audit_push(tty);
2029 } 2049 }
2030 return eof_push ? -EAGAIN : 0; 2050 return eof_push ? -EAGAIN : 0;