pty: fix the cached path of the pty slave file descriptor in the master

Christian Brauner reported that if you use the TIOCGPTPEER ioctl() to
get a slave pty file descriptor, the resulting file descriptor doesn't
look right in /proc/<pid>/fd/<fd>.  In particular, he wanted to use
readlink() on /proc/self/fd/<fd> to get the pathname of the slave pty
(basically implementing "ptsname{_r}()").

The reason for that was that we had generated the wrong 'struct path'
when we create the pty in ptmx_open().

In particular, the dentry was correct, but the vfsmount pointed to the
mount of the ptmx node. That _can_ be correct - in case you use
"/dev/pts/ptmx" to open the master - but usually is not.  The normal
case is to use /dev/ptmx, which then looks up the pts/ directory, and
then the vfsmount of the ptmx node is obviously the /dev directory, not
the /dev/pts/ directory.

We actually did have the right vfsmount available, but in the wrong
place (it gets looked up in 'devpts_acquire()' when we get a reference
to the pts filesystem), and so ptmx_open() used the wrong mnt pointer.

The end result of this confusion was that the pty worked fine, but when
if you did TIOCGPTPEER to get the slave side of the pty, end end result
would also work, but have that dodgy 'struct path'.

And then when doing "d_path()" on to get the pathname, the vfsmount
would not match the root of the pts directory, and d_path() would return
an empty pathname thinking that the entry had escaped a bind mount into
another mount.

This fixes the problem by making devpts_acquire() return the vfsmount
for the pts filesystem, allowing ptmx_open() to trivially just use the
right mount for the pts dentry, and create the proper 'struct path'.

Reported-by: Christian Brauner <christian.brauner@ubuntu.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Eric Biederman <ebiederm@xmission.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2017-08-16 17:08:07 -07:00
parent ac9a40905a
commit c8c03f1858
3 changed files with 9 additions and 4 deletions

View File

@ -793,6 +793,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
struct tty_struct *tty; struct tty_struct *tty;
struct path *pts_path; struct path *pts_path;
struct dentry *dentry; struct dentry *dentry;
struct vfsmount *mnt;
int retval; int retval;
int index; int index;
@ -805,7 +806,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
if (retval) if (retval)
return retval; return retval;
fsi = devpts_acquire(filp); fsi = devpts_acquire(filp, &mnt);
if (IS_ERR(fsi)) { if (IS_ERR(fsi)) {
retval = PTR_ERR(fsi); retval = PTR_ERR(fsi);
goto out_free_file; goto out_free_file;
@ -849,7 +850,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
pts_path = kmalloc(sizeof(struct path), GFP_KERNEL); pts_path = kmalloc(sizeof(struct path), GFP_KERNEL);
if (!pts_path) if (!pts_path)
goto err_release; goto err_release;
pts_path->mnt = filp->f_path.mnt; pts_path->mnt = mnt;
pts_path->dentry = dentry; pts_path->dentry = dentry;
path_get(pts_path); path_get(pts_path);
tty->link->driver_data = pts_path; tty->link->driver_data = pts_path;
@ -866,6 +867,7 @@ err_path_put:
path_put(pts_path); path_put(pts_path);
kfree(pts_path); kfree(pts_path);
err_release: err_release:
mntput(mnt);
tty_unlock(tty); tty_unlock(tty);
// This will also put-ref the fsi // This will also put-ref the fsi
tty_release(inode, filp); tty_release(inode, filp);
@ -874,6 +876,7 @@ out:
devpts_kill_index(fsi, index); devpts_kill_index(fsi, index);
out_put_fsi: out_put_fsi:
devpts_release(fsi); devpts_release(fsi);
mntput(mnt);
out_free_file: out_free_file:
tty_free_file(filp); tty_free_file(filp);
return retval; return retval;

View File

@ -133,7 +133,7 @@ static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb)
return sb->s_fs_info; return sb->s_fs_info;
} }
struct pts_fs_info *devpts_acquire(struct file *filp) struct pts_fs_info *devpts_acquire(struct file *filp, struct vfsmount **ptsmnt)
{ {
struct pts_fs_info *result; struct pts_fs_info *result;
struct path path; struct path path;
@ -142,6 +142,7 @@ struct pts_fs_info *devpts_acquire(struct file *filp)
path = filp->f_path; path = filp->f_path;
path_get(&path); path_get(&path);
*ptsmnt = NULL;
/* Has the devpts filesystem already been found? */ /* Has the devpts filesystem already been found? */
sb = path.mnt->mnt_sb; sb = path.mnt->mnt_sb;
@ -165,6 +166,7 @@ struct pts_fs_info *devpts_acquire(struct file *filp)
* pty code needs to hold extra references in case of last /dev/tty close * pty code needs to hold extra references in case of last /dev/tty close
*/ */
atomic_inc(&sb->s_active); atomic_inc(&sb->s_active);
*ptsmnt = mntget(path.mnt);
result = DEVPTS_SB(sb); result = DEVPTS_SB(sb);
out: out:

View File

@ -19,7 +19,7 @@
struct pts_fs_info; struct pts_fs_info;
struct pts_fs_info *devpts_acquire(struct file *); struct pts_fs_info *devpts_acquire(struct file *, struct vfsmount **ptsmnt);
void devpts_release(struct pts_fs_info *); void devpts_release(struct pts_fs_info *);
int devpts_new_index(struct pts_fs_info *); int devpts_new_index(struct pts_fs_info *);