From patchwork Fri Feb 18 14:23:43 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim Gardner X-Patchwork-Id: 83602 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from chlorine.canonical.com (chlorine.canonical.com [91.189.94.204]) by ozlabs.org (Postfix) with ESMTP id 7C9DAB70E9 for ; Sat, 19 Feb 2011 01:23:55 +1100 (EST) Received: from localhost ([127.0.0.1] helo=chlorine.canonical.com) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1PqRFN-0005P7-3H; Fri, 18 Feb 2011 14:23:49 +0000 Received: from mail.tpi.com ([70.99.223.143]) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1PqRFJ-0005O9-In for kernel-team@lists.ubuntu.com; Fri, 18 Feb 2011 14:23:45 +0000 Received: from [10.0.2.5] (unknown [10.0.2.5]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by mail.tpi.com (Postfix) with ESMTP id BA11229F051 for ; Fri, 18 Feb 2011 06:22:41 -0800 (PST) Message-ID: <4D5E80EF.2090106@canonical.com> Date: Fri, 18 Feb 2011 07:23:43 -0700 From: Tim Gardner User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101208 Thunderbird/3.1.7 MIME-Version: 1.0 To: kernel-team@lists.ubuntu.com Subject: Re: Dapper CVE: tty: Make tiocgicount a handler, CVE-2010-4076 References: <20110217155854.2D097F89F8@sepang.rtg.net> In-Reply-To: <20110217155854.2D097F89F8@sepang.rtg.net> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.13 Precedence: list Reply-To: tim.gardner@canonical.com List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: kernel-team-bounces@lists.ubuntu.com Errors-To: kernel-team-bounces@lists.ubuntu.com Added CVE-2010-4077 and restored 'case TIOCGICOUNT' to its original code. Acked-by: Brad Figg From 21f89593252c109e2184e64531975713d8dbab20 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 16 Feb 2011 13:09:41 -0700 Subject: [PATCH] tty: Make tiocgicount a handler, CVE-2010-4076, CVE-2010-4077 BugLink: http://bugs.launchpad.net/bugs/720189 CVE-2010-4076 CVE-2010-4077 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 Signed-off-by: Greg Kroah-Hartman (backported from commit d281da7ff6f70efca0553c288bb883e8605b3862) Signed-off-by: Tim Gardner --- drivers/char/tty_io.c | 22 ++++++++++++++++++++++ drivers/serial/serial_core.c | 37 +++++++++++++++++-------------------- drivers/usb/serial/usb-serial.c | 13 +++++++++++++ drivers/usb/serial/usb-serial.h | 2 ++ include/linux/tty_driver.h | 11 +++++++++++ 5 files changed, 65 insertions(+), 20 deletions(-) diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 4b1eef5..0838a30 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -95,6 +95,7 @@ #include #include #include +#include #include #include @@ -2267,6 +2268,20 @@ tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd, return retval; } +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->driver->get_icount) + retval = tty->driver->get_icount(tty, &icount); + if (retval != 0) + return retval; + if (copy_to_user(arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; +} + /* * Split this up, as gcc can choke on it otherwise.. */ @@ -2403,6 +2418,12 @@ int tty_ioctl(struct inode * inode, struct file * file, 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; } if (tty->driver->ioctl) { retval = (tty->driver->ioctl)(tty, file, cmd, arg); @@ -2789,6 +2810,7 @@ void tty_set_operations(struct tty_driver *driver, struct tty_operations *op) driver->write_proc = op->write_proc; driver->tiocmget = op->tiocmget; driver->tiocmset = op->tiocmset; + driver->get_icount = op->get_icount; } diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index e159b32..365374f 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -988,10 +988,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; @@ -999,19 +999,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; - - return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; + 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 0; } /* @@ -1064,10 +1064,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) @@ -2107,6 +2103,7 @@ static struct tty_operations uart_ops = { #endif .tiocmget = uart_tiocmget, .tiocmset = uart_tiocmset, + .get_icount = uart_get_icount, }; /** diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 0ed6790..c77eb4d 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -481,6 +481,18 @@ exit: 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; +} + void usb_serial_port_softint(void *private) { struct usb_serial_port *port = private; @@ -978,6 +990,7 @@ static struct tty_operations serial_ops = { .read_proc = serial_read_proc, .tiocmget = serial_tiocmget, .tiocmset = serial_tiocmset, + .get_icount = serial_get_icount, }; struct tty_driver *usb_serial_tty_driver; diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index fbf526e..36711c2 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -226,6 +226,8 @@ struct usb_serial_driver { void (*unthrottle) (struct usb_serial_port *port); int (*tiocmget) (struct usb_serial_port *port, struct file *file); int (*tiocmset) (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); + int (*get_icount)(struct tty_struct *tty, + struct serial_icounter_struct *icount); void (*read_int_callback)(struct urb *urb, struct pt_regs *regs); void (*write_int_callback)(struct urb *urb, struct pt_regs *regs); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index b368b29..fb4c81c 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -113,11 +113,18 @@ * * This routine is used to send a high-priority XON/XOFF * character to the device. + * + * 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 #include #include +#include /* for serial_state and serial_icounter_struct */ struct tty_struct; @@ -150,6 +157,8 @@ struct tty_operations { 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); }; struct tty_driver { @@ -212,6 +221,8 @@ struct tty_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); struct list_head tty_drivers; }; -- 1.7.0.4