diff options
| -rw-r--r-- | drivers/tty/pty.c | 64 | ||||
| -rw-r--r-- | drivers/tty/tty_io.c | 3 | ||||
| -rw-r--r-- | fs/devpts/inode.c | 65 | ||||
| -rw-r--r-- | include/linux/devpts_fs.h | 10 |
4 files changed, 89 insertions, 53 deletions
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 284749fb0f6b..a6d5164c33a9 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c | |||
| @@ -69,13 +69,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp) | |||
| 69 | #ifdef CONFIG_UNIX98_PTYS | 69 | #ifdef CONFIG_UNIX98_PTYS |
| 70 | if (tty->driver == ptm_driver) { | 70 | if (tty->driver == ptm_driver) { |
| 71 | mutex_lock(&devpts_mutex); | 71 | mutex_lock(&devpts_mutex); |
| 72 | if (tty->link->driver_data) { | 72 | if (tty->link->driver_data) |
| 73 | struct path *path = tty->link->driver_data; | 73 | devpts_pty_kill(tty->link->driver_data); |
| 74 | |||
| 75 | devpts_pty_kill(path->dentry); | ||
| 76 | path_put(path); | ||
| 77 | kfree(path); | ||
| 78 | } | ||
| 79 | mutex_unlock(&devpts_mutex); | 74 | mutex_unlock(&devpts_mutex); |
| 80 | } | 75 | } |
| 81 | #endif | 76 | #endif |
| @@ -607,25 +602,24 @@ static inline void legacy_pty_init(void) { } | |||
| 607 | static struct cdev ptmx_cdev; | 602 | static struct cdev ptmx_cdev; |
| 608 | 603 | ||
| 609 | /** | 604 | /** |
| 610 | * pty_open_peer - open the peer of a pty | 605 | * ptm_open_peer - open the peer of a pty |
| 611 | * @tty: the peer of the pty being opened | 606 | * @master: the open struct file of the ptmx device node |
| 607 | * @tty: the master of the pty being opened | ||
| 608 | * @flags: the flags for open | ||
| 612 | * | 609 | * |
| 613 | * Open the cached dentry in tty->link, providing a safe way for userspace | 610 | * Provide a race free way for userspace to open the slave end of a pty |
| 614 | * to get the slave end of a pty (where they have the master fd and cannot | 611 | * (where they have the master fd and cannot access or trust the mount |
| 615 | * access or trust the mount namespace /dev/pts was mounted inside). | 612 | * namespace /dev/pts was mounted inside). |
| 616 | */ | 613 | */ |
| 617 | static struct file *pty_open_peer(struct tty_struct *tty, int flags) | 614 | int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags) |
| 618 | { | ||
| 619 | if (tty->driver->subtype != PTY_TYPE_MASTER) | ||
| 620 | return ERR_PTR(-EIO); | ||
| 621 | return dentry_open(tty->link->driver_data, flags, current_cred()); | ||
| 622 | } | ||
| 623 | |||
| 624 | static int pty_get_peer(struct tty_struct *tty, int flags) | ||
| 625 | { | 615 | { |
| 626 | int fd = -1; | 616 | int fd = -1; |
| 627 | struct file *filp = NULL; | 617 | struct file *filp; |
| 628 | int retval = -EINVAL; | 618 | int retval = -EINVAL; |
| 619 | struct path path; | ||
| 620 | |||
| 621 | if (tty->driver != ptm_driver) | ||
| 622 | return -EIO; | ||
| 629 | 623 | ||
| 630 | fd = get_unused_fd_flags(0); | 624 | fd = get_unused_fd_flags(0); |
| 631 | if (fd < 0) { | 625 | if (fd < 0) { |
| @@ -633,7 +627,16 @@ static int pty_get_peer(struct tty_struct *tty, int flags) | |||
| 633 | goto err; | 627 | goto err; |
| 634 | } | 628 | } |
| 635 | 629 | ||
| 636 | filp = pty_open_peer(tty, flags); | 630 | /* Compute the slave's path */ |
| 631 | path.mnt = devpts_mntget(master, tty->driver_data); | ||
| 632 | if (IS_ERR(path.mnt)) { | ||
| 633 | retval = PTR_ERR(path.mnt); | ||
| 634 | goto err_put; | ||
| 635 | } | ||
| 636 | path.dentry = tty->link->driver_data; | ||
| 637 | |||
| 638 | filp = dentry_open(&path, flags, current_cred()); | ||
| 639 | mntput(path.mnt); | ||
| 637 | if (IS_ERR(filp)) { | 640 | if (IS_ERR(filp)) { |
| 638 | retval = PTR_ERR(filp); | 641 | retval = PTR_ERR(filp); |
| 639 | goto err_put; | 642 | goto err_put; |
| @@ -662,8 +665,6 @@ static int pty_unix98_ioctl(struct tty_struct *tty, | |||
| 662 | return pty_get_pktmode(tty, (int __user *)arg); | 665 | return pty_get_pktmode(tty, (int __user *)arg); |
| 663 | case TIOCGPTN: /* Get PT Number */ | 666 | case TIOCGPTN: /* Get PT Number */ |
| 664 | return put_user(tty->index, (unsigned int __user *)arg); | 667 | return put_user(tty->index, (unsigned int __user *)arg); |
| 665 | case TIOCGPTPEER: /* Open the other end */ | ||
| 666 | return pty_get_peer(tty, (int) arg); | ||
| 667 | case TIOCSIG: /* Send signal to other side of pty */ | 668 | case TIOCSIG: /* Send signal to other side of pty */ |
| 668 | return pty_signal(tty, (int) arg); | 669 | return pty_signal(tty, (int) arg); |
| 669 | } | 670 | } |
| @@ -791,7 +792,6 @@ static int ptmx_open(struct inode *inode, struct file *filp) | |||
| 791 | { | 792 | { |
| 792 | struct pts_fs_info *fsi; | 793 | struct pts_fs_info *fsi; |
| 793 | struct tty_struct *tty; | 794 | struct tty_struct *tty; |
| 794 | struct path *pts_path; | ||
| 795 | struct dentry *dentry; | 795 | struct dentry *dentry; |
| 796 | int retval; | 796 | int retval; |
| 797 | int index; | 797 | int index; |
| @@ -845,26 +845,16 @@ static int ptmx_open(struct inode *inode, struct file *filp) | |||
| 845 | retval = PTR_ERR(dentry); | 845 | retval = PTR_ERR(dentry); |
| 846 | goto err_release; | 846 | goto err_release; |
| 847 | } | 847 | } |
| 848 | /* We need to cache a fake path for TIOCGPTPEER. */ | 848 | tty->link->driver_data = dentry; |
| 849 | pts_path = kmalloc(sizeof(struct path), GFP_KERNEL); | ||
| 850 | if (!pts_path) | ||
| 851 | goto err_release; | ||
| 852 | pts_path->mnt = filp->f_path.mnt; | ||
| 853 | pts_path->dentry = dentry; | ||
| 854 | path_get(pts_path); | ||
| 855 | tty->link->driver_data = pts_path; | ||
| 856 | 849 | ||
| 857 | retval = ptm_driver->ops->open(tty, filp); | 850 | retval = ptm_driver->ops->open(tty, filp); |
| 858 | if (retval) | 851 | if (retval) |
| 859 | goto err_path_put; | 852 | goto err_release; |
| 860 | 853 | ||
| 861 | tty_debug_hangup(tty, "opening (count=%d)\n", tty->count); | 854 | tty_debug_hangup(tty, "opening (count=%d)\n", tty->count); |
| 862 | 855 | ||
| 863 | tty_unlock(tty); | 856 | tty_unlock(tty); |
| 864 | return 0; | 857 | return 0; |
| 865 | err_path_put: | ||
| 866 | path_put(pts_path); | ||
| 867 | kfree(pts_path); | ||
| 868 | err_release: | 858 | err_release: |
| 869 | tty_unlock(tty); | 859 | tty_unlock(tty); |
| 870 | // This will also put-ref the fsi | 860 | // This will also put-ref the fsi |
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 974b13d24401..10c4038c0e8d 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c | |||
| @@ -2518,6 +2518,9 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
| 2518 | case TIOCSSERIAL: | 2518 | case TIOCSSERIAL: |
| 2519 | tty_warn_deprecated_flags(p); | 2519 | tty_warn_deprecated_flags(p); |
| 2520 | break; | 2520 | break; |
| 2521 | case TIOCGPTPEER: | ||
| 2522 | /* Special because the struct file is needed */ | ||
| 2523 | return ptm_open_peer(file, tty, (int)arg); | ||
| 2521 | default: | 2524 | default: |
| 2522 | retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg); | 2525 | retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg); |
| 2523 | if (retval != -ENOIOCTLCMD) | 2526 | if (retval != -ENOIOCTLCMD) |
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 108df2e3602c..7eae33ffa3fc 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c | |||
| @@ -133,6 +133,50 @@ static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb) | |||
| 133 | return sb->s_fs_info; | 133 | return sb->s_fs_info; |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | static int devpts_ptmx_path(struct path *path) | ||
| 137 | { | ||
| 138 | struct super_block *sb; | ||
| 139 | int err; | ||
| 140 | |||
| 141 | /* Has the devpts filesystem already been found? */ | ||
| 142 | if (path->mnt->mnt_sb->s_magic == DEVPTS_SUPER_MAGIC) | ||
| 143 | return 0; | ||
| 144 | |||
| 145 | /* Is a devpts filesystem at "pts" in the same directory? */ | ||
| 146 | err = path_pts(path); | ||
| 147 | if (err) | ||
| 148 | return err; | ||
| 149 | |||
| 150 | /* Is the path the root of a devpts filesystem? */ | ||
| 151 | sb = path->mnt->mnt_sb; | ||
| 152 | if ((sb->s_magic != DEVPTS_SUPER_MAGIC) || | ||
| 153 | (path->mnt->mnt_root != sb->s_root)) | ||
| 154 | return -ENODEV; | ||
| 155 | |||
| 156 | return 0; | ||
| 157 | } | ||
| 158 | |||
| 159 | struct vfsmount *devpts_mntget(struct file *filp, struct pts_fs_info *fsi) | ||
| 160 | { | ||
| 161 | struct path path; | ||
| 162 | int err; | ||
| 163 | |||
| 164 | path = filp->f_path; | ||
| 165 | path_get(&path); | ||
| 166 | |||
| 167 | err = devpts_ptmx_path(&path); | ||
| 168 | dput(path.dentry); | ||
| 169 | if (err) { | ||
| 170 | mntput(path.mnt); | ||
| 171 | path.mnt = ERR_PTR(err); | ||
| 172 | } | ||
| 173 | if (DEVPTS_SB(path.mnt->mnt_sb) != fsi) { | ||
| 174 | mntput(path.mnt); | ||
| 175 | path.mnt = ERR_PTR(-ENODEV); | ||
| 176 | } | ||
| 177 | return path.mnt; | ||
| 178 | } | ||
| 179 | |||
| 136 | struct pts_fs_info *devpts_acquire(struct file *filp) | 180 | struct pts_fs_info *devpts_acquire(struct file *filp) |
| 137 | { | 181 | { |
| 138 | struct pts_fs_info *result; | 182 | struct pts_fs_info *result; |
| @@ -143,27 +187,16 @@ struct pts_fs_info *devpts_acquire(struct file *filp) | |||
| 143 | path = filp->f_path; | 187 | path = filp->f_path; |
| 144 | path_get(&path); | 188 | path_get(&path); |
| 145 | 189 | ||
| 146 | /* Has the devpts filesystem already been found? */ | 190 | err = devpts_ptmx_path(&path); |
| 147 | sb = path.mnt->mnt_sb; | 191 | if (err) { |
| 148 | if (sb->s_magic != DEVPTS_SUPER_MAGIC) { | 192 | result = ERR_PTR(err); |
| 149 | /* Is a devpts filesystem at "pts" in the same directory? */ | 193 | goto out; |
| 150 | err = path_pts(&path); | ||
| 151 | if (err) { | ||
| 152 | result = ERR_PTR(err); | ||
| 153 | goto out; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* Is the path the root of a devpts filesystem? */ | ||
| 157 | result = ERR_PTR(-ENODEV); | ||
| 158 | sb = path.mnt->mnt_sb; | ||
| 159 | if ((sb->s_magic != DEVPTS_SUPER_MAGIC) || | ||
| 160 | (path.mnt->mnt_root != sb->s_root)) | ||
| 161 | goto out; | ||
| 162 | } | 194 | } |
| 163 | 195 | ||
| 164 | /* | 196 | /* |
| 165 | * pty code needs to hold extra references in case of last /dev/tty close | 197 | * pty code needs to hold extra references in case of last /dev/tty close |
| 166 | */ | 198 | */ |
| 199 | sb = path.mnt->mnt_sb; | ||
| 167 | atomic_inc(&sb->s_active); | 200 | atomic_inc(&sb->s_active); |
| 168 | result = DEVPTS_SB(sb); | 201 | result = DEVPTS_SB(sb); |
| 169 | 202 | ||
diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index 277ab9af9ac2..100cb4343763 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | 19 | ||
| 20 | struct pts_fs_info; | 20 | struct pts_fs_info; |
| 21 | 21 | ||
| 22 | struct vfsmount *devpts_mntget(struct file *, struct pts_fs_info *); | ||
| 22 | struct pts_fs_info *devpts_acquire(struct file *); | 23 | struct pts_fs_info *devpts_acquire(struct file *); |
| 23 | void devpts_release(struct pts_fs_info *); | 24 | void devpts_release(struct pts_fs_info *); |
| 24 | 25 | ||
| @@ -32,6 +33,15 @@ void *devpts_get_priv(struct dentry *); | |||
| 32 | /* unlink */ | 33 | /* unlink */ |
| 33 | void devpts_pty_kill(struct dentry *); | 34 | void devpts_pty_kill(struct dentry *); |
| 34 | 35 | ||
| 36 | /* in pty.c */ | ||
| 37 | int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags); | ||
| 38 | |||
| 39 | #else | ||
| 40 | static inline int | ||
| 41 | ptm_open_peer(struct file *master, struct tty_struct *tty, int flags) | ||
| 42 | { | ||
| 43 | return -EIO; | ||
| 44 | } | ||
| 35 | #endif | 45 | #endif |
| 36 | 46 | ||
| 37 | 47 | ||
