Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1fd19997 authored by Pratham Pratap's avatar Pratham Pratap Committed by Gerrit - the friendly Code Review server
Browse files

USB: f_qc_rndis: Prevent use-after-free for _rndis_qc



Assume that there are two threads, thread1 is setting
value of _rndis_qc variable in rndis_qc_bind_config_vendor
function. Thread2 jumps in and get the value of _rndis_qc
in rndis_qc_open_dev function before it is freed in
rndis_qc_bind_config_vendor function, since rndis_ipa_init
or usb_add_function failed. Use-after-free will happen as
Thread2 is referencing freed objects. To prevent this
spinlock is used where ever it is needed to protect
_rndis_qc variable.

Change-Id: Ibfe10cedc18bcb19dd01cd2bec43a5554fd008bc
Signed-off-by: default avatarPratham Pratap <prathampratap@codeaurora.org>
parent 25d15ba1
Loading
Loading
Loading
Loading
+64 −22
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
 * Copyright (C) 2008 Nokia Corporation
 * Copyright (C) 2009 Samsung Electronics
 *			Author: Michal Nazarewicz (mina86@mina86.com)
 * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
@@ -1301,8 +1301,6 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
	rndis->port.func.suspend = rndis_qc_suspend;
	rndis->port.func.resume = rndis_qc_resume;

	_rndis_qc = rndis;

	if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) {
		status = rndis_ipa_init(&rndis_ipa_params);
		if (status) {
@@ -1318,6 +1316,8 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
		goto fail;
	}

	_rndis_qc = rndis;

	return 0;

fail:
@@ -1329,74 +1329,115 @@ fail:

static int rndis_qc_open_dev(struct inode *ip, struct file *fp)
{
	int ret = 0;
	unsigned long flags;
	pr_info("Open rndis QC driver\n");

	spin_lock_irqsave(&rndis_lock, flags);
	if (!_rndis_qc) {
		pr_err("rndis_qc_dev not created yet\n");
		return -ENODEV;
		ret = -ENODEV;
		goto fail;
	}

	if (rndis_qc_lock(&_rndis_qc->open_excl)) {
		pr_err("Already opened\n");
		return -EBUSY;
		ret = -EBUSY;
		goto fail;
	}

	fp->private_data = _rndis_qc;
fail:
	spin_unlock_irqrestore(&rndis_lock, flags);

	if (!ret)
		pr_info("rndis QC file opened\n");

	return 0;
	return ret;
}

static int rndis_qc_release_dev(struct inode *ip, struct file *fp)
{
	struct f_rndis_qc	*rndis = fp->private_data;

	unsigned long flags;
	pr_info("Close rndis QC file\n");
	rndis_qc_unlock(&rndis->open_excl);

	spin_lock_irqsave(&rndis_lock, flags);

	if (!_rndis_qc) {
		pr_err("rndis_qc_dev not present\n");
		spin_unlock_irqrestore(&rndis_lock, flags);
		return -ENODEV;
	}
	rndis_qc_unlock(&_rndis_qc->open_excl);
	spin_unlock_irqrestore(&rndis_lock, flags);
	return 0;
}

static long rndis_qc_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
{
	struct f_rndis_qc	*rndis = fp->private_data;
	u8 qc_max_pkt_per_xfer = 0;
	u32 qc_max_pkt_size = 0;
	int ret = 0;
	unsigned long flags;

	pr_info("Received command %d\n", cmd);
	spin_lock_irqsave(&rndis_lock, flags);
	if (!_rndis_qc) {
		pr_err("rndis_qc_dev not present\n");
		ret = -ENODEV;
		goto fail;
	}

	if (rndis_qc_lock(&rndis->ioctl_excl))
		return -EBUSY;
	qc_max_pkt_per_xfer = _rndis_qc->ul_max_pkt_per_xfer;
	qc_max_pkt_size = _rndis_qc->max_pkt_size;

	if (rndis_qc_lock(&_rndis_qc->ioctl_excl)) {
		ret = -EBUSY;
		goto fail;
	}

	spin_unlock_irqrestore(&rndis_lock, flags);

	pr_info("Received command %d\n", cmd);

	switch (cmd) {
	case RNDIS_QC_GET_MAX_PKT_PER_XFER:
		ret = copy_to_user((void __user *)arg,
					&rndis->ul_max_pkt_per_xfer,
					sizeof(rndis->ul_max_pkt_per_xfer));
					&qc_max_pkt_per_xfer,
					sizeof(qc_max_pkt_per_xfer));
		if (ret) {
			pr_err("copying to user space failed\n");
			ret = -EFAULT;
		}
		pr_info("Sent UL max packets per xfer %d\n",
				rndis->ul_max_pkt_per_xfer);
				qc_max_pkt_per_xfer);
		break;
	case RNDIS_QC_GET_MAX_PKT_SIZE:
		ret = copy_to_user((void __user *)arg,
					&rndis->max_pkt_size,
					sizeof(rndis->max_pkt_size));
					&qc_max_pkt_size,
					sizeof(qc_max_pkt_size));
		if (ret) {
			pr_err("copying to user space failed\n");
			ret = -EFAULT;
		}
		pr_debug("Sent max packet size %d\n",
				rndis->max_pkt_size);
				qc_max_pkt_size);
		break;
	default:
		pr_err("Unsupported IOCTL\n");
		ret = -EINVAL;
	}

	rndis_qc_unlock(&rndis->ioctl_excl);
	spin_lock_irqsave(&rndis_lock, flags);

	if (!_rndis_qc) {
		pr_err("rndis_qc_dev not present\n");
		ret = -ENODEV;
		goto fail;
	}
	rndis_qc_unlock(&_rndis_qc->ioctl_excl);

fail:
	spin_unlock_irqrestore(&rndis_lock, flags);
	return ret;
}

@@ -1419,10 +1460,11 @@ static int rndis_qc_init(void)

	pr_info("initialize rndis QC instance\n");

	spin_lock_init(&rndis_lock);

	ret = misc_register(&rndis_qc_device);
	if (ret)
		pr_err("rndis QC driver failed to register\n");
	spin_lock_init(&rndis_lock);

	ret = bam_data_setup(RNDIS_QC_NO_PORTS);
	if (ret) {