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.resume = rndis_qc_resume;
|
||||||
rndis->func.free_func = rndis_qc_free;
|
rndis->func.free_func = rndis_qc_free;
|
||||||
|
|
||||||
_rndis_qc = rndis;
|
|
||||||
|
|
||||||
status = rndis_ipa_init(&rndis_ipa_params);
|
status = rndis_ipa_init(&rndis_ipa_params);
|
||||||
if (status) {
|
if (status) {
|
||||||
pr_err("%s: failed to init rndis_ipa\n", __func__);
|
pr_err("%s: failed to init rndis_ipa\n", __func__);
|
||||||
kfree(rndis);
|
goto fail;
|
||||||
return ERR_PTR(status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_rndis_qc = rndis;
|
||||||
|
|
||||||
return &rndis->func;
|
return &rndis->func;
|
||||||
|
fail:
|
||||||
|
kfree(rndis);
|
||||||
|
_rndis_qc = NULL;
|
||||||
|
return ERR_PTR(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct usb_function *qcrndis_alloc(struct usb_function_instance *fi)
|
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)
|
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;
|
||||||
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)
|
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;
|
||||||
|
|
||||||
|
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);
|
pr_info("Received command %d\n", cmd);
|
||||||
|
|
||||||
if (rndis_qc_lock(&rndis->ioctl_excl))
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1385,11 +1430,11 @@ static int qcrndis_set_inst_name(struct usb_function_instance *fi,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_init(&rndis_lock);
|
||||||
opts->rndis = rndis;
|
opts->rndis = rndis;
|
||||||
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 = ipa_data_setup(USB_IPA_FUNC_RNDIS);
|
ret = ipa_data_setup(USB_IPA_FUNC_RNDIS);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue