pty: fix the cached path of the pty slave file descriptor in the master
authorLinus Torvalds <>
Thu, 17 Aug 2017 00:08:07 +0000 (17:08 -0700)
committerLinus Torvalds <>
Thu, 17 Aug 2017 16:10:48 +0000 (09:10 -0700)
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 <>
Cc: Al Viro <>
Acked-by: Eric Biederman <>
Signed-off-by: Linus Torvalds <>

index 284749f..1fc80ea 100644 (file)
@@ -793,6 +793,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
        struct tty_struct *tty;
        struct path *pts_path;
        struct dentry *dentry;
+       struct vfsmount *mnt;
        int retval;
        int index;
@@ -805,7 +806,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
        if (retval)
                return retval;
-       fsi = devpts_acquire(filp);
+       fsi = devpts_acquire(filp, &mnt);
        if (IS_ERR(fsi)) {
                retval = PTR_ERR(fsi);
                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);
        if (!pts_path)
                goto err_release;
-       pts_path->mnt = filp->f_path.mnt;
+       pts_path->mnt = mnt;
        pts_path->dentry = dentry;
        tty->link->driver_data = pts_path;
@@ -866,6 +867,7 @@ err_path_put:
+       mntput(mnt);
        // This will also put-ref the fsi
        tty_release(inode, filp);
@@ -874,6 +876,7 @@ out:
        devpts_kill_index(fsi, index);
+       mntput(mnt);
        return retval;
index 108df2e..44dfbca 100644 (file)
@@ -133,7 +133,7 @@ static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb)
        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 path path;
@@ -142,6 +142,7 @@ struct pts_fs_info *devpts_acquire(struct file *filp)
        path = filp->f_path;
+       *ptsmnt = NULL;
        /* Has the devpts filesystem already been found? */
        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
+       *ptsmnt = mntget(path.mnt);
        result = DEVPTS_SB(sb);
index 277ab9a..7883e90 100644 (file)
@@ -19,7 +19,7 @@
 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 *);
 int devpts_new_index(struct pts_fs_info *);