--- /dev/null
+From 7d71109df186d630a41280670c8d71d0cf9b0da9 Mon Sep 17 00:00:00 2001
+From: Christian Brauner <christian.brauner@ubuntu.com>
+Date: Tue, 13 Mar 2018 17:55:24 +0100
+Subject: devpts: hoist out check for DEVPTS_SUPER_MAGIC
+
+From: Christian Brauner <christian.brauner@ubuntu.com>
+
+commit 7d71109df186d630a41280670c8d71d0cf9b0da9 upstream.
+
+Hoist the check whether we have already found a suitable devpts filesystem
+out of devpts_ptmx_path() in preparation for the devpts bind-mount
+resolution patch. This is a non-functional change.
+
+Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
+Reviewed-by: "Eric W. Biederman" <ebiederm@xmission.com>
+Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/devpts/inode.c | 26 +++++++++++++++-----------
+ 1 file changed, 15 insertions(+), 11 deletions(-)
+
+--- a/fs/devpts/inode.c
++++ b/fs/devpts/inode.c
+@@ -138,10 +138,6 @@ static int devpts_ptmx_path(struct path
+ struct super_block *sb;
+ int err;
+
+- /* Has the devpts filesystem already been found? */
+- if (path->mnt->mnt_sb->s_magic == DEVPTS_SUPER_MAGIC)
+- return 0;
+-
+ /* Is a devpts filesystem at "pts" in the same directory? */
+ err = path_pts(path);
+ if (err)
+@@ -159,21 +155,25 @@ static int devpts_ptmx_path(struct path
+ struct vfsmount *devpts_mntget(struct file *filp, struct pts_fs_info *fsi)
+ {
+ struct path path;
+- int err;
++ int err = 0;
+
+ path = filp->f_path;
+ path_get(&path);
+
+- err = devpts_ptmx_path(&path);
++ /* Has the devpts filesystem already been found? */
++ if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC)
++ err = devpts_ptmx_path(&path);
+ dput(path.dentry);
+ if (err) {
+ mntput(path.mnt);
+ return ERR_PTR(err);
+ }
++
+ if (DEVPTS_SB(path.mnt->mnt_sb) != fsi) {
+ mntput(path.mnt);
+ return ERR_PTR(-ENODEV);
+ }
++
+ return path.mnt;
+ }
+
+@@ -182,15 +182,19 @@ struct pts_fs_info *devpts_acquire(struc
+ struct pts_fs_info *result;
+ struct path path;
+ struct super_block *sb;
+- int err;
+
+ path = filp->f_path;
+ path_get(&path);
+
+- err = devpts_ptmx_path(&path);
+- if (err) {
+- result = ERR_PTR(err);
+- goto out;
++ /* Has the devpts filesystem already been found? */
++ if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) {
++ int err;
++
++ err = devpts_ptmx_path(&path);
++ if (err) {
++ result = ERR_PTR(err);
++ goto out;
++ }
+ }
+
+ /*
--- /dev/null
+From a319b01d9095da6f6c54bd20c1f1300762506255 Mon Sep 17 00:00:00 2001
+From: Christian Brauner <christian.brauner@ubuntu.com>
+Date: Tue, 13 Mar 2018 17:55:25 +0100
+Subject: devpts: resolve devpts bind-mounts
+
+From: Christian Brauner <christian.brauner@ubuntu.com>
+
+commit a319b01d9095da6f6c54bd20c1f1300762506255 upstream.
+
+Most libcs will still look at /dev/ptmx when opening the master fd of a pty
+device. When /dev/ptmx is a bind-mount of /dev/pts/ptmx and the TIOCGPTPEER
+ioctl() is used to safely retrieve a file descriptor for the slave side of
+the pty based on the master fd, the /proc/self/fd/{0,1,2} symlinks will
+point to /. A very simply reproducer for this issue presupposing a libc
+that uses TIOCGPTPEER in its openpty() implementation is:
+
+unshare --mount
+mount --bind /dev/pts/ptmx /dev/ptmx
+chmod 666 /dev/ptmx
+script
+ls -al /proc/self/fd/0
+
+Having bind-mounts of /dev/pts/ptmx to /dev/ptmx not working correctly is a
+regression. In addition, it is also a fairly common scenario in containers
+employing user namespaces.
+
+The reason for the current failure is that the kernel tries to verify the
+useability of the devpts filesystem without resolving the /dev/ptmx
+bind-mount first. This will lead it to detect that the dentry is escaping
+its bind-mount. The reason is that while the devpts filesystem mounted at
+/dev/pts has the devtmpfs mounted at /dev as its parent mount:
+
+21 -- -- / /dev
+-- 21 -- / /dev/pts
+
+devtmpfs and devpts are on different devices
+
+-- -- 0:6 / /dev
+-- -- 0:20 / /dev/pts
+
+This has the consequence that the pathname of the parent directory of the
+devpts filesystem mount at /dev/pts is /. So if /dev/ptmx is a bind-mount
+of /dev/pts/ptmx then the /dev/ptmx bind-mount and the devpts mount at
+/dev/pts will end up being located on the same device which is recorded in
+the superblock of their vfsmount. This means the parent directory of the
+/dev/ptmx bind-mount will be /ptmx:
+
+-- -- ---- /ptmx /dev/ptmx
+
+Without the bind-mount resolution patch the kernel will now perform the
+bind-mount escape check directly on /dev/ptmx. The function responsible for
+this is devpts_ptmx_path() which calls pts_path() which in turn calls
+path_parent_directory(). Based on the above explanation,
+path_parent_directory() will yield / as the parent directory for the
+/dev/ptmx bind-mount and not the expected /dev. Thus, the kernel detects
+that /dev/ptmx is escaping its bind-mount and will set /proc/<pid>/fd/<nr>
+to /.
+
+This patch changes the logic to first resolve any bind-mounts. After the
+bind-mounts have been resolved (i.e. we have traced it back to the
+associated devpts mount) devpts_ptmx_path() can be called. In order to
+guarantee correct path generation for the slave file descriptor the kernel
+now requires that a pts directory is found in the parent directory of the
+ptmx bind-mount. This implies that when doing bind-mounts the ptmx
+bind-mount and the devpts mount should have a common parent directory. A
+valid example is:
+
+mount -t devpts devpts /dev/pts
+mount --bind /dev/pts/ptmx /dev/ptmx
+
+an invalid example is:
+
+mount -t devpts devpts /dev/pts
+mount --bind /dev/pts/ptmx /ptmx
+
+This allows us to support:
+- calling open on ptmx devices located inside non-standard devpts mounts:
+ mount -t devpts devpts /mnt
+ master = open("/mnt/ptmx", ...);
+ slave = ioctl(master, TIOCGPTPEER, ...);
+- calling open on ptmx devices located outside the devpts mount with a
+ common ancestor directory:
+ mount -t devpts devpts /dev/pts
+ mount --bind /dev/pts/ptmx /dev/ptmx
+ master = open("/dev/ptmx", ...);
+ slave = ioctl(master, TIOCGPTPEER, ...);
+
+while failing on ptmx devices located outside the devpts mount without a
+common ancestor directory:
+ mount -t devpts devpts /dev/pts
+ mount --bind /dev/pts/ptmx /ptmx
+ master = open("/ptmx", ...);
+ slave = ioctl(master, TIOCGPTPEER, ...);
+
+in which case save path generation cannot be guaranteed.
+
+Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
+Suggested-by: Eric Biederman <ebiederm@xmission.com>
+Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
+Reviewed-by: "Eric W. Biederman" <ebiederm@xmission.com>
+Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/devpts/inode.c | 26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+--- a/fs/devpts/inode.c
++++ b/fs/devpts/inode.c
+@@ -160,21 +160,27 @@ struct vfsmount *devpts_mntget(struct fi
+ path = filp->f_path;
+ path_get(&path);
+
+- /* Has the devpts filesystem already been found? */
+- if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC)
++ /* Walk upward while the start point is a bind mount of
++ * a single file.
++ */
++ while (path.mnt->mnt_root == path.dentry)
++ if (follow_up(&path) == 0)
++ break;
++
++ /* devpts_ptmx_path() finds a devpts fs or returns an error. */
++ if ((path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) ||
++ (DEVPTS_SB(path.mnt->mnt_sb) != fsi))
+ err = devpts_ptmx_path(&path);
+ dput(path.dentry);
+- if (err) {
+- mntput(path.mnt);
+- return ERR_PTR(err);
+- }
++ if (!err) {
++ if (DEVPTS_SB(path.mnt->mnt_sb) == fsi)
++ return path.mnt;
+
+- if (DEVPTS_SB(path.mnt->mnt_sb) != fsi) {
+- mntput(path.mnt);
+- return ERR_PTR(-ENODEV);
++ err = -ENODEV;
+ }
+
+- return path.mnt;
++ mntput(path.mnt);
++ return ERR_PTR(err);
+ }
+
+ struct pts_fs_info *devpts_acquire(struct file *filp)