summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@quicinc.com>2017-05-25 15:51:53 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2017-05-25 15:51:53 -0700
commiteadd4e31817140be75591941d446f07a30ff295c (patch)
treed0dfbe8a461ac338449776ac45e591461550ab1d /drivers/usb
parent7217c5a9c14bba72f28fcea056aa759e7f0280c8 (diff)
parent1509025cbfb80e50080f7a7cfb0fc828277c1820 (diff)
Merge "USB: f_qc_rndis: Prevent use-after-free for _rndis_qc"
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/gadget/function/f_qc_rndis.c91
1 files changed, 68 insertions, 23 deletions
diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c
index 45c39d3c4225..434af820e827 100644
--- a/drivers/usb/gadget/function/f_qc_rndis.c
+++ b/drivers/usb/gadget/function/f_qc_rndis.c
@@ -1245,16 +1245,19 @@ usb_function *rndis_qc_bind_config_vendor(struct usb_function_instance *fi,
rndis->func.resume = rndis_qc_resume;
rndis->func.free_func = rndis_qc_free;
- _rndis_qc = rndis;
-
status = rndis_ipa_init(&rndis_ipa_params);
if (status) {
pr_err("%s: failed to init rndis_ipa\n", __func__);
- kfree(rndis);
- return ERR_PTR(status);
+ goto fail;
}
+ _rndis_qc = rndis;
+
return &rndis->func;
+fail:
+ kfree(rndis);
+ _rndis_qc = NULL;
+ return ERR_PTR(status);
}
static struct usb_function *qcrndis_alloc(struct usb_function_instance *fi)
@@ -1264,74 +1267,116 @@ static struct usb_function *qcrndis_alloc(struct usb_function_instance *fi)
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;
- pr_info("rndis QC file opened\n");
+fail:
+ spin_unlock_irqrestore(&rndis_lock, flags);
- return 0;
+ if (!ret)
+ pr_info("rndis QC file opened\n");
+
+ 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;
+ }
+
+ 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);
- if (rndis_qc_lock(&rndis->ioctl_excl))
- return -EBUSY;
+ 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;
}
@@ -1385,11 +1430,11 @@ static int qcrndis_set_inst_name(struct usb_function_instance *fi,
return -ENOMEM;
}
+ spin_lock_init(&rndis_lock);
opts->rndis = rndis;
ret = misc_register(&rndis_qc_device);
if (ret)
pr_err("rndis QC driver failed to register\n");
- spin_lock_init(&rndis_lock);
ret = ipa_data_setup(USB_IPA_FUNC_RNDIS);
if (ret) {