diff options
Diffstat (limited to 'drivers/char/tty_ioctl.c')
| -rw-r--r-- | drivers/char/tty_ioctl.c | 88 |
1 files changed, 59 insertions, 29 deletions
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 6f4c7d0a53bf..8116bb1c8f80 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c | |||
| @@ -97,14 +97,19 @@ EXPORT_SYMBOL(tty_driver_flush_buffer); | |||
| 97 | * @tty: terminal | 97 | * @tty: terminal |
| 98 | * | 98 | * |
| 99 | * Indicate that a tty should stop transmitting data down the stack. | 99 | * Indicate that a tty should stop transmitting data down the stack. |
| 100 | * Takes the termios mutex to protect against parallel throttle/unthrottle | ||
| 101 | * and also to ensure the driver can consistently reference its own | ||
| 102 | * termios data at this point when implementing software flow control. | ||
| 100 | */ | 103 | */ |
| 101 | 104 | ||
| 102 | void tty_throttle(struct tty_struct *tty) | 105 | void tty_throttle(struct tty_struct *tty) |
| 103 | { | 106 | { |
| 107 | mutex_lock(&tty->termios_mutex); | ||
| 104 | /* check TTY_THROTTLED first so it indicates our state */ | 108 | /* check TTY_THROTTLED first so it indicates our state */ |
| 105 | if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && | 109 | if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && |
| 106 | tty->ops->throttle) | 110 | tty->ops->throttle) |
| 107 | tty->ops->throttle(tty); | 111 | tty->ops->throttle(tty); |
| 112 | mutex_unlock(&tty->termios_mutex); | ||
| 108 | } | 113 | } |
| 109 | EXPORT_SYMBOL(tty_throttle); | 114 | EXPORT_SYMBOL(tty_throttle); |
| 110 | 115 | ||
| @@ -113,13 +118,21 @@ EXPORT_SYMBOL(tty_throttle); | |||
| 113 | * @tty: terminal | 118 | * @tty: terminal |
| 114 | * | 119 | * |
| 115 | * Indicate that a tty may continue transmitting data down the stack. | 120 | * Indicate that a tty may continue transmitting data down the stack. |
| 121 | * Takes the termios mutex to protect against parallel throttle/unthrottle | ||
| 122 | * and also to ensure the driver can consistently reference its own | ||
| 123 | * termios data at this point when implementing software flow control. | ||
| 124 | * | ||
| 125 | * Drivers should however remember that the stack can issue a throttle, | ||
| 126 | * then change flow control method, then unthrottle. | ||
| 116 | */ | 127 | */ |
| 117 | 128 | ||
| 118 | void tty_unthrottle(struct tty_struct *tty) | 129 | void tty_unthrottle(struct tty_struct *tty) |
| 119 | { | 130 | { |
| 131 | mutex_lock(&tty->termios_mutex); | ||
| 120 | if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && | 132 | if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && |
| 121 | tty->ops->unthrottle) | 133 | tty->ops->unthrottle) |
| 122 | tty->ops->unthrottle(tty); | 134 | tty->ops->unthrottle(tty); |
| 135 | mutex_unlock(&tty->termios_mutex); | ||
| 123 | } | 136 | } |
| 124 | EXPORT_SYMBOL(tty_unthrottle); | 137 | EXPORT_SYMBOL(tty_unthrottle); |
| 125 | 138 | ||
| @@ -613,9 +626,25 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) | |||
| 613 | return 0; | 626 | return 0; |
| 614 | } | 627 | } |
| 615 | 628 | ||
| 629 | static void copy_termios(struct tty_struct *tty, struct ktermios *kterm) | ||
| 630 | { | ||
| 631 | mutex_lock(&tty->termios_mutex); | ||
| 632 | memcpy(kterm, tty->termios, sizeof(struct ktermios)); | ||
| 633 | mutex_unlock(&tty->termios_mutex); | ||
| 634 | } | ||
| 635 | |||
| 636 | static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm) | ||
| 637 | { | ||
| 638 | mutex_lock(&tty->termios_mutex); | ||
| 639 | memcpy(kterm, tty->termios_locked, sizeof(struct ktermios)); | ||
| 640 | mutex_unlock(&tty->termios_mutex); | ||
| 641 | } | ||
| 642 | |||
| 616 | static int get_termio(struct tty_struct *tty, struct termio __user *termio) | 643 | static int get_termio(struct tty_struct *tty, struct termio __user *termio) |
| 617 | { | 644 | { |
| 618 | if (kernel_termios_to_user_termio(termio, tty->termios)) | 645 | struct ktermios kterm; |
| 646 | copy_termios(tty, &kterm); | ||
| 647 | if (kernel_termios_to_user_termio(termio, &kterm)) | ||
| 619 | return -EFAULT; | 648 | return -EFAULT; |
| 620 | return 0; | 649 | return 0; |
| 621 | } | 650 | } |
| @@ -917,6 +946,8 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, | |||
| 917 | struct tty_struct *real_tty; | 946 | struct tty_struct *real_tty; |
| 918 | void __user *p = (void __user *)arg; | 947 | void __user *p = (void __user *)arg; |
| 919 | int ret = 0; | 948 | int ret = 0; |
| 949 | struct ktermios kterm; | ||
| 950 | struct termiox ktermx; | ||
| 920 | 951 | ||
| 921 | if (tty->driver->type == TTY_DRIVER_TYPE_PTY && | 952 | if (tty->driver->type == TTY_DRIVER_TYPE_PTY && |
| 922 | tty->driver->subtype == PTY_TYPE_MASTER) | 953 | tty->driver->subtype == PTY_TYPE_MASTER) |
| @@ -952,23 +983,20 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, | |||
| 952 | return set_termios(real_tty, p, TERMIOS_OLD); | 983 | return set_termios(real_tty, p, TERMIOS_OLD); |
| 953 | #ifndef TCGETS2 | 984 | #ifndef TCGETS2 |
| 954 | case TCGETS: | 985 | case TCGETS: |
| 955 | mutex_lock(&real_tty->termios_mutex); | 986 | copy_termios(real_tty, &kterm); |
| 956 | if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios)) | 987 | if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm)) |
| 957 | ret = -EFAULT; | 988 | ret = -EFAULT; |
| 958 | mutex_unlock(&real_tty->termios_mutex); | ||
| 959 | return ret; | 989 | return ret; |
| 960 | #else | 990 | #else |
| 961 | case TCGETS: | 991 | case TCGETS: |
| 962 | mutex_lock(&real_tty->termios_mutex); | 992 | copy_termios(real_tty, &kterm); |
| 963 | if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios)) | 993 | if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm)) |
| 964 | ret = -EFAULT; | 994 | ret = -EFAULT; |
| 965 | mutex_unlock(&real_tty->termios_mutex); | ||
| 966 | return ret; | 995 | return ret; |
| 967 | case TCGETS2: | 996 | case TCGETS2: |
| 968 | mutex_lock(&real_tty->termios_mutex); | 997 | copy_termios(real_tty, &kterm); |
| 969 | if (kernel_termios_to_user_termios((struct termios2 __user *)arg, real_tty->termios)) | 998 | if (kernel_termios_to_user_termios((struct termios2 __user *)arg, &kterm)) |
| 970 | ret = -EFAULT; | 999 | ret = -EFAULT; |
| 971 | mutex_unlock(&real_tty->termios_mutex); | ||
| 972 | return ret; | 1000 | return ret; |
| 973 | case TCSETSF2: | 1001 | case TCSETSF2: |
| 974 | return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT); | 1002 | return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT); |
| @@ -987,34 +1015,36 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, | |||
| 987 | return set_termios(real_tty, p, TERMIOS_TERMIO); | 1015 | return set_termios(real_tty, p, TERMIOS_TERMIO); |
| 988 | #ifndef TCGETS2 | 1016 | #ifndef TCGETS2 |
| 989 | case TIOCGLCKTRMIOS: | 1017 | case TIOCGLCKTRMIOS: |
| 990 | mutex_lock(&real_tty->termios_mutex); | 1018 | copy_termios_locked(real_tty, &kterm); |
| 991 | if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios_locked)) | 1019 | if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm)) |
| 992 | ret = -EFAULT; | 1020 | ret = -EFAULT; |
| 993 | mutex_unlock(&real_tty->termios_mutex); | ||
| 994 | return ret; | 1021 | return ret; |
| 995 | case TIOCSLCKTRMIOS: | 1022 | case TIOCSLCKTRMIOS: |
| 996 | if (!capable(CAP_SYS_ADMIN)) | 1023 | if (!capable(CAP_SYS_ADMIN)) |
| 997 | return -EPERM; | 1024 | return -EPERM; |
| 998 | mutex_lock(&real_tty->termios_mutex); | 1025 | copy_termios_locked(real_tty, &kterm); |
| 999 | if (user_termios_to_kernel_termios(real_tty->termios_locked, | 1026 | if (user_termios_to_kernel_termios(&kterm, |
| 1000 | (struct termios __user *) arg)) | 1027 | (struct termios __user *) arg)) |
| 1001 | ret = -EFAULT; | 1028 | return -EFAULT; |
| 1029 | mutex_lock(&real_tty->termios_mutex); | ||
| 1030 | memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios)); | ||
| 1002 | mutex_unlock(&real_tty->termios_mutex); | 1031 | mutex_unlock(&real_tty->termios_mutex); |
| 1003 | return ret; | 1032 | return 0; |
| 1004 | #else | 1033 | #else |
| 1005 | case TIOCGLCKTRMIOS: | 1034 | case TIOCGLCKTRMIOS: |
| 1006 | mutex_lock(&real_tty->termios_mutex); | 1035 | copy_termios_locked(real_tty, &kterm); |
| 1007 | if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios_locked)) | 1036 | if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm)) |
| 1008 | ret = -EFAULT; | 1037 | ret = -EFAULT; |
| 1009 | mutex_unlock(&real_tty->termios_mutex); | ||
| 1010 | return ret; | 1038 | return ret; |
| 1011 | case TIOCSLCKTRMIOS: | 1039 | case TIOCSLCKTRMIOS: |
| 1012 | if (!capable(CAP_SYS_ADMIN)) | 1040 | if (!capable(CAP_SYS_ADMIN)) |
| 1013 | ret = -EPERM; | 1041 | return -EPERM; |
| 1014 | mutex_lock(&real_tty->termios_mutex); | 1042 | copy_termios_locked(real_tty, &kterm); |
| 1015 | if (user_termios_to_kernel_termios_1(real_tty->termios_locked, | 1043 | if (user_termios_to_kernel_termios_1(&kterm, |
| 1016 | (struct termios __user *) arg)) | 1044 | (struct termios __user *) arg)) |
| 1017 | ret = -EFAULT; | 1045 | return -EFAULT; |
| 1046 | mutex_lock(&real_tty->termios_mutex); | ||
| 1047 | memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios)); | ||
| 1018 | mutex_unlock(&real_tty->termios_mutex); | 1048 | mutex_unlock(&real_tty->termios_mutex); |
| 1019 | return ret; | 1049 | return ret; |
| 1020 | #endif | 1050 | #endif |
| @@ -1023,9 +1053,10 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, | |||
| 1023 | if (real_tty->termiox == NULL) | 1053 | if (real_tty->termiox == NULL) |
| 1024 | return -EINVAL; | 1054 | return -EINVAL; |
| 1025 | mutex_lock(&real_tty->termios_mutex); | 1055 | mutex_lock(&real_tty->termios_mutex); |
| 1026 | if (copy_to_user(p, real_tty->termiox, sizeof(struct termiox))) | 1056 | memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox)); |
| 1027 | ret = -EFAULT; | ||
| 1028 | mutex_unlock(&real_tty->termios_mutex); | 1057 | mutex_unlock(&real_tty->termios_mutex); |
| 1058 | if (copy_to_user(p, &ktermx, sizeof(struct termiox))) | ||
| 1059 | ret = -EFAULT; | ||
| 1029 | return ret; | 1060 | return ret; |
| 1030 | case TCSETX: | 1061 | case TCSETX: |
| 1031 | return set_termiox(real_tty, p, 0); | 1062 | return set_termiox(real_tty, p, 0); |
| @@ -1035,10 +1066,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, | |||
| 1035 | return set_termiox(real_tty, p, TERMIOS_FLUSH); | 1066 | return set_termiox(real_tty, p, TERMIOS_FLUSH); |
| 1036 | #endif | 1067 | #endif |
| 1037 | case TIOCGSOFTCAR: | 1068 | case TIOCGSOFTCAR: |
| 1038 | mutex_lock(&real_tty->termios_mutex); | 1069 | copy_termios(real_tty, &kterm); |
| 1039 | ret = put_user(C_CLOCAL(real_tty) ? 1 : 0, | 1070 | ret = put_user((kterm.c_cflag & CLOCAL) ? 1 : 0, |
| 1040 | (int __user *)arg); | 1071 | (int __user *)arg); |
| 1041 | mutex_unlock(&real_tty->termios_mutex); | ||
| 1042 | return ret; | 1072 | return ret; |
| 1043 | case TIOCSSOFTCAR: | 1073 | case TIOCSSOFTCAR: |
| 1044 | if (get_user(arg, (unsigned int __user *) arg)) | 1074 | if (get_user(arg, (unsigned int __user *) arg)) |
