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 | ||