summaryrefslogtreecommitdiffstats
path: root/drivers/tty/pty.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/pty.c')
-rw-r--r--drivers/tty/pty.c71
1 files changed, 67 insertions, 4 deletions
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 2a6bd9ae3f8b..d1399aac05a1 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -24,6 +24,9 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/poll.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include <linux/ioctl.h>
#undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
@@ -66,8 +69,13 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
#ifdef CONFIG_UNIX98_PTYS
if (tty->driver == ptm_driver) {
mutex_lock(&devpts_mutex);
- if (tty->link->driver_data)
- devpts_pty_kill(tty->link->driver_data);
+ if (tty->link->driver_data) {
+ struct path *path = tty->link->driver_data;
+
+ devpts_pty_kill(path->dentry);
+ path_put(path);
+ kfree(path);
+ }
mutex_unlock(&devpts_mutex);
}
#endif
@@ -440,6 +448,48 @@ err:
return retval;
}
+/**
+ * pty_open_peer - open the peer of a pty
+ * @tty: the peer of the pty being opened
+ *
+ * Open the cached dentry in tty->link, providing a safe way for userspace
+ * to get the slave end of a pty (where they have the master fd and cannot
+ * access or trust the mount namespace /dev/pts was mounted inside).
+ */
+static struct file *pty_open_peer(struct tty_struct *tty, int flags)
+{
+ if (tty->driver->subtype != PTY_TYPE_MASTER)
+ return ERR_PTR(-EIO);
+ return dentry_open(tty->link->driver_data, flags, current_cred());
+}
+
+static int pty_get_peer(struct tty_struct *tty, int flags)
+{
+ int fd = -1;
+ struct file *filp = NULL;
+ int retval = -EINVAL;
+
+ fd = get_unused_fd_flags(0);
+ if (fd < 0) {
+ retval = fd;
+ goto err;
+ }
+
+ filp = pty_open_peer(tty, flags);
+ if (IS_ERR(filp)) {
+ retval = PTR_ERR(filp);
+ goto err_put;
+ }
+
+ fd_install(fd, filp);
+ return fd;
+
+err_put:
+ put_unused_fd(fd);
+err:
+ return retval;
+}
+
static void pty_cleanup(struct tty_struct *tty)
{
tty_port_put(tty->port);
@@ -613,6 +663,8 @@ static int pty_unix98_ioctl(struct tty_struct *tty,
return pty_get_pktmode(tty, (int __user *)arg);
case TIOCGPTN: /* Get PT Number */
return put_user(tty->index, (unsigned int __user *)arg);
+ case TIOCGPTPEER: /* Open the other end */
+ return pty_get_peer(tty, (int) arg);
case TIOCSIG: /* Send signal to other side of pty */
return pty_signal(tty, (int) arg);
}
@@ -740,6 +792,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
{
struct pts_fs_info *fsi;
struct tty_struct *tty;
+ struct path *pts_path;
struct dentry *dentry;
int retval;
int index;
@@ -793,16 +846,26 @@ static int ptmx_open(struct inode *inode, struct file *filp)
retval = PTR_ERR(dentry);
goto err_release;
}
- tty->link->driver_data = dentry;
+ /* We need to cache a fake path for TIOCGPTPEER. */
+ pts_path = kmalloc(sizeof(struct path), GFP_KERNEL);
+ if (!pts_path)
+ goto err_release;
+ pts_path->mnt = filp->f_path.mnt;
+ pts_path->dentry = dentry;
+ path_get(pts_path);
+ tty->link->driver_data = pts_path;
retval = ptm_driver->ops->open(tty, filp);
if (retval)
- goto err_release;
+ goto err_path_put;
tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
tty_unlock(tty);
return 0;
+err_path_put:
+ path_put(pts_path);
+ kfree(pts_path);
err_release:
tty_unlock(tty);
// This will also put-ref the fsi