| From 2831c89f42dcde440cfdccb9fee9f42d54bbc1ef Mon Sep 17 00:00:00 2001 |
| From: "Herton R. Krzesinski" <herton@redhat.com> |
| Date: Mon, 11 Jan 2016 12:07:43 -0200 |
| Subject: pty: fix possible use after free of tty->driver_data |
| |
| From: Herton R. Krzesinski <herton@redhat.com> |
| |
| commit 2831c89f42dcde440cfdccb9fee9f42d54bbc1ef upstream. |
| |
| This change fixes a bug for a corner case where we have the the last |
| release from a pty master/slave coming from a previously opened /dev/tty |
| file. When this happens, the tty->driver_data can be stale, due to all |
| ptmx or pts/N files having already been closed before (and thus the inode |
| related to these files, which tty->driver_data points to, being already |
| freed/destroyed). |
| |
| The fix here is to keep a reference on the opened master ptmx inode. |
| We maintain the inode referenced until the final pty_unix98_shutdown, |
| and only pass this inode to devpts_kill_index. |
| |
| Signed-off-by: Herton R. Krzesinski <herton@redhat.com> |
| Reviewed-by: Peter Hurley <peter@hurleysoftware.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/tty/pty.c | 18 +++++++++++++++++- |
| 1 file changed, 17 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/tty/pty.c |
| +++ b/drivers/tty/pty.c |
| @@ -617,7 +617,14 @@ static void pty_unix98_remove(struct tty |
| /* this is called once with whichever end is closed last */ |
| static void pty_unix98_shutdown(struct tty_struct *tty) |
| { |
| - devpts_kill_index(tty->driver_data, tty->index); |
| + struct inode *ptmx_inode; |
| + |
| + if (tty->driver->subtype == PTY_TYPE_MASTER) |
| + ptmx_inode = tty->driver_data; |
| + else |
| + ptmx_inode = tty->link->driver_data; |
| + devpts_kill_index(ptmx_inode, tty->index); |
| + iput(ptmx_inode); /* drop reference we acquired at ptmx_open */ |
| } |
| |
| static const struct tty_operations ptm_unix98_ops = { |
| @@ -708,6 +715,15 @@ static int ptmx_open(struct inode *inode |
| set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ |
| tty->driver_data = inode; |
| |
| + /* |
| + * In the case where all references to ptmx inode are dropped and we |
| + * still have /dev/tty opened pointing to the master/slave pair (ptmx |
| + * is closed/released before /dev/tty), we must make sure that the inode |
| + * is still valid when we call the final pty_unix98_shutdown, thus we |
| + * hold an additional reference to the ptmx inode |
| + */ |
| + ihold(inode); |
| + |
| tty_add_file(tty, filp); |
| |
| slave_inode = devpts_pty_new(inode, |