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

Commit c0b8e888 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 0d254b9b
Loading
Loading
Loading
Loading
+64 −21
Original line number Diff line number Diff line
@@ -1289,8 +1289,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) {
@@ -1305,6 +1303,9 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
			rndis_ipa_cleanup(rndis_ipa_params.private);
		goto fail;
	}

	_rndis_qc = rndis;

	c->cdev->gadget->bam2bam_func_enabled = true;

	return 0;
@@ -1317,74 +1318,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;
}

@@ -1407,10 +1449,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(USB_FUNC_RNDIS, RNDIS_QC_NO_PORTS);
	if (ret) {