Commit c8c03f18 authored by Linus Torvalds's avatar Linus Torvalds

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: default avatarChristian Brauner <christian.brauner@ubuntu.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: default avatarEric Biederman <ebiederm@xmission.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ac9a4090
...@@ -793,6 +793,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int ptmx_open(struct inode *inode, struct file *filp) ...@@ -866,6 +867,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
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 @@ static int ptmx_open(struct inode *inode, struct file *filp) ...@@ -874,6 +876,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
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;
......
...@@ -133,7 +133,7 @@ static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb) ...@@ -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) ...@@ -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) ...@@ -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:
......
...@@ -19,7 +19,7 @@ ...@@ -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 *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment