Commit message: Staging: bcm: info leak in ioctl

The DevInfo.u32Reserved[] array isn't initialized so it leaks kernel
information to user space.

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-4515
CWE: CWE-200
Vulnerability description: The bcm_char_ioctl function in drivers/staging/bcm/Bcmchar.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 memory via an IOCTL_BCM_GET_DEVICE_DRIVER_INFO ioctl call.

Project: linux
File: Bcmchar.c

static int bcm_char_open(struct inode *inode, struct file *filp)
{
	struct bcm_mini_adapter *Adapter = NULL;
	struct bcm_tarang_data *pTarang = NULL;

	Adapter = GET_BCM_ADAPTER(gblpnetdev);
	pTarang = kzalloc(sizeof(struct bcm_tarang_data), GFP_KERNEL);
	if (!pTarang)
		return -ENOMEM;

	pTarang->Adapter = Adapter;
	pTarang->RxCntrlMsgBitMask = 0xFFFFFFFF & ~(1 << 0xB);

	down(&Adapter->RxAppControlQueuelock);
	pTarang->next = Adapter->pTarangs;
	Adapter->pTarangs = pTarang;
	up(&Adapter->RxAppControlQueuelock);

	/* Store the Adapter structure */
	filp->private_data = pTarang;

	/* Start Queuing the control response Packets */
	atomic_inc(&Adapter->ApplicationRunning);

	nonseekable_open(inode, filp);
	return 0;
}

Project: linux
File: Bcmchar.c

static ssize_t bcm_char_read(struct file *filp, char __user *buf, size_t size,
			     loff_t *f_pos)
{
	struct bcm_tarang_data *pTarang = filp->private_data;
	struct bcm_mini_adapter *Adapter = pTarang->Adapter;
	struct sk_buff *Packet = NULL;
	ssize_t PktLen = 0;
	int wait_ret_val = 0;
	unsigned long ret = 0;

	wait_ret_val = wait_event_interruptible(Adapter->process_read_wait_queue,
						(pTarang->RxAppControlHead ||
						 Adapter->device_removed));
	if ((wait_ret_val == -ERESTARTSYS)) {
		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, OSAL_DBG, DBG_LVL_ALL,
				"Exiting as i've been asked to exit!!!\n");
		return wait_ret_val;
	}

	if (Adapter->device_removed) {
		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, OSAL_DBG, DBG_LVL_ALL,
				"Device Removed... Killing the Apps...\n");
		return -ENODEV;
	}

	if (FALSE == Adapter->fw_download_done)
		return -EACCES;

	down(&Adapter->RxAppControlQueuelock);

	if (pTarang->RxAppControlHead) {
		Packet = pTarang->RxAppControlHead;
		DEQUEUEPACKET(pTarang->RxAppControlHead,
			      pTarang->RxAppControlTail);
		pTarang->AppCtrlQueueLen--;
	}

	up(&Adapter->RxAppControlQueuelock);

	if (Packet) {
		PktLen = Packet->len;
		ret = copy_to_user(buf, Packet->data,
				   min_t(size_t, PktLen, size));
		if (ret) {
			dev_kfree_skb(Packet);
			BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0,
					"Returning from copy to user failure\n");
			return -EFAULT;
		}
		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, OSAL_DBG, DBG_LVL_ALL,
				"Read %zd Bytes From Adapter packet = %p by process %d!\n",
				PktLen, Packet, current->pid);
		dev_kfree_skb(Packet);
	}

	BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, OSAL_DBG, DBG_LVL_ALL, "<\n");
	return PktLen;
}

Project: linux
File: Bcmchar.c

static int bcm_char_release(struct inode *inode, struct file *filp)
{
	struct bcm_tarang_data *pTarang, *tmp, *ptmp;
	struct bcm_mini_adapter *Adapter = NULL;
	struct sk_buff *pkt, *npkt;

	pTarang = (struct bcm_tarang_data *)filp->private_data;

	if (pTarang == NULL) {
		BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0,
				"ptarang is null\n");
		return 0;
	}

	Adapter = pTarang->Adapter;

	down(&Adapter->RxAppControlQueuelock);

	tmp = Adapter->pTarangs;
	for (ptmp = NULL; tmp; ptmp = tmp, tmp = tmp->next) {
		if (tmp == pTarang)
			break;
	}

	if (tmp) {
		if (!ptmp)
			Adapter->pTarangs = tmp->next;
		else
			ptmp->next = tmp->next;
	} else {
		up(&Adapter->RxAppControlQueuelock);
		return 0;
	}

	pkt = pTarang->RxAppControlHead;
	while (pkt) {
		npkt = pkt->next;
		kfree_skb(pkt);
		pkt = npkt;
	}

	up(&Adapter->RxAppControlQueuelock);

	/* Stop Queuing the control response Packets */
	atomic_dec(&Adapter->ApplicationRunning);

	kfree(pTarang);

	/* remove this filp from the asynchronously notified filp's */
	filp->private_data = NULL;
	return 0;
}

Project: linux
File: Bcmchar.c

int register_control_device_interface(struct bcm_mini_adapter *Adapter)
{

	if (Adapter->major > 0)
		return Adapter->major;

	Adapter->major = register_chrdev(0, DEV_NAME, &bcm_fops);
	if (Adapter->major < 0) {
		pr_err(DRV_NAME ": could not created character device\n");
		return Adapter->major;
	}

	Adapter->pstCreatedClassDevice = device_create(bcm_class, NULL,
						MKDEV(Adapter->major, 0),
						Adapter, DEV_NAME);

	if (IS_ERR(Adapter->pstCreatedClassDevice)) {
		pr_err(DRV_NAME ": class device create failed\n");
		unregister_chrdev(Adapter->major, DEV_NAME);
		return PTR_ERR(Adapter->pstCreatedClassDevice);
	}

	return 0;
}

Project: linux
File: Bcmchar.c

void unregister_control_device_interface(struct bcm_mini_adapter *Adapter)
{
	if (Adapter->major > 0) {
		device_destroy(bcm_class, MKDEV(Adapter->major, 0));
		unregister_chrdev(Adapter->major, DEV_NAME);
	}
}