[2521] | 1 | From 4291086b1f081b869c6d79e5b7441633dc3ace00 Mon Sep 17 00:00:00 2001 |
---|
| 2 | From: Peter Hurley <peter@hurleysoftware.com> |
---|
| 3 | Date: Sat, 3 May 2014 14:04:59 +0200 |
---|
| 4 | Subject: [PATCH] n_tty: Fix n_tty_write crash when echoing in raw mode |
---|
| 5 | |
---|
| 6 | The tty atomic_write_lock does not provide an exclusion guarantee for |
---|
| 7 | the tty driver if the termios settings are LECHO & !OPOST. And since |
---|
| 8 | it is unexpected and not allowed to call TTY buffer helpers like |
---|
| 9 | tty_insert_flip_string concurrently, this may lead to crashes when |
---|
| 10 | concurrect writers call pty_write. In that case the following two |
---|
| 11 | writers: |
---|
| 12 | * the ECHOing from a workqueue and |
---|
| 13 | * pty_write from the process |
---|
| 14 | race and can overflow the corresponding TTY buffer like follows. |
---|
| 15 | |
---|
| 16 | If we look into tty_insert_flip_string_fixed_flag, there is: |
---|
| 17 | int space = __tty_buffer_request_room(port, goal, flags); |
---|
| 18 | struct tty_buffer *tb = port->buf.tail; |
---|
| 19 | ... |
---|
| 20 | memcpy(char_buf_ptr(tb, tb->used), chars, space); |
---|
| 21 | ... |
---|
| 22 | tb->used += space; |
---|
| 23 | |
---|
| 24 | so the race of the two can result in something like this: |
---|
| 25 | A B |
---|
| 26 | __tty_buffer_request_room |
---|
| 27 | __tty_buffer_request_room |
---|
| 28 | memcpy(buf(tb->used), ...) |
---|
| 29 | tb->used += space; |
---|
| 30 | memcpy(buf(tb->used), ...) ->BOOM |
---|
| 31 | |
---|
| 32 | B's memcpy is past the tty_buffer due to the previous A's tb->used |
---|
| 33 | increment. |
---|
| 34 | |
---|
| 35 | Since the N_TTY line discipline input processing can output |
---|
| 36 | concurrently with a tty write, obtain the N_TTY ldisc output_lock to |
---|
| 37 | serialize echo output with normal tty writes. This ensures the tty |
---|
| 38 | buffer helper tty_insert_flip_string is not called concurrently and |
---|
| 39 | everything is fine. |
---|
| 40 | |
---|
| 41 | Note that this is nicely reproducible by an ordinary user using |
---|
| 42 | forkpty and some setup around that (raw termios + ECHO). And it is |
---|
| 43 | present in kernels at least after commit |
---|
| 44 | d945cb9cce20ac7143c2de8d88b187f62db99bdc (pty: Rework the pty layer to |
---|
| 45 | use the normal buffering logic) in 2.6.31-rc3. |
---|
| 46 | |
---|
| 47 | js: add more info to the commit log |
---|
| 48 | js: switch to bool |
---|
| 49 | js: lock unconditionally |
---|
| 50 | js: lock only the tty->ops->write call |
---|
| 51 | |
---|
| 52 | References: CVE-2014-0196 |
---|
| 53 | Reported-and-tested-by: Jiri Slaby <jslaby@suse.cz> |
---|
| 54 | Signed-off-by: Peter Hurley <peter@hurleysoftware.com> |
---|
| 55 | Signed-off-by: Jiri Slaby <jslaby@suse.cz> |
---|
| 56 | Cc: Linus Torvalds <torvalds@linux-foundation.org> |
---|
| 57 | Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> |
---|
| 58 | Cc: <stable@vger.kernel.org> |
---|
| 59 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
---|
| 60 | --- |
---|
| 61 | drivers/tty/n_tty.c | 4 ++++ |
---|
| 62 | 1 file changed, 4 insertions(+) |
---|
| 63 | |
---|
| 64 | diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c |
---|
| 65 | index 41fe8a0..fe9d129 100644 |
---|
| 66 | --- a/drivers/tty/n_tty.c |
---|
| 67 | +++ b/drivers/tty/n_tty.c |
---|
| 68 | @@ -2353,8 +2353,12 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, |
---|
| 69 | if (tty->ops->flush_chars) |
---|
| 70 | tty->ops->flush_chars(tty); |
---|
| 71 | } else { |
---|
| 72 | + struct n_tty_data *ldata = tty->disc_data; |
---|
| 73 | + |
---|
| 74 | while (nr > 0) { |
---|
| 75 | + mutex_lock(&ldata->output_lock); |
---|
| 76 | c = tty->ops->write(tty, b, nr); |
---|
| 77 | + mutex_unlock(&ldata->output_lock); |
---|
| 78 | if (c < 0) { |
---|
| 79 | retval = c; |
---|
| 80 | goto break_out; |
---|
| 81 | -- |
---|
| 82 | 1.7.10.4 |
---|
| 83 | |
---|