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

Commit e0b76d0b authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents ffd42993 c0b8e888
Loading
Loading
Loading
Loading
+64 −21
Original line number Original line 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.suspend = rndis_qc_suspend;
	rndis->port.func.resume = rndis_qc_resume;
	rndis->port.func.resume = rndis_qc_resume;


	_rndis_qc = rndis;

	if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) {
	if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) {
		status = rndis_ipa_init(&rndis_ipa_params);
		status = rndis_ipa_init(&rndis_ipa_params);
		if (status) {
		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);
			rndis_ipa_cleanup(rndis_ipa_params.private);
		goto fail;
		goto fail;
	}
	}

	_rndis_qc = rndis;

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


	return 0;
	return 0;
@@ -1317,74 +1318,115 @@ fail:


static int rndis_qc_open_dev(struct inode *ip, struct file *fp)
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");
	pr_info("Open rndis QC driver\n");


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


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


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

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


	return 0;
	return ret;
}
}


static int rndis_qc_release_dev(struct inode *ip, struct file *fp)
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");
	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;
	return 0;
}
}


static long rndis_qc_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
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;
	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))
	qc_max_pkt_per_xfer = _rndis_qc->ul_max_pkt_per_xfer;
		return -EBUSY;
	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) {
	switch (cmd) {
	case RNDIS_QC_GET_MAX_PKT_PER_XFER:
	case RNDIS_QC_GET_MAX_PKT_PER_XFER:
		ret = copy_to_user((void __user *)arg,
		ret = copy_to_user((void __user *)arg,
					&rndis->ul_max_pkt_per_xfer,
					&qc_max_pkt_per_xfer,
					sizeof(rndis->ul_max_pkt_per_xfer));
					sizeof(qc_max_pkt_per_xfer));
		if (ret) {
		if (ret) {
			pr_err("copying to user space failed\n");
			pr_err("copying to user space failed\n");
			ret = -EFAULT;
			ret = -EFAULT;
		}
		}
		pr_info("Sent UL max packets per xfer %d\n",
		pr_info("Sent UL max packets per xfer %d\n",
				rndis->ul_max_pkt_per_xfer);
				qc_max_pkt_per_xfer);
		break;
		break;
	case RNDIS_QC_GET_MAX_PKT_SIZE:
	case RNDIS_QC_GET_MAX_PKT_SIZE:
		ret = copy_to_user((void __user *)arg,
		ret = copy_to_user((void __user *)arg,
					&rndis->max_pkt_size,
					&qc_max_pkt_size,
					sizeof(rndis->max_pkt_size));
					sizeof(qc_max_pkt_size));
		if (ret) {
		if (ret) {
			pr_err("copying to user space failed\n");
			pr_err("copying to user space failed\n");
			ret = -EFAULT;
			ret = -EFAULT;
		}
		}
		pr_debug("Sent max packet size %d\n",
		pr_debug("Sent max packet size %d\n",
				rndis->max_pkt_size);
				qc_max_pkt_size);
		break;
		break;
	default:
	default:
		pr_err("Unsupported IOCTL\n");
		pr_err("Unsupported IOCTL\n");
		ret = -EINVAL;
		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;
	return ret;
}
}


@@ -1407,10 +1449,11 @@ static int rndis_qc_init(void)


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


	spin_lock_init(&rndis_lock);

	ret = misc_register(&rndis_qc_device);
	ret = misc_register(&rndis_qc_device);
	if (ret)
	if (ret)
		pr_err("rndis QC driver failed to register\n");
		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);
	ret = bam_data_setup(USB_FUNC_RNDIS, RNDIS_QC_NO_PORTS);
	if (ret) {
	if (ret) {