[Acked/cmt] [Trusty][pull request] (upstream) n_tty: Fix buffer overruns with larger-than-4k pastes

Andy Whitcroft apw at canonical.com
Fri Jul 25 09:54:46 UTC 2014


On Thu, Jul 24, 2014 at 05:43:29PM -0300, Rafael David Tinoco wrote:
> From 0b8b060c8932c600a240fb1fa055fa103b5e969a Mon Sep 17 00:00:00 2001
> From: Peter Hurley <peter at hurleysoftware.com>
> Date: Tue, 10 Dec 2013 17:12:02 -0500
> Subject: n_tty: Fix buffer overruns  with larger-than-4k pastes
> 
> BugLink: http://bugs.launchpad.net/bugs/1208740
> 
> 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 at users.sourceforge.net>
> 
> OriginalAuthor: Peter Hurley <peter at hurleysoftware.com>
> Reported-by: Margarita Manterola <margamanterola at gmail.com>
> Acked-by: Stas Sergeev <stsp at users.sourceforge.net>
> Signed-off-by: Peter Hurley <peter at hurleysoftware.com>
> (cherry-picked from commit 4d0ed18277cc6f07513ee0b04475f19cd69e75ef v3.14-rc1)
> Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
> Signed-off-by: Rafael David Tinoco <rafael.tinoco at canonical.com>
> ---
>  drivers/tty/n_tty.c | 26 +++++++++++++++++++++++---
>  1 file changed, 23 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
> index 86093e2..f591184 100644
> --- a/drivers/tty/n_tty.c
> +++ b/drivers/tty/n_tty.c
> @@ -105,6 +105,7 @@ struct n_tty_data {
>  
>  	/* must hold exclusive termios_rwsem to reset these */
>  	unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
> +	unsigned char push:1;
>  
>  	/* shared by producer and consumer */
>  	char read_buf[N_TTY_BUF_SIZE];
> @@ -342,6 +343,7 @@ static void reset_buffer_flags(struct n_tty_data *ldata)
>  
>  	ldata->erasing = 0;
>  	bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
> +	ldata->push = 0;
>  }
>  
>  static void n_tty_packet_mode_flush(struct tty_struct *tty)
> @@ -1762,7 +1764,16 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
>  
>  	if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) {
>  		bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
> -		ldata->line_start = ldata->canon_head = ldata->read_tail;
> +		ldata->line_start = ldata->read_tail;
> +		if (!L_ICANON(tty) || !read_cnt(ldata)) {
> +			ldata->canon_head = ldata->read_tail;
> +			ldata->push = 0;
> +		} else {
> +			set_bit((ldata->read_head - 1) & (N_TTY_BUF_SIZE - 1),
> +				ldata->read_flags);
> +			ldata->canon_head = ldata->read_head;
> +			ldata->push = 1;
> +		}
>  		ldata->erasing = 0;
>  		ldata->lnext = 0;
>  	}
> @@ -1967,6 +1978,12 @@ static int copy_from_read_buf(struct tty_struct *tty,
>   *	it copies one line of input up to and including the line-delimiting
>   *	character into the user-space buffer.
>   *
> + *	NB: When termios is changed from non-canonical to canonical mode and
> + *	the read buffer contains data, n_tty_set_termios() simulates an EOF
> + *	push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer.
> + *	This causes data already processed as input to be immediately available
> + *	as input although a newline has not been received.
> + *
>   *	Called under the atomic_read_lock mutex
>   *
>   *	n_tty_read()/consumer path:
> @@ -2013,7 +2030,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
>  	n += found;
>  	c = n;
>  
> -	if (found && read_buf(ldata, eol) == __DISABLED_CHAR) {
> +	if (found && !ldata->push && read_buf(ldata, eol) == __DISABLED_CHAR) {
>  		n--;
>  		eof_push = !n && ldata->read_tail != ldata->line_start;
>  	}
> @@ -2040,7 +2057,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
>  	ldata->read_tail += c;
>  
>  	if (found) {
> -		ldata->line_start = ldata->read_tail;
> +		if (!ldata->push)
> +			ldata->line_start = ldata->read_tail;
> +		else
> +			ldata->push = 0;
>  		tty_audit_push(tty);
>  	}
>  	return eof_push ? -EAGAIN : 0;
> -- 

Looks reasonable, I would like Chris Arges to look over this as he was
involved in the last batch of tty fixes for "pasting wholy unreasonable
amounts of data does not work" and we have some local fixes applied at
least in Saucy for this.  But assming he is happy:

Acked-by: Andy Whitcroft <apw at canonical.com>

-apw




More information about the kernel-team mailing list