n_tty: Fix unsafe driver-side signals
authorPeter Hurley <peter@hurleysoftware.com>
Wed, 6 Mar 2013 13:38:19 +0000 (08:38 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 18 Mar 2013 23:13:59 +0000 (16:13 -0700)
An ldisc reference is insufficient guarantee the foreground process
group is not in the process of being signalled from a hangup.

1) Reads of tty->pgrp must be locked with ctrl_lock
2) The group pid must be referenced for the duration of signalling.
   Because the driver-side is not process-context, a pid reference
   must be acquired.

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

index e3a9321f7f89c5f51fa72f83131eda3c305da642..61f1bc97ccd93ef4df095d5f55ef7a82b20ade11 100644 (file)
@@ -1017,23 +1017,19 @@ static void eraser(unsigned char c, struct tty_struct *tty)
  *     isig            -       handle the ISIG optio
  *     @sig: signal
  *     @tty: terminal
- *     @flush: force flush
  *
- *     Called when a signal is being sent due to terminal input. This
- *     may caus terminal flushing to take place according to the termios
- *     settings and character used. Called from the driver receive_buf
- *     path so serialized.
+ *     Called when a signal is being sent due to terminal input.
+ *     Called from the driver receive_buf path so serialized.
  *
- *     Locking: ctrl_lock, read_lock (both via flush buffer)
+ *     Locking: ctrl_lock
  */
 
-static inline void isig(int sig, struct tty_struct *tty, int flush)
+static inline void isig(int sig, struct tty_struct *tty)
 {
-       if (tty->pgrp)
-               kill_pgrp(tty->pgrp, sig, 1);
-       if (flush || !L_NOFLSH(tty)) {
-               n_tty_flush_buffer(tty);
-               tty_driver_flush_buffer(tty);
+       struct pid *tty_pgrp = tty_get_pgrp(tty);
+       if (tty_pgrp) {
+               kill_pgrp(tty_pgrp, sig, 1);
+               put_pid(tty_pgrp);
        }
 }
 
@@ -1054,7 +1050,11 @@ static inline void n_tty_receive_break(struct tty_struct *tty)
        if (I_IGNBRK(tty))
                return;
        if (I_BRKINT(tty)) {
-               isig(SIGINT, tty, 1);
+               isig(SIGINT, tty);
+               if (!L_NOFLSH(tty)) {
+                       n_tty_flush_buffer(tty);
+                       tty_driver_flush_buffer(tty);
+               }
                return;
        }
        if (I_PARMRK(tty)) {
@@ -1221,11 +1221,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
                signal = SIGTSTP;
                if (c == SUSP_CHAR(tty)) {
 send_signal:
-                       /*
-                        * Note that we do not use isig() here because we want
-                        * the order to be:
-                        * 1) flush, 2) echo, 3) signal
-                        */
                        if (!L_NOFLSH(tty)) {
                                n_tty_flush_buffer(tty);
                                tty_driver_flush_buffer(tty);
@@ -1236,8 +1231,7 @@ send_signal:
                                echo_char(c, tty);
                                process_echoes(tty);
                        }
-                       if (tty->pgrp)
-                               kill_pgrp(tty->pgrp, signal, 1);
+                       isig(signal, tty);
                        return;
                }
        }