| From: Peter Hurley <peter@hurleysoftware.com> |
| Date: Sun, 10 Jan 2016 22:40:55 -0800 |
| Subject: tty: Fix unsafe ldisc reference via ioctl(TIOCGETD) |
| |
| commit 5c17c861a357e9458001f021a7afa7aab9937439 upstream. |
| |
| ioctl(TIOCGETD) retrieves the line discipline id directly from the |
| ldisc because the line discipline id (c_line) in termios is untrustworthy; |
| userspace may have set termios via ioctl(TCSETS*) without actually |
| changing the line discipline via ioctl(TIOCSETD). |
| |
| However, directly accessing the current ldisc via tty->ldisc is |
| unsafe; the ldisc ptr dereferenced may be stale if the line discipline |
| is changing via ioctl(TIOCSETD) or hangup. |
| |
| Wait for the line discipline reference (just like read() or write()) |
| to retrieve the "current" line discipline id. |
| |
| Signed-off-by: Peter Hurley <peter@hurleysoftware.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/tty/tty_io.c | 24 +++++++++++++++++++++++- |
| 1 file changed, 23 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/tty/tty_io.c |
| +++ b/drivers/tty/tty_io.c |
| @@ -2475,6 +2475,28 @@ static int tiocsetd(struct tty_struct *t |
| } |
| |
| /** |
| + * tiocgetd - get line discipline |
| + * @tty: tty device |
| + * @p: pointer to user data |
| + * |
| + * Retrieves the line discipline id directly from the ldisc. |
| + * |
| + * Locking: waits for ldisc reference (in case the line discipline |
| + * is changing or the tty is being hungup) |
| + */ |
| + |
| +static int tiocgetd(struct tty_struct *tty, int __user *p) |
| +{ |
| + struct tty_ldisc *ld; |
| + int ret; |
| + |
| + ld = tty_ldisc_ref_wait(tty); |
| + ret = put_user(ld->ops->num, p); |
| + tty_ldisc_deref(ld); |
| + return ret; |
| +} |
| + |
| +/** |
| * send_break - performed time break |
| * @tty: device to break on |
| * @duration: timeout in mS |
| @@ -2684,7 +2706,7 @@ long tty_ioctl(struct file *file, unsign |
| case TIOCGSID: |
| return tiocgsid(tty, real_tty, p); |
| case TIOCGETD: |
| - return put_user(tty->ldisc->ops->num, (int __user *)p); |
| + return tiocgetd(tty, p); |
| case TIOCSETD: |
| return tiocsetd(tty, p); |
| case TIOCVHANGUP: |