lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <1410383431-4578-1-git-send-email-peter@hurleysoftware.com>
Date:	Wed, 10 Sep 2014 17:10:31 -0400
From:	Peter Hurley <peter@...leysoftware.com>
To:	Greg Kroah-Hartman <gregkh@...uxfoundation.org>
Cc:	Jiri Slaby <jslaby@...e.cz>,
	One Thousand Gnomes <gnomes@...rguk.ukuu.org.uk>,
	linux-serial@...r.kernel.org, linux-kernel@...r.kernel.org,
	Peter Hurley <peter@...leysoftware.com>
Subject: [RFC] tty: Override and virtualize flow control for tty_send_xchar()

I'm offering this patch as an RFC rather than a PATCH because I think
it's added complexity is not worth the hassle, given that it is only
needed for drivers which don't support the send_xchar() method.

But it does complete the flow control patch series, by fixing a
problem with flow control where a driver that does not support the
send_xchar() method can accidentally restore a stopped terminal,
even though other flow control has restarted the terminal.

Regards,
Peter Hurley

--- >% ---
Subject: [RFC] tty: Override and virtualize flow control for
 tty_send_xchar()

When sending START/STOP from tcflow(TCIxxx), if the tty driver does
not support the send_xchar() method, tty_send_xchar() uses the
normal driver write() routine. Because the tty may stopped,
tty_send_xchar() must override the flow control state and restore
it after START/STOP has been written.

Add file-scope helper, force_start_tty(), which saves the current
flow control state, and starts the tty if necessary (but not if
the output flow has been stopped by tcflow(TCOOFF)). While the
flow control state is overridden, stop_tty() and start_tty()
continue to track the virtual flow control state without notifying
the driver (so the actual flow state does not change).

If, while the override is on, tcflow(TCOOFF) turns off flow control,
the override is ended and the actual flow control is stopped.

Also, add file-scope helper, restore_tty_stopped(), which restores
the actual flow control state to the tracked virtual flow control
state, stopping the tty if necessary.

Signed-off-by: Peter Hurley <peter@...leysoftware.com>
---
 drivers/tty/tty_io.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++------
 include/linux/tty.h  |  4 +++-
 2 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 54e359b..11461ba 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -937,6 +937,13 @@ void no_tty(void)
 
 void __stop_tty(struct tty_struct *tty)
 {
+	if (tty->override_stopped) {
+		if (!tty->flow_stopped) {
+			tty->virt_stopped = 1;
+			return;
+		}
+		tty->override_stopped = 0;
+	}
 	if (tty->stopped)
 		return;
 	tty->stopped = 1;
@@ -968,6 +975,13 @@ EXPORT_SYMBOL(stop_tty);
 
 void __start_tty(struct tty_struct *tty)
 {
+	if (tty->override_stopped) {
+		if (!tty->flow_stopped) {
+			tty->virt_stopped = 0;
+			return;
+		}
+		tty->override_stopped = 0;
+	}
 	if (!tty->stopped || tty->flow_stopped)
 		return;
 	tty->stopped = 0;
@@ -986,6 +1000,35 @@ void start_tty(struct tty_struct *tty)
 }
 EXPORT_SYMBOL(start_tty);
 
+/* Used by tty_send_xchar() to force the tty to start */
+static void force_start_tty(struct tty_struct *tty)
+{
+	spin_lock_irq(&tty->flow_lock);
+	if (!tty->flow_stopped) {
+		tty->override_stopped = 1;
+		tty->virt_stopped = tty->stopped;
+		if (tty->stopped) {
+			tty->stopped = 0;
+			if (tty->ops->start)
+				tty->ops->start(tty);
+			tty_wakeup(tty);
+		}
+	}
+	spin_unlock_irq(&tty->flow_lock);
+}
+
+static void restore_tty_stopped(struct tty_struct *tty)
+{
+	spin_lock_irq(&tty->flow_lock);
+	if (tty->override_stopped) {
+		tty->override_stopped = 0;
+		tty->stopped = tty->virt_stopped;
+		if (tty->stopped && tty->ops->stop)
+			tty->ops->stop(tty);
+	}
+	spin_unlock_irq(&tty->flow_lock);
+}
+
 /* We limit tty time update visibility to every 8 seconds or so. */
 static void tty_update_time(struct timespec *time)
 {
@@ -1241,8 +1284,6 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf,
 
 int tty_send_xchar(struct tty_struct *tty, char ch)
 {
-	int	was_stopped = tty->stopped;
-
 	if (tty->ops->send_xchar) {
 		tty->ops->send_xchar(tty, ch);
 		return 0;
@@ -1251,11 +1292,10 @@ int tty_send_xchar(struct tty_struct *tty, char ch)
 	if (tty_write_lock(tty, 0) < 0)
 		return -ERESTARTSYS;
 
-	if (was_stopped)
-		start_tty(tty);
+	force_start_tty(tty);
 	tty->ops->write(tty, &ch, 1);
-	if (was_stopped)
-		stop_tty(tty);
+	restore_tty_stopped(tty);
+
 	tty_write_unlock(tty);
 	return 0;
 }
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 7a0a796..9c79497 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -264,7 +264,9 @@ struct tty_struct {
 	struct winsize winsize;		/* winsize_mutex */
 	unsigned long stopped:1,	/* flow_lock */
 		      flow_stopped:1,
-		      unused:62;
+		      virt_stopped:1,
+		      override_stopped:1,
+		      unused:60;
 	int hw_stopped;
 	unsigned long ctrl_status:8,	/* ctrl_lock */
 		      packet:1,
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