Linux Archive

Linux Archive (http://www.linux-archive.org/)
-   Ubuntu Kernel Team (http://www.linux-archive.org/ubuntu-kernel-team/)
-   -   tty: Make tiocgicount a handler, CVE-2010-4076, CVE-2010-4077 (http://www.linux-archive.org/ubuntu-kernel-team/491094-tty-make-tiocgicount-handler-cve-2010-4076-cve-2010-4077-a.html)

Alan Cox 09-16-2010 05:21 PM

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 <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

(cherry picked from commit d281da7ff6f70efca0553c288bb883e8605b3862)

Signed-off-by: Tim Gardner <tim.gardner@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 507441a..3a69c39 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>
@@ -2456,6 +2457,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 &&
@@ -2576,6 +2591,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 7f28307..232e2bb 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -1074,10 +1074,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 *uport = state->uart_port;

@@ -1085,19 +1085,19 @@ static int uart_get_count(struct uart_state *state,
memcpy(&cnow, &uport->icount, sizeof(struct uart_icount));
spin_unlock_irq(&uport->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;
}

/*
@@ -1150,10 +1150,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)
@@ -2305,6 +2301,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 8fadc13..e8d141d 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -520,6 +520,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
@@ -1209,6 +1221,7 @@ static const struct tty_operations serial_ops = {
.chars_in_buffer = serial_chars_in_buffer,
.tiocmget = serial_tiocmget,
.tiocmset = serial_tiocmset,
+ .get_icount = serial_get_icount,
.cleanup = serial_cleanup,
.install = serial_install,
.proc_fops = &serial_proc_fops,
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index b086779..db2d227 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -224,6 +224,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>
@@ -232,6 +238,7 @@

struct tty_struct;
struct tty_driver;
+struct serial_icounter_struct;

struct tty_operations {
struct tty_struct * (*lookup)(struct tty_driver *driver,
@@ -268,6 +275,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 84a4c44..8288b57 100644
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -271,6 +271,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);
--
1.7.0.4


--------------000804020605000709020002
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

--
kernel-team mailing list
kernel-team@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/kernel-team

--------------000804020605000709020002--

Alan Cox 09-16-2010 05:21 PM

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 <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

(cherry picked from commit d281da7ff6f70efca0553c288bb883e8605b3862)

Signed-off-by: Tim Gardner <tim.gardner@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 53ffcfc..123cedf 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>
@@ -2436,6 +2437,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 &&
@@ -2556,6 +2571,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 dcc7244..772d9d8 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -1067,10 +1067,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 *uport = state->uart_port;

@@ -1078,19 +1078,19 @@ static int uart_get_count(struct uart_state *state,
memcpy(&cnow, &uport->icount, sizeof(struct uart_icount));
spin_unlock_irq(&uport->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;
}

/*
@@ -1143,10 +1143,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)
@@ -2322,6 +2318,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 bd3fa7f..f23f3b4 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -562,6 +562,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
@@ -1214,6 +1226,7 @@ static const struct tty_operations serial_ops = {
.chars_in_buffer = serial_chars_in_buffer,
.tiocmget = serial_tiocmget,
.tiocmset = serial_tiocmset,
+ .get_icount = serial_get_icount,
.cleanup = serial_cleanup,
.install = serial_install,
.proc_fops = &serial_proc_fops,
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index b086779..db2d227 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -224,6 +224,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>
@@ -232,6 +238,7 @@

struct tty_struct;
struct tty_driver;
+struct serial_icounter_struct;

struct tty_operations {
struct tty_struct * (*lookup)(struct tty_driver *driver,
@@ -268,6 +275,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 ce911eb..7a159e1 100644
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -259,6 +259,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);
--
1.7.0.4


--------------070001070604020505090206
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

--
kernel-team mailing list
kernel-team@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/kernel-team

--------------070001070604020505090206--

Alan Cox 09-16-2010 05:21 PM

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 <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

(cherry picked from commit d281da7ff6f70efca0553c288bb883e8605b3862)

Signed-off-by: Tim Gardner <tim.gardner@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);
--
1.7.0.4


--------------070909020501050800020203
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

--
kernel-team mailing list
kernel-team@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/kernel-team

--------------070909020501050800020203--

Tim Gardner 02-16-2011 04:39 PM

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 <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

(backported from commit d281da7ff6f70efca0553c288bb883e8605b3862)

Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
---
.../openvz/patchset/0001-2.6.24-ovz002.patch | 22 +++++++-----
drivers/char/tty_io.c | 22 ++++++++++++
drivers/serial/serial_core.c | 37 +++++++++-----------
drivers/usb/serial/usb-serial.c | 13 +++++++
include/linux/tty_driver.h | 11 ++++++
include/linux/usb/serial.h | 2 +
6 files changed, 78 insertions(+), 29 deletions(-)

diff --git a/debian/binary-custom.d/openvz/patchset/0001-2.6.24-ovz002.patch b/debian/binary-custom.d/openvz/patchset/0001-2.6.24-ovz002.patch
index f8443e0..729b278 100644
--- a/debian/binary-custom.d/openvz/patchset/0001-2.6.24-ovz002.patch
+++ b/debian/binary-custom.d/openvz/patchset/0001-2.6.24-ovz002.patch
@@ -4639,15 +4639,6 @@ Index: kernel/drivers/char/tty_io.c
================================================== =================
--- kernel.orig/drivers/char/tty_io.c 2008-11-18 01:19:43.000000000 +0100
+++ kernel/drivers/char/tty_io.c 2008-11-24 15:47:45.000000000 +0100
-@@ -94,6 +94,8 @@
- #include <linux/wait.h>
- #include <linux/bitops.h>
- #include <linux/delay.h>
-+#include <linux/nsproxy.h>
-+#include <linux/ve.h>
-
- #include <asm/uaccess.h>
- #include <asm/system.h>
@@ -104,6 +106,7 @@

#include <linux/kmod.h>
@@ -91574,3 +91565,16 @@ Index: kernel/sound/core/info.c
}
return 0;
}
+diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
+index e85486b..8171d69 100644
+--- a/drivers/char/tty_io.c
++++ b/drivers/char/tty_io.c
+@@ -94,6 +94,8 @@
+ #include <linux/wait.h>
+ #include <linux/bitops.h>
+ #include <linux/delay.h>
++#include <linux/nsproxy.h>
++#include <linux/ve.h>
+ #include <linux/serial.h>
+
+ #include <asm/uaccess.h>
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 23d7cfd..4f29a78 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -94,6 +94,7 @@
#include <linux/wait.h>
#include <linux/bitops.h>
#include <linux/delay.h>
+#include <linux/serial.h>

#include <asm/uaccess.h>
#include <asm/system.h>
@@ -3302,6 +3303,20 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
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..
*/
@@ -3435,6 +3450,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;
case TCFLSH:
switch (arg) {
case TCIFLUSH:
@@ -3849,6 +3870,7 @@ void tty_set_operations(struct tty_driver *driver,
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 3bb5d24..472a098 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -1022,10 +1022,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;

@@ -1033,19 +1033,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;
}

/*
@@ -1098,10 +1098,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)
@@ -2197,6 +2193,7 @@ static const 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 d7fae72..11bb54e 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -540,6 +540,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
@@ -1145,6 +1157,7 @@ static const 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/include/linux/tty_driver.h b/include/linux/tty_driver.h
index 85c95cd..3ddb342 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -118,11 +118,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 <linux/fs.h>
#include <linux/list.h>
#include <linux/cdev.h>
+#include <linux/serial.h> /* for serial_state and serial_icounter_struct */

struct tty_struct;

@@ -157,6 +164,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 {
@@ -220,6 +229,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;
};
diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
index 488ce12..6f44a59 100644
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -243,6 +243,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);
void (*write_int_callback)(struct urb *urb);
--
1.7.0.4


--------------030907070100080804080701
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

--
kernel-team mailing list
kernel-team@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/kernel-team

--------------030907070100080804080701--

Tim Gardner 02-16-2011 07:09 PM

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 <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

(backported from commit d281da7ff6f70efca0553c288bb883e8605b3862)

Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
---
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 <linux/wait.h>
#include <linux/bitops.h>
#include <linux/delay.h>
+#include <linux/serial.h>

#include <asm/uaccess.h>
#include <asm/system.h>
@@ -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 <linux/fs.h>
#include <linux/list.h>
#include <linux/cdev.h>
+#include <linux/serial.h> /* 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


--------------060202040404010205060008
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

--
kernel-team mailing list
kernel-team@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/kernel-team

--------------060202040404010205060008--


All times are GMT. The time now is 07:50 AM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.