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