| [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 |  | 
|---|