Karmic CVE: tty: Make tiocgicount a handler, CVE-2010-4076
Brad Figg
brad.figg at canonical.com
Tue Feb 22 16:24:51 UTC 2011
On 02/17/2011 10:00 AM, Tim Gardner wrote:
> The following changes since commit 9c40f6a021668061c2bcfaf2e74c1596eee74cd0:
> Tim Gardner (1):
> UBUNTU: Bump ABI
>
> are available in the git repository at:
>
> git://kernel.ubuntu.com/rtg/ubuntu-karmic.git CVE-2010-4076
>
> Alan Cox (1):
> tty: Make tiocgicount a handler, CVE-2010-4076
>
> drivers/char/tty_io.c | 21 +++++++++++++++++++++
> drivers/serial/serial_core.c | 37 +++++++++++++++++--------------------
> drivers/usb/serial/usb-serial.c | 13 +++++++++++++
> include/linux/tty_driver.h | 9 +++++++++
> include/linux/usb/serial.h | 2 ++
> 5 files changed, 62 insertions(+), 20 deletions(-)
>
> From 807e98d20e26876bb0fa003d8d9b32428a8d8b97 Mon Sep 17 00:00:00 2001
> From: Alan Cox<alan at linux.intel.com>
> Date: Thu, 16 Sep 2010 18:21:24 +0100
> Subject: [PATCH] tty: Make tiocgicount a handler, CVE-2010-4076
>
> BugLink: http://bugs.launchpad.net/bugs/720189
>
> CVE-2010-4076
>
> Dan Rosenberg noted that various drivers return the struct with uncleared
> fields. Instead of spending forever trying to stomp all the drivers that
> get it wrong (and every new driver) do the job in one place.
>
> This first patch adds the needed operations and hooks them up, including
> the needed USB midlayer and serial core plumbing.
>
> Signed-off-by: Alan Cox<alan at linux.intel.com>
> Signed-off-by: Greg Kroah-Hartman<gregkh at suse.de>
>
> (cherry picked from commit d281da7ff6f70efca0553c288bb883e8605b3862)
>
> Signed-off-by: Tim Gardner<tim.gardner at canonical.com>
> ---
> drivers/char/tty_io.c | 21 +++++++++++++++++++++
> drivers/serial/serial_core.c | 37 +++++++++++++++++--------------------
> drivers/usb/serial/usb-serial.c | 13 +++++++++++++
> include/linux/tty_driver.h | 9 +++++++++
> include/linux/usb/serial.h | 2 ++
> 5 files changed, 62 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
> index e5704e1..1cc3563 100644
> --- a/drivers/char/tty_io.c
> +++ b/drivers/char/tty_io.c
> @@ -96,6 +96,7 @@
> #include<linux/bitops.h>
> #include<linux/delay.h>
> #include<linux/seq_file.h>
> +#include<linux/serial.h>
>
> #include<linux/uaccess.h>
> #include<asm/system.h>
> @@ -2418,6 +2419,20 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
> return tty->ops->tiocmset(tty, file, set, clear);
> }
>
> +static int tty_tiocgicount(struct tty_struct *tty, void __user *arg)
> +{
> + int retval = -EINVAL;
> + struct serial_icounter_struct icount;
> + memset(&icount, 0, sizeof(icount));
> + if (tty->ops->get_icount)
> + retval = tty->ops->get_icount(tty,&icount);
> + if (retval != 0)
> + return retval;
> + if (copy_to_user(arg,&icount, sizeof(icount)))
> + return -EFAULT;
> + return 0;
> +}
> +
> struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
> {
> if (tty->driver->type == TTY_DRIVER_TYPE_PTY&&
> @@ -2538,6 +2553,12 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> case TIOCMBIC:
> case TIOCMBIS:
> return tty_tiocmset(tty, file, cmd, p);
> + case TIOCGICOUNT:
> + retval = tty_tiocgicount(tty, p);
> + /* For the moment allow fall through to the old method */
> + if (retval != -EINVAL)
> + return retval;
> + break;
> case TCFLSH:
> switch (arg) {
> case TCIFLUSH:
> diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
> index b0bb29d..216272a 100644
> --- a/drivers/serial/serial_core.c
> +++ b/drivers/serial/serial_core.c
> @@ -1059,10 +1059,10 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg)
> * NB: both 1->0 and 0->1 transitions are counted except for
> * RI where only 0->1 is counted.
> */
> -static int uart_get_count(struct uart_state *state,
> - struct serial_icounter_struct __user *icnt)
> +static int uart_get_icount(struct tty_struct *tty,
> + struct serial_icounter_struct *icount)
> {
> - struct serial_icounter_struct icount;
> + struct uart_state *state = tty->driver_data;
> struct uart_icount cnow;
> struct uart_port *port = state->port;
>
> @@ -1070,19 +1070,19 @@ static int uart_get_count(struct uart_state *state,
> memcpy(&cnow,&port->icount, sizeof(struct uart_icount));
> spin_unlock_irq(&port->lock);
>
> - icount.cts = cnow.cts;
> - icount.dsr = cnow.dsr;
> - icount.rng = cnow.rng;
> - icount.dcd = cnow.dcd;
> - icount.rx = cnow.rx;
> - icount.tx = cnow.tx;
> - icount.frame = cnow.frame;
> - icount.overrun = cnow.overrun;
> - icount.parity = cnow.parity;
> - icount.brk = cnow.brk;
> - icount.buf_overrun = cnow.buf_overrun;
> + icount->cts = cnow.cts;
> + icount->dsr = cnow.dsr;
> + icount->rng = cnow.rng;
> + icount->dcd = cnow.dcd;
> + icount->rx = cnow.rx;
> + icount->tx = cnow.tx;
> + icount->frame = cnow.frame;
> + icount->overrun = cnow.overrun;
> + icount->parity = cnow.parity;
> + icount->brk = cnow.brk;
> + icount->buf_overrun = cnow.buf_overrun;
>
> - return copy_to_user(icnt,&icount, sizeof(icount)) ? -EFAULT : 0;
> + return 0;
> }
>
> /*
> @@ -1134,10 +1134,6 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
> case TIOCMIWAIT:
> ret = uart_wait_modem_status(state, arg);
> break;
> -
> - case TIOCGICOUNT:
> - ret = uart_get_count(state, uarg);
> - break;
> }
>
> if (ret != -ENOIOCTLCMD)
> @@ -2299,6 +2295,7 @@ static const struct tty_operations uart_ops = {
> #endif
> .tiocmget = uart_tiocmget,
> .tiocmset = uart_tiocmset,
> + .get_icount = uart_get_icount,
> #ifdef CONFIG_CONSOLE_POLL
> .poll_init = uart_poll_init,
> .poll_get_char = uart_poll_get_char,
> diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
> index 3292e03..10d5d35 100644
> --- a/drivers/usb/serial/usb-serial.c
> +++ b/drivers/usb/serial/usb-serial.c
> @@ -569,6 +569,18 @@ static int serial_tiocmset(struct tty_struct *tty, struct file *file,
> return -EINVAL;
> }
>
> +static int serial_get_icount(struct tty_struct *tty,
> + struct serial_icounter_struct *icount)
> +{
> + struct usb_serial_port *port = tty->driver_data;
> +
> + dbg("%s - port %d", __func__, port->number);
> +
> + if (port->serial->type->get_icount)
> + return port->serial->type->get_icount(tty, icount);
> + return -EINVAL;
> +}
> +
> /*
> * We would be calling tty_wakeup here, but unfortunately some line
> * disciplines have an annoying habit of calling tty->write from
> @@ -1210,6 +1222,7 @@ static const struct tty_operations serial_ops = {
> .tiocmget = serial_tiocmget,
> .tiocmset = serial_tiocmset,
> .shutdown = serial_release,
> + .get_icount = serial_get_icount,
> .install = serial_install,
> .proc_fops = &serial_proc_fops,
> };
> diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
> index 3566129..c6a8bda 100644
> --- a/include/linux/tty_driver.h
> +++ b/include/linux/tty_driver.h
> @@ -216,6 +216,12 @@
> * unless the tty also has a valid tty->termiox pointer.
> *
> * Optional: Called under the termios lock
> + *
> + * int (*get_icount)(struct tty_struct *tty, struct serial_icounter *icount);
> + *
> + * Called when the device receives a TIOCGICOUNT ioctl. Passed a kernel
> + * structure to complete. This method is optional and will only be called
> + * if provided (otherwise EINVAL will be returned).
> */
>
> #include<linux/fs.h>
> @@ -224,6 +230,7 @@
>
> struct tty_struct;
> struct tty_driver;
> +struct serial_icounter_struct;
>
> struct tty_operations {
> struct tty_struct * (*lookup)(struct tty_driver *driver,
> @@ -259,6 +266,8 @@ struct tty_operations {
> unsigned int set, unsigned int clear);
> int (*resize)(struct tty_struct *tty, struct winsize *ws);
> int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
> + int (*get_icount)(struct tty_struct *tty,
> + struct serial_icounter_struct *icount);
> #ifdef CONFIG_CONSOLE_POLL
> int (*poll_init)(struct tty_driver *driver, int line, char *options);
> int (*poll_get_char)(struct tty_driver *driver, int line);
> diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
> index 20b12f3..25cb53d 100644
> --- a/include/linux/usb/serial.h
> +++ b/include/linux/usb/serial.h
> @@ -258,6 +258,8 @@ struct usb_serial_driver {
> int (*tiocmget)(struct tty_struct *tty, struct file *file);
> int (*tiocmset)(struct tty_struct *tty, struct file *file,
> unsigned int set, unsigned int clear);
> + int (*get_icount)(struct tty_struct *tty,
> + struct serial_icounter_struct *icount);
> /* Called by the tty layer for port level work. There may or may not
> be an attached tty at this point */
> void (*dtr_rts)(struct usb_serial_port *port, int on);
Acked-by: Brad Figg <brad.figg at canonical.com>
--
Brad Figg brad.figg at canonical.com http://www.canonical.com
More information about the kernel-team
mailing list