-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 To all, A race condition in the PTY write buffer handling has been discovered in the Linux kernel. See quoted text below and attachment for patch submitted by Jiri Slaby and Peter Hurley. Regards, Mark
Hi,
SUSE customer Ericsson reported a kernel crash to us which turned out to be a race condition in the PTY write buffer handling.
When two processes/threads write to the same pty, the buffer end could be overwritten and so memory corruption into adjacent buffers could lead to crashes / code execution.
Jiri Slaby and Peter Hurley localized and fixed this problem.
CVE-2014-0196 has been assigned to this issue.
Jiri thinks this was introduced during 2.6.31 development by d945cb9cce20ac7143c2de8d88b187f62db99bdc (pty: Rework the pty layer to use the normal buffering logic) in 2.6.31-rc3. Until then, pty was writing directly to a line discipline without using buffers.
https://bugzilla.novell.com/show_bug.cgi?id=875690
Patch is also attached.
Ciao, Marcus
n_tty-Fix-n_tty_write-crash-when-echoing-in-raw-mode.patch
From 34ba81cd3561c5fc6aff3041f3445d69f85b5155 Mon Sep 17 00:00:00 2001 From: Peter Hurley <peter@hurleysoftware.com> Date: Tue, 29 Apr 2014 12:38:36 -0400 Subject: [PATCH 1/1] n_tty: Fix n_tty_write crash when echoing in raw mode
The tty atomic_write_lock does not provide an exclusion guarantee for the tty driver if the termios settings are LECHO & !OPOST. And since it is unexpected and not allowed to call TTY buffer helpers like tty_insert_flip_string concurrently, this may lead to crashes when concurrect writers call pty_write. In that case the following two writers: * the ECHOing from a workqueue and * pty_write from the process race and can overflow the corresponding TTY buffer like follows.
If we look into tty_insert_flip_string_fixed_flag, there is: int space = __tty_buffer_request_room(port, goal, flags); struct tty_buffer *tb = port->buf.tail; ... memcpy(char_buf_ptr(tb, tb->used), chars, space); ... tb->used += space;
so the race of the two can result in something like this: A B __tty_buffer_request_room __tty_buffer_request_room memcpy(buf(tb->used), ...) tb->used += space; memcpy(buf(tb->used), ...) ->BOOM
B's memcpy is past the tty_buffer due to the previous A's tb->used increment.
Since the N_TTY line discipline input processing can output concurrently with a tty write, obtain the N_TTY ldisc output_lock to serialize echo output with normal tty writes. This ensures the tty buffer helper tty_insert_flip_string is not called concurrently and everything is fine.
Note that this is nicely reproducible by an ordinary user using forkpty and some setup around that (raw termios + ECHO). And it is exploitable in kernels at least after commit d945cb9cce20ac7143c2de8d88b187f62db99bdc (pty: Rework the pty layer to use the normal buffering logic) in 2.6.31-rc3.
js: add more info to the commit log js: switch to bool
Reported-and-tested-by: Jiri Slaby <jslaby@suse.cz> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Jiri Slaby <jslaby@suse.cz> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> --- Hi security team,
this is a fix for a potentially exploitable hole in the TTY layer. linux-distros ML has been informed already (to the best of my knowledge).
We, at suse, would appreciate embargo till Mon May 5th.
Thanks.
drivers/tty/n_tty.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 746ae80b972f..94101fc64e02 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2353,10 +2353,18 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, if (tty->ops->flush_chars) tty->ops->flush_chars(tty); } else { + struct n_tty_data *ldata = tty->disc_data; + bool lock; + + lock = L_ECHO(tty) || (ldata->icanon && L_ECHONL(tty)); + if (lock) + mutex_lock(&ldata->output_lock); while (nr > 0) { c = tty->ops->write(tty, b, nr); if (c < 0) { retval = c; + if (lock) + mutex_unlock(&ldata->output_lock); goto break_out; } if (!c) @@ -2364,6 +2372,8 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, b += c; nr -= c; } + if (lock) + mutex_unlock(&ldata->output_lock); } if (!nr) break; -- 1.9.2
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iF4EAREIAAYFAlNn2+kACgkQZ/Z80n6+J/Z+VwD+NEmrct67a5k4ezgMFHYGQFRe OaBWcjPwO872ETeOVYQBAIPzEAYUXUz8142uw0xZmCfa43YjRHt8s5HMTBP8pFDe =kxZq -----END PGP SIGNATURE-----