Commit message: Staging: sb105x: info leak in mp_get_count()

The icount.reserved[] array isn't initialized so it leaks stack
information to userspace.

Reported-by: Nico Golde <nico@ngolde.de>
Reported-by: Fabian Yamaguchi <fabs@goesec.de>
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
CVE: CVE-2013-4516
CWE: CWE-200
Vulnerability description: The mp_get_count function in drivers/staging/sb105x/sb_pci_mp.c in the Linux kernel before 3.12 does not initialize a certain data structure, which allows local users to obtain sensitive information from kernel stack memory via a TIOCGICOUNT ioctl call.

Project: linux

static void mp_change_pm(struct sb_uart_state *state, int pm_state)
{
	struct sb_uart_port *port = state->port;
	if (port->ops->pm)
		port->ops->pm(port, pm_state, state->pm_state);
	state->pm_state = pm_state;
}

Project: linux

static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port)
{
	unsigned int flags;


	if (!port->iobase && !port->mapbase && !port->membase)
	{
		DPRINTK("%s error \n",__FUNCTION__);
		return;
	}
	flags = UART_CONFIG_TYPE;
	if (port->flags & UPF_AUTO_IRQ)
		flags |= UART_CONFIG_IRQ;
	if (port->flags & UPF_BOOT_AUTOCONF) {
		port->type = PORT_UNKNOWN;
		port->ops->config_port(port, flags);
	}

	if (port->type != PORT_UNKNOWN) {
		unsigned long flags;

		mp_report_port(drv, port);

		spin_lock_irqsave(&port->lock, flags);
		port->ops->set_mctrl(port, 0);
		spin_unlock_irqrestore(&port->lock, flags);

		mp_change_pm(state, 3);
	}
}

Project: linux

static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port)
{
	char address[64];

	switch (port->iotype) {
		case UPIO_PORT:
			snprintf(address, sizeof(address),"I/O 0x%x", port->iobase);
			break;
		case UPIO_HUB6:
			snprintf(address, sizeof(address),"I/O 0x%x offset 0x%x", port->iobase, port->hub6);
			break;
		case UPIO_MEM:
			snprintf(address, sizeof(address),"MMIO 0x%lx", port->mapbase);
			break;
		default:
			snprintf(address, sizeof(address),"*unknown*" );
			strlcpy(address, "*unknown*", sizeof(address));
			break;
	}

	printk( "%s%d at %s (irq = %d) is a %s\n",
			drv->dev_name, port->line, address, port->irq, mp_type(port));

}

Project: linux

static const char *mp_type(struct sb_uart_port *port)
{
	const char *str = NULL;

	if (port->ops->type)
		str = port->ops->type(port);

	if (!str)
		str = "unknown";

	return str;
}

Project: linux

static void multi_release_std_resource(struct mp_port *mtpt)
{
	unsigned int size = 8 << mtpt->port.regshift;

	switch (mtpt->port.iotype) {
		case UPIO_MEM:
			if (!mtpt->port.mapbase)
				break;

			if (mtpt->port.flags & UPF_IOREMAP) {
				iounmap(mtpt->port.membase);
				mtpt->port.membase = NULL;
			}

			release_mem_region(mtpt->port.mapbase, size);
			break;

		case UPIO_HUB6:
		case UPIO_PORT:
			release_region(mtpt->port.iobase,size);
			break;
	}
}

Project: linux

