Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9356b535 authored by Peter Hurley's avatar Peter Hurley Committed by Greg Kroah-Hartman
Browse files

n_tty: Access termios values safely



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: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6a1c0680
Loading
Loading
Loading
Loading
+39 −5
Original line number Diff line number Diff line
@@ -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 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
	if (c < 0)
		return c;

	down_read(&tty->termios_rwsem);

	minimum = time = 0;
	timeout = MAX_SCHEDULE_TIMEOUT;
	if (!ldata->icanon) {
@@ -1955,12 +1967,16 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
	 *	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;

	add_wait_queue(&tty->read_wait, &wait);
@@ -2009,7 +2025,11 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
				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 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
		 * 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 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
		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;
}