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 <[email protected]>
---
 drivers/tty/n_tty.c | 44 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 39 insertions(+), 5 deletions(-)

diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 4389b6f..924a3f5 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1492,10 +1492,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);
@@ -1504,7 +1508,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,
@@ -1513,6 +1519,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;
@@ -1520,6 +1528,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;
 }
 
@@ -1932,6 +1942,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 +1965,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 +2023,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 +2066,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,10 +2098,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;
 }
 
@@ -2118,6 +2145,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);
 
@@ -2171,13 +2200,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;
 }
 
-- 
1.8.1.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
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