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