linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Peter Hurley <peter@hurleysoftware.com>
To: Marc Aurele La France <tsi@tuyoix.net>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Jiri Slaby <jslaby@suse.com>,
	linux-kernel@vger.kernel.org
Cc: Volth <openssh@volth.com>, Damien Miller <djm@mindrot.org>
Subject: Re: n_tty: Check the other end of pty pair before returning EAGAIN on a read()
Date: Thu, 10 Dec 2015 06:59:34 -0800	[thread overview]
Message-ID: <56699356.8040802@hurleysoftware.com> (raw)
In-Reply-To: <alpine.LNX.2.00.1512091358290.9574@fanir.tuyoix.net>

On 12/09/2015 01:06 PM, Marc Aurele La France wrote:
> Greetings.
> 
> The following four commits are some of the changes that have been made
> to the tty layer since kernel version 3.11:
> 
> 1) f95499c3030fe1bfad57745f2db1959c5b43dca8
>     n_tty: Don't wait for buffer work in read() loop
> 
> 2) f8747d4a466ab2cafe56112c51b3379f9fdb7a12
>     tty: Fix pty master read() after slave closes
> 
> 3) 52bce7f8d4fc633c9a9d0646eef58ba6ae9a3b73
>     pty, n_tty: Simplify input processing on final close
> 
> 4) 1a48632ffed61352a7810ce089dc5a8bcd505a60
>     pty: Fix input race when closing
> 
> Commit "4)" corrected an issue whereby EIO could be prematurely
> returned on a read of one end of a master/slave pty pair after the
> other had been completely closed.  Yet, I would argue that EAGAIN
> should not be returned either when there actually is data to be
> returned.  This whether or not the other end has been completely
> closed.
> 
> Indeed, the previous code (before commit "1)") checked the other end
> of the pty pair for any data before returning EAGAIN.  This mimics the
> behaviour of other System-V variants (Solaris, AIX, etc.)
                                                      ^^^^
What other SysV systems were tested?

> in this
> regard and ensured that EAGAIN really did mean no data was available
> at the time of the call.
> 
> Portable OpenSSH, since release 4.6p1 in 2007, relies on this being
> the case and has been broken since commit "1)" introduced spurious
> EAGAIN returns (i.e. as of 3.12 kernels).  The scenario at hand is
> as follows.
> 
> After sshd has been SIGCHLD'ed about the shell's termination, it
> continues to read the master pty until an error occurs.  This error
> will be EIO if no process has the slave pty open.  Otherwise (for
> example when the shell spawned long-running processes in the
> background before terminating), that error is expected to be EAGAIN.
> sshd cannot continue to read until an EIO in all cases, because doing
> so causes the session to hang until all processes have closed the
> slave pty, which is not the desired behaviour.  Thus a spurious EAGAIN
> return causes sshd to lose data, whether or not the slave pty is
> completely closed.

Ah, the games userspace will be up to :)


> I've been using the following script to reproduce the problem.  It
> loops until the issue is detected.
> 
> 	#! /bin/bash
> 
> 	LOG=sshlog-`date "+%F.%T"`
> 
> 	touch ${LOG}
> 
> 	while test -z "`grep -n '^Connection' ${LOG} | grep -v '0:Connection'`"
> 	do
> 	 ssh -p 22 -tt root@localhost \
> 	  '/bin/bash -c "/bin/ping -c4 8.8.8.8"' 2>&1 | \
> 	  tee -a ${LOG}
> 	done
> 
> It should be noted that the problem is extremely rare, but still
> occurs, on real hardware.  This bug is easier to replicate in a
> virtual machine such as those that can be created using Google Cloud.
> 
> The patch below is a suggested fix.  It was developed using a 4.3.0
> kernel and should apply, modulo fuzz, to any release >= 4.0.5.  My
> suggested fix is modeled after commit "2)" mentionned above.  Given
> commit "2)" was later reworked by commit "3)", I fully expect my fix
> to be reworked as well.
> 
> I volunteer to backport the fix this ends up being to any stable
> release >= 3.12 deemed needed.
> 
> Please Reply-To-All.
> 
> Thanks and have a great day.
> 
> Marc.
> 
> Reported-by: Volth <openssh@volth.com>
> BugLink: https://bugzilla.mindrot.org/show_bug.cgi?id=52
> BugLink: https://bugzilla.mindrot.org/show_bug.cgi?id=2492
> Signed-off-by: Marc Aurele La France <tsi@tuyoix.net>
> 
> --- a/drivers/tty/n_tty.c
> +++ a/drivers/tty/n_tty.c
> @@ -2281,20 +2281,26 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
>  			if (!timeout)
>  				break;
>  			if (file->f_flags & O_NONBLOCK) {
> -				retval = -EAGAIN;
> -				break;
> -			}
> -			if (signal_pending(current)) {
> -				retval = -ERESTARTSYS;
> -				break;
> -			}
> -			up_read(&tty->termios_rwsem);
> +				up_read(&tty->termios_rwsem);
> +				flush_work(&tty->port->buf.work);
> +				down_read(&tty->termios_rwsem);
> +				if (!input_available_p(tty, 0)) {
> +					retval = -EAGAIN;
> +					break;
> +				}
> +			} else {
> +				if (signal_pending(current)) {
> +					retval = -ERESTARTSYS;
> +					break;
> +				}
> +				up_read(&tty->termios_rwsem);

No sense in doing this just for O_NONBLOCK; might as well do it before
all the condition tests.

Which renders the earlier fixes for the slave end closing superfluous,
so might as well rip those out.

n_tty_poll() will need to be fixed as well, because if one application
used read() with O_NONBLOCK to expect to block until i/o became available,
then I guarantee some other application uses poll() with no timeout
for the same purpose.

Regards,
Peter Hurley

> 
> -			timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
> -					     timeout);
> +				timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
> +						     timeout);
> 
> -			down_read(&tty->termios_rwsem);
> -			continue;
> +				down_read(&tty->termios_rwsem);
> +				continue;
> +			}
>  		}
> 
>  		if (ldata->icanon && !L_EXTPROC(tty)) {
> 


  reply	other threads:[~2015-12-10 14:59 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-12-09 21:06 n_tty: Check the other end of pty pair before returning EAGAIN on a read() Marc Aurele La France
2015-12-10 14:59 ` Peter Hurley [this message]
2015-12-10 22:48   ` Marc Aurele La France
2015-12-11  0:07     ` Peter Hurley
2015-12-11 13:37       ` Marc Aurele La France
2015-12-11 13:56         ` Peter Hurley
2015-12-18 14:26           ` Marc Aurele La France
2015-12-18 16:39             ` Peter Hurley
2015-12-18 17:23               ` Marc Aurele La France
2016-01-14 21:50                 ` Marc Aurele La France
  -- strict thread matches above, loose matches on Subject: below --
2016-02-28 22:53 Brian Bloniarz
2016-02-28 23:02 Brian Bloniarz
2016-02-29  3:56 Brian Bloniarz
2016-03-01  4:30 ` Peter Hurley

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=56699356.8040802@hurleysoftware.com \
    --to=peter@hurleysoftware.com \
    --cc=djm@mindrot.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=jslaby@suse.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=openssh@volth.com \
    --cc=tsi@tuyoix.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).