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 <pe...@hurleysoftware.com>
---
 drivers/tty/n_tty.c | 35 ++++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index fa463a9..985e0a3 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1457,6 +1457,8 @@ static void n_tty_receive_buf(struct tty_struct *tty, 
const unsigned char *cp,
        char    buf[64];
        unsigned long cpuflags;
 
+       down_read(&tty->termios_rwsem);
+
        if (ldata->real_raw) {
                raw_spin_lock_irqsave(&ldata->read_lock, cpuflags);
                i = min(N_TTY_BUF_SIZE - read_cnt(ldata),
@@ -1514,13 +1516,19 @@ static void n_tty_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);
+
+       up_read(&tty->termios_rwsem);
 }
 
 int is_ignored(int sig)
@@ -1932,6 +1940,8 @@ do_it_again:
        if (c < 0)
                return c;
 
+       down_read(&tty->termios_rwsem);
+
        minimum = time = 0;
        timeout = MAX_SCHEDULE_TIMEOUT;
        if (!ldata->icanon) {
@@ -1953,11 +1963,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;
 
@@ -2007,7 +2021,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);
@@ -2046,13 +2064,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);
@@ -2074,9 +2096,12 @@ 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;
+       }
 
+       up_read(&tty->termios_rwsem);
        n_tty_set_room(tty);
        return retval;
 }
-- 
1.8.1.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to