static int multi_startup(struct sb_uart_port *port)
{
	struct mp_port *mtpt = (struct mp_port *)port;
	unsigned long flags;
	int retval;

	mtpt->capabilities = uart_config[mtpt->port.type].flags;
	mtpt->mcr = 0;

	if (mtpt->capabilities & UART_CLEAR_FIFO) {
		serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO);
		serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO |
				UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
		serial_outp(mtpt, UART_FCR, 0);
	}

	(void) serial_inp(mtpt, UART_LSR);
	(void) serial_inp(mtpt, UART_RX);
	(void) serial_inp(mtpt, UART_IIR);
	(void) serial_inp(mtpt, UART_MSR);
	serial_outp(mtpt, UART_MSR, 0);


	if (!(mtpt->port.flags & UPF_BUGGY_UART) &&
			(serial_inp(mtpt, UART_LSR) == 0xff)) {
		printk("ttyS%d: LSR safety check engaged!\n", mtpt->port.line);
	}

	if ((!is_real_interrupt(mtpt->port.irq)) || (mtpt->poll_type==TYPE_POLL)) {
		unsigned int timeout = mtpt->port.timeout;

		timeout = timeout > 6 ? (timeout / 2 - 2) : 1;

		mtpt->timer.data = (unsigned long)mtpt;
		mod_timer(&mtpt->timer, jiffies + timeout);
	} 
	else 
	{
		retval = serial_link_irq_chain(mtpt);
		if (retval)
			return retval;
	}

	serial_outp(mtpt, UART_LCR, UART_LCR_WLEN8);

	spin_lock_irqsave(&mtpt->port.lock, flags);
	if ((is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_INTERRUPT))
		mtpt->port.mctrl |= TIOCM_OUT2;

	multi_set_mctrl(&mtpt->port, mtpt->port.mctrl);
	spin_unlock_irqrestore(&mtpt->port.lock, flags);

	
	mtpt->ier = UART_IER_RLSI | UART_IER_RDI;
	serial_outp(mtpt, UART_IER, mtpt->ier);

	(void) serial_inp(mtpt, UART_LSR);
	(void) serial_inp(mtpt, UART_RX);
	(void) serial_inp(mtpt, UART_IIR);
	(void) serial_inp(mtpt, UART_MSR);

	return 0;
}

Project: linux

static int pci_remap_base(struct pci_dev *pcidev, unsigned int offset, 
		unsigned int address, unsigned int size) 
{
#if 0
	struct resource *root;
	unsigned index = (offset - 0x10) >> 2;
#endif

	pci_write_config_dword(pcidev, offset, address);
#if 0
	root = pcidev->resource[index].parent;
	release_resource(&pcidev->resource[index]);
	address &= ~0x1;
	pcidev->resource[index].start = address;
	pcidev->resource[index].end	  = address + size - 1;

	if (request_resource(root, &pcidev->resource[index]) != NULL)
	{
		printk(KERN_ERR "pci remap conflict!! 0x%x\n", address);
		return (-1);
	}
#endif

	return (0);
}

Project: linux

static int sb1054_get_register(struct sb_uart_port *port, int page, int reg)
{
	int ret = 0;
	unsigned int lcr = 0;
	unsigned int mcr = 0;
	unsigned int tmp = 0;

	if( page <= 0)
	{
		printk(" page 0 can not use this fuction\n");
		return -1;
	}

	switch(page)
	{
		case 1:
			lcr = SB105X_GET_LCR(port);
			tmp = lcr | SB105X_LCR_DLAB;
			SB105X_PUT_LCR(port, tmp);

			tmp = SB105X_GET_LCR(port);

			ret = SB105X_GET_REG(port,reg);
			SB105X_PUT_LCR(port,lcr);
			break;
		case 2:
			mcr = SB105X_GET_MCR(port);
			tmp = mcr | SB105X_MCR_P2S;
			SB105X_PUT_MCR(port,tmp);

			ret = SB105X_GET_REG(port,reg);

			SB105X_PUT_MCR(port,mcr);
			break;
		case 3:
			lcr = SB105X_GET_LCR(port);
			tmp = lcr | SB105X_LCR_BF;
			SB105X_PUT_LCR(port,tmp);
			SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P3KEY);

			ret = SB105X_GET_REG(port,reg);

			SB105X_PUT_LCR(port,lcr);
			break;
		case 4:
			lcr = SB105X_GET_LCR(port);
			tmp = lcr | SB105X_LCR_BF;
			SB105X_PUT_LCR(port,tmp);
			SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P4KEY);

			ret = SB105X_GET_REG(port,reg);

			SB105X_PUT_LCR(port,lcr);
			break;
		default:
			printk(" error invalid page number \n");
			return -1;
	}

	return ret;
}

Project: linux

static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt)
{
	spin_lock_irq(&i->lock);

	if (!list_empty(i->head)) {
		if (i->head == &mtpt->list)
			i->head = i->head->next;
		list_del(&mtpt->list);
	} else {
		i->head = NULL;
	}

	spin_unlock_irq(&i->lock);
}

Project: linux

static void serial_unlink_irq_chain(struct mp_port *mtpt)
{
	struct irq_info *i = irq_lists + mtpt->port.irq;

	if (list_empty(i->head))
	{
		free_irq(mtpt->port.irq, i);
	}
	serial_do_unlink(i, mtpt);
}