a600648
From 938f50fc744cb49892bd42c8f56bdfa63e82a27d Mon Sep 17 00:00:00 2001
a600648
From: Peter Hurley <peter@hurleysoftware.com>
a600648
Date: Sun, 10 Jan 2016 22:40:55 -0800
a600648
Subject: [PATCH] tty: Fix unsafe ldisc reference via ioctl(TIOCGETD)
a600648
a600648
ioctl(TIOCGETD) retrieves the line discipline id directly from the
a600648
ldisc because the line discipline id (c_line) in termios is untrustworthy;
a600648
userspace may have set termios via ioctl(TCSETS*) without actually
a600648
changing the line discipline via ioctl(TIOCSETD).
a600648
a600648
However, directly accessing the current ldisc via tty->ldisc is
a600648
unsafe; the ldisc ptr dereferenced may be stale if the line discipline
a600648
is changing via ioctl(TIOCSETD) or hangup.
a600648
a600648
Wait for the line discipline reference (just like read() or write())
a600648
to retrieve the "current" line discipline id.
a600648
a600648
Cc: <stable@vger.kernel.org>
a600648
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
a600648
---
a600648
 drivers/tty/tty_io.c | 24 +++++++++++++++++++++++-
a600648
 1 file changed, 23 insertions(+), 1 deletion(-)
a600648
a600648
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
a600648
index f435977de740..bd4027e36910 100644
a600648
--- a/drivers/tty/tty_io.c
a600648
+++ b/drivers/tty/tty_io.c
a600648
@@ -2654,6 +2654,28 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
a600648
 }
a600648
 
a600648
 /**
a600648
+ *	tiocgetd	-	get line discipline
a600648
+ *	@tty: tty device
a600648
+ *	@p: pointer to user data
a600648
+ *
a600648
+ *	Retrieves the line discipline id directly from the ldisc.
a600648
+ *
a600648
+ *	Locking: waits for ldisc reference (in case the line discipline
a600648
+ *		is changing or the tty is being hungup)
a600648
+ */
a600648
+
a600648
+static int tiocgetd(struct tty_struct *tty, int __user *p)
a600648
+{
a600648
+	struct tty_ldisc *ld;
a600648
+	int ret;
a600648
+
a600648
+	ld = tty_ldisc_ref_wait(tty);
a600648
+	ret = put_user(ld->ops->num, p);
a600648
+	tty_ldisc_deref(ld);
a600648
+	return ret;
a600648
+}
a600648
+
a600648
+/**
a600648
  *	send_break	-	performed time break
a600648
  *	@tty: device to break on
a600648
  *	@duration: timeout in mS
a600648
@@ -2879,7 +2901,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
a600648
 	case TIOCGSID:
a600648
 		return tiocgsid(tty, real_tty, p);
a600648
 	case TIOCGETD:
a600648
-		return put_user(tty->ldisc->ops->num, (int __user *)p);
a600648
+		return tiocgetd(tty, p);
a600648
 	case TIOCSETD:
a600648
 		return tiocsetd(tty, p);
a600648
 	case TIOCVHANGUP:
a600648
-- 
a600648
2.5.0
a600648