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

This commit is contained in:
Linux Build Service Account 2017-05-25 15:51:53 -07:00 committed by Gerrit - the friendly Code Review server
commit eadd4e3181

View file

@ -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) {