Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tty: add TIOCGPTPEER ioctl

When opening the slave end of a PTY, it is not possible for userspace to
safely ensure that /dev/pts/$num is actually a slave (in cases where the
mount namespace in which devpts was mounted is controlled by an
untrusted process). In addition, there are several unresolvable
race conditions if userspace were to attempt to detect attacks through
stat(2) and other similar methods [in addition it is not clear how
userspace could detect attacks involving FUSE].

Resolve this by providing an interface for userpace to safely open the
"peer" end of a PTY file descriptor by using the dentry cached by
devpts. Since it is not possible to have an open master PTY without
having its slave exposed in /dev/pts this interface is safe. This
interface currently does not provide a way to get the master pty (since
it is not clear whether such an interface is safe or even useful).

Cc: Christian Brauner <christian.brauner@ubuntu.com>
Cc: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Aleksa Sarai and committed by
Greg Kroah-Hartman
54ebbfb1 5f0f187f

+76 -5
+1
arch/alpha/include/uapi/asm/ioctls.h
··· 100 100 #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ 101 101 #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ 102 102 #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ 103 + #define TIOCGPTPEER _IOR('T', 0x41, int) /* Safely open the slave */ 103 104 104 105 #define TIOCSERCONFIG 0x5453 105 106 #define TIOCSERGWILD 0x5454
+1
arch/mips/include/uapi/asm/ioctls.h
··· 91 91 #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ 92 92 #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ 93 93 #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ 94 + #define TIOCGPTPEER _IOR('T', 0x41, int) /* Safely open the slave */ 94 95 95 96 /* I hope the range from 0x5480 on is free ... */ 96 97 #define TIOCSCTTY 0x5480 /* become controlling tty */
+1
arch/parisc/include/uapi/asm/ioctls.h
··· 60 60 #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ 61 61 #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ 62 62 #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ 63 + #define TIOCGPTPEER _IOR('T', 0x41, int) /* Safely open the slave */ 63 64 64 65 #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ 65 66 #define FIOCLEX 0x5451
+1
arch/powerpc/include/uapi/asm/ioctls.h
··· 100 100 #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ 101 101 #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ 102 102 #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ 103 + #define TIOCGPTPEER _IOR('T', 0x41, int) /* Safely open the slave */ 103 104 104 105 #define TIOCSERCONFIG 0x5453 105 106 #define TIOCSERGWILD 0x5454
+1
arch/sh/include/uapi/asm/ioctls.h
··· 93 93 #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ 94 94 #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ 95 95 #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ 96 + #define TIOCGPTPEER _IOR('T', 0x41, int) /* Safely open the slave */ 96 97 97 98 #define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */ 98 99 #define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */
+2 -1
arch/sparc/include/uapi/asm/ioctls.h
··· 27 27 #define TIOCGRS485 _IOR('T', 0x41, struct serial_rs485) 28 28 #define TIOCSRS485 _IOWR('T', 0x42, struct serial_rs485) 29 29 30 - /* Note that all the ioctls that are not available in Linux have a 30 + /* Note that all the ioctls that are not available in Linux have a 31 31 * double underscore on the front to: a) avoid some programs to 32 32 * think we support some ioctls under Linux (autoconfiguration stuff) 33 33 */ ··· 88 88 #define TIOCGPTN _IOR('t', 134, unsigned int) /* Get Pty Number */ 89 89 #define TIOCSPTLCK _IOW('t', 135, int) /* Lock/unlock PTY */ 90 90 #define TIOCSIG _IOW('t', 136, int) /* Generate signal on Pty slave */ 91 + #define TIOCGPTPEER _IOR('t', 137, int) /* Safely open the slave */ 91 92 92 93 /* Little f */ 93 94 #define FIOCLEX _IO('f', 1)
+1
arch/xtensa/include/uapi/asm/ioctls.h
··· 105 105 #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ 106 106 #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ 107 107 #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ 108 + #define TIOCGPTPEER _IOR('T', 0x41, int) /* Safely open the slave */ 108 109 109 110 #define TIOCSERCONFIG _IO('T', 83) 110 111 #define TIOCSERGWILD _IOR('T', 84, int)
+67 -4
drivers/tty/pty.c
··· 24 24 #include <linux/slab.h> 25 25 #include <linux/mutex.h> 26 26 #include <linux/poll.h> 27 + #include <linux/mount.h> 28 + #include <linux/file.h> 29 + #include <linux/ioctl.h> 27 30 28 31 #undef TTY_DEBUG_HANGUP 29 32 #ifdef TTY_DEBUG_HANGUP ··· 69 66 #ifdef CONFIG_UNIX98_PTYS 70 67 if (tty->driver == ptm_driver) { 71 68 mutex_lock(&devpts_mutex); 72 - if (tty->link->driver_data) 73 - devpts_pty_kill(tty->link->driver_data); 69 + if (tty->link->driver_data) { 70 + struct path *path = tty->link->driver_data; 71 + 72 + devpts_pty_kill(path->dentry); 73 + path_put(path); 74 + kfree(path); 75 + } 74 76 mutex_unlock(&devpts_mutex); 75 77 } 76 78 #endif ··· 448 440 return retval; 449 441 } 450 442 443 + /** 444 + * pty_open_peer - open the peer of a pty 445 + * @tty: the peer of the pty being opened 446 + * 447 + * Open the cached dentry in tty->link, providing a safe way for userspace 448 + * to get the slave end of a pty (where they have the master fd and cannot 449 + * access or trust the mount namespace /dev/pts was mounted inside). 450 + */ 451 + static struct file *pty_open_peer(struct tty_struct *tty, int flags) 452 + { 453 + if (tty->driver->subtype != PTY_TYPE_MASTER) 454 + return ERR_PTR(-EIO); 455 + return dentry_open(tty->link->driver_data, flags, current_cred()); 456 + } 457 + 458 + static int pty_get_peer(struct tty_struct *tty, int flags) 459 + { 460 + int fd = -1; 461 + struct file *filp = NULL; 462 + int retval = -EINVAL; 463 + 464 + fd = get_unused_fd_flags(0); 465 + if (fd < 0) { 466 + retval = fd; 467 + goto err; 468 + } 469 + 470 + filp = pty_open_peer(tty, flags); 471 + if (IS_ERR(filp)) { 472 + retval = PTR_ERR(filp); 473 + goto err_put; 474 + } 475 + 476 + fd_install(fd, filp); 477 + return fd; 478 + 479 + err_put: 480 + put_unused_fd(fd); 481 + err: 482 + return retval; 483 + } 484 + 451 485 static void pty_cleanup(struct tty_struct *tty) 452 486 { 453 487 tty_port_put(tty->port); ··· 663 613 return pty_get_pktmode(tty, (int __user *)arg); 664 614 case TIOCGPTN: /* Get PT Number */ 665 615 return put_user(tty->index, (unsigned int __user *)arg); 616 + case TIOCGPTPEER: /* Open the other end */ 617 + return pty_get_peer(tty, (int) arg); 666 618 case TIOCSIG: /* Send signal to other side of pty */ 667 619 return pty_signal(tty, (int) arg); 668 620 } ··· 792 740 { 793 741 struct pts_fs_info *fsi; 794 742 struct tty_struct *tty; 743 + struct path *pts_path; 795 744 struct dentry *dentry; 796 745 int retval; 797 746 int index; ··· 846 793 retval = PTR_ERR(dentry); 847 794 goto err_release; 848 795 } 849 - tty->link->driver_data = dentry; 796 + /* We need to cache a fake path for TIOCGPTPEER. */ 797 + pts_path = kmalloc(sizeof(struct path), GFP_KERNEL); 798 + if (!pts_path) 799 + goto err_release; 800 + pts_path->mnt = filp->f_path.mnt; 801 + pts_path->dentry = dentry; 802 + path_get(pts_path); 803 + tty->link->driver_data = pts_path; 850 804 851 805 retval = ptm_driver->ops->open(tty, filp); 852 806 if (retval) 853 - goto err_release; 807 + goto err_path_put; 854 808 855 809 tty_debug_hangup(tty, "opening (count=%d)\n", tty->count); 856 810 857 811 tty_unlock(tty); 858 812 return 0; 813 + err_path_put: 814 + path_put(pts_path); 815 + kfree(pts_path); 859 816 err_release: 860 817 tty_unlock(tty); 861 818 // This will also put-ref the fsi
+1
include/uapi/asm-generic/ioctls.h
··· 77 77 #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ 78 78 #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ 79 79 #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ 80 + #define TIOCGPTPEER _IOR('T', 0x41, int) /* Safely open the slave */ 80 81 81 82 #define FIONCLEX 0x5450 82 83 #define FIOCLEX 0x5451