Merge "USB: f_qc_rndis: Prevent use-after-free for _rndis_qc"
This commit is contained in:
commit
eadd4e3181
1 changed files with 68 additions and 23 deletions
|
@ -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;
|
||||
|
||||
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);
|
||||
|
||||
pr_info("Received command %d\n", cmd);
|
||||
|
||||
if (rndis_qc_lock(&rndis->ioctl_excl))
|
||||
return -EBUSY;
|
||||
|
||||
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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue