n_tty: Access termios values safely
authorPeter Hurley <peter@hurleysoftware.com>
Sat, 15 Jun 2013 13:14:24 +0000 (09:14 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 Jul 2013 23:43:01 +0000 (16:43 -0700)
Use termios_rwsem to guarantee safe access to the termios values.
This is particularly important for N_TTY as changing certain termios
settings alters the mode of operation.

termios_rwsem must be dropped across throttle/unthrottle since
those functions claim the termios_rwsem exclusively (to guarantee
safe access to the termios and for mutual exclusion).

Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/n_tty.c

index ab923bb..0599b58 100644 (file)
@@ -1491,10 +1491,14 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
         * canonical mode and don't have a newline yet!
         */
        while (1) {
+               int throttled;
                tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
                if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE)
                        break;
-               if (!tty_throttle_safe(tty))
+               up_read(&tty->termios_rwsem);
+               throttled = tty_throttle_safe(tty);
+               down_read(&tty->termios_rwsem);
+               if (!throttled)
                        break;
        }
        __tty_set_flow_change(tty, 0);
@@ -1503,7 +1507,9 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
 static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
                              char *fp, int count)
 {
+       down_read(&tty->termios_rwsem);
        __receive_buf(tty, cp, fp, count);
+       up_read(&tty->termios_rwsem);
 }
 
 static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
@@ -1512,6 +1518,8 @@ static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
        struct n_tty_data *ldata = tty->disc_data;
        int room;
 
+       down_read(&tty->termios_rwsem);
+
        tty->receive_room = room = receive_room(tty);
        if (!room)
                ldata->no_room = 1;
@@ -1519,6 +1527,8 @@ static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
        if (count)
                __receive_buf(tty, cp, fp, count);
 
+       up_read(&tty->termios_rwsem);
+
        return count;
 }
 
@@ -1934,6 +1944,8 @@ do_it_again:
        if (c < 0)
                return c;
 
+       down_read(&tty->termios_rwsem);
+
        minimum = time = 0;
        timeout = MAX_SCHEDULE_TIMEOUT;
        if (!ldata->icanon) {
@@ -1955,11 +1967,15 @@ do_it_again:
         *      Internal serialization of reads.
         */
        if (file->f_flags & O_NONBLOCK) {
-               if (!mutex_trylock(&ldata->atomic_read_lock))
+               if (!mutex_trylock(&ldata->atomic_read_lock)) {
+                       up_read(&tty->termios_rwsem);
                        return -EAGAIN;
+               }
        } else {
-               if (mutex_lock_interruptible(&ldata->atomic_read_lock))
+               if (mutex_lock_interruptible(&ldata->atomic_read_lock)) {
+                       up_read(&tty->termios_rwsem);
                        return -ERESTARTSYS;
+               }
        }
        packet = tty->packet;
 
@@ -2009,7 +2025,11 @@ do_it_again:
                                break;
                        }
                        n_tty_set_room(tty);
+                       up_read(&tty->termios_rwsem);
+
                        timeout = schedule_timeout(timeout);
+
+                       down_read(&tty->termios_rwsem);
                        continue;
                }
                __set_current_state(TASK_RUNNING);
@@ -2048,13 +2068,17 @@ do_it_again:
                 * we won't get any more characters.
                 */
                while (1) {
+                       int unthrottled;
                        tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
                        if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
                                break;
                        if (!tty->count)
                                break;
                        n_tty_set_room(tty);
-                       if (!tty_unthrottle_safe(tty))
+                       up_read(&tty->termios_rwsem);
+                       unthrottled = tty_unthrottle_safe(tty);
+                       down_read(&tty->termios_rwsem);
+                       if (!unthrottled)
                                break;
                }
                __tty_set_flow_change(tty, 0);
@@ -2076,10 +2100,13 @@ do_it_again:
                retval = size;
                if (nr)
                        clear_bit(TTY_PUSH, &tty->flags);
-       } else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
+       } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) {
+               up_read(&tty->termios_rwsem);
                goto do_it_again;
+       }
 
        n_tty_set_room(tty);
+       up_read(&tty->termios_rwsem);
        return retval;
 }
 
@@ -2120,6 +2147,8 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
                        return retval;
        }
 
+       down_read(&tty->termios_rwsem);
+
        /* Write out any echoed characters that are still pending */
        process_echoes(tty);
 
@@ -2173,13 +2202,18 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
                        retval = -EAGAIN;
                        break;
                }
+               up_read(&tty->termios_rwsem);
+
                schedule();
+
+               down_read(&tty->termios_rwsem);
        }
 break_out:
        __set_current_state(TASK_RUNNING);
        remove_wait_queue(&tty->write_wait, &wait);
        if (b - buf != nr && tty->fasync)
                set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+       up_read(&tty->termios_rwsem);
        return (b - buf) ? b - buf : retval;
 }