mbox

Karmic CVE: tty: Make tiocgicount a handler, CVE-2010-4076

Message ID 20110217180043.9D53DF89F8@sepang.rtg.net
State Accepted
Headers show

Pull-request

git://kernel.ubuntu.com/rtg/ubuntu-karmic.git CVE-2010-4076

Message

Tim Gardner Feb. 17, 2011, 6 p.m. UTC
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@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@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(-)

Comments

Stefan Bader Feb. 18, 2011, 1:10 p.m. UTC | #1
On 02/17/2011 07:00 PM, 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@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@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>
Acked-by: Stefan Bader <stefan.bader@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);
Brad Figg Feb. 22, 2011, 4:24 p.m. UTC | #2
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@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@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);

Acked-by: Brad Figg <brad.figg@canonical.com>
Tim Gardner Feb. 22, 2011, 5:17 p.m. UTC | #3
applied