From fc2fd4a78809e3d03b38fbb36c0135bb488652e0 Mon Sep 17 00:00:00 2001 From: Vijayavardhan Vennapusa Date: Thu, 24 Mar 2016 17:34:21 +0530 Subject: [PATCH] USB: f_qc_rndis: Add spinlock protection whereever required Add spinlock protection in rndis_qc_command_complete() and rndis_qc_response_complete() to avoid races with disable and unbind() function calls. Otherwise it results in crash or using freed memory. Change-Id: Ida99de70a541ba12a8a8610b1c6fa717e42d865c Signed-off-by: Vijayavardhan Vennapusa Signed-off-by: Chandana Kishori Chiluveru --- drivers/usb/gadget/function/f_qc_rndis.c | 51 ++++++++++++++++++------ 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c index eb306529981f..ede1c8dd51a6 100644 --- a/drivers/usb/gadget/function/f_qc_rndis.c +++ b/drivers/usb/gadget/function/f_qc_rndis.c @@ -472,16 +472,26 @@ static void rndis_qc_response_available(void *_rndis) static void rndis_qc_response_complete(struct usb_ep *ep, struct usb_request *req) { - struct f_rndis_qc *rndis = req->context; + struct f_rndis_qc *rndis; int status = req->status; struct usb_composite_dev *cdev; + struct usb_ep *notify_ep; + + spin_lock(&rndis_lock); + rndis = _rndis_qc; + if (!rndis || !rndis->notify || !rndis->notify->driver_data) { + spin_unlock(&rndis_lock); + return; + } if (!rndis->func.config || !rndis->func.config->cdev) { pr_err("%s(): cdev or config is NULL.\n", __func__); + spin_unlock(&rndis_lock); return; } cdev = rndis->func.config->cdev; + /* after TX: * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) * - RNDIS_RESPONSE_AVAILABLE (status/irq) @@ -491,7 +501,7 @@ static void rndis_qc_response_complete(struct usb_ep *ep, case -ESHUTDOWN: /* connection gone */ atomic_set(&rndis->notify_count, 0); - break; + goto out; default: pr_info("RNDIS %s response error %d, %d/%d\n", ep->name, status, @@ -499,30 +509,47 @@ static void rndis_qc_response_complete(struct usb_ep *ep, /* FALLTHROUGH */ case 0: if (ep != rndis->notify) - break; + goto out; /* handle multiple pending RNDIS_RESPONSE_AVAILABLE * notifications by resending until we're done */ if (atomic_dec_and_test(&rndis->notify_count)) - break; - status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); + goto out; + notify_ep = rndis->notify; + spin_unlock(&rndis_lock); + status = usb_ep_queue(notify_ep, req, GFP_ATOMIC); if (status) { - atomic_dec(&rndis->notify_count); + spin_lock(&rndis_lock); + if (!_rndis_qc) + goto out; + atomic_dec(&_rndis_qc->notify_count); DBG(cdev, "notify/1 --> %d\n", status); + spin_unlock(&rndis_lock); } - break; } + + return; + +out: + spin_unlock(&rndis_lock); } static void rndis_qc_command_complete(struct usb_ep *ep, struct usb_request *req) { - struct f_rndis_qc *rndis = req->context; + struct f_rndis_qc *rndis; int status; rndis_init_msg_type *buf; u32 ul_max_xfer_size, dl_max_xfer_size; + spin_lock(&rndis_lock); + rndis = _rndis_qc; + if (!rndis || !rndis->notify || !rndis->notify->driver_data) { + spin_unlock(&rndis_lock); + return; + } + /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ status = rndis_msg_parser(rndis->params, (u8 *) req->buf); if (status < 0) @@ -551,6 +578,7 @@ static void rndis_qc_command_complete(struct usb_ep *ep, rndis_get_dl_max_xfer_size(rndis->params); ipa_data_set_dl_max_xfer_size(dl_max_xfer_size); } + spin_unlock(&rndis_lock); } static int @@ -749,13 +777,16 @@ static void rndis_qc_disable(struct usb_function *f) { struct f_rndis_qc *rndis = func_to_rndis_qc(f); struct usb_composite_dev *cdev = f->config->cdev; + unsigned long flags; if (!rndis->notify->driver_data) return; DBG(cdev, "rndis deactivated\n"); + spin_lock_irqsave(&rndis_lock, flags); rndis_uninit(rndis->params); + spin_unlock_irqrestore(&rndis_lock, flags); ipa_data_disconnect(&rndis->bam_port, USB_IPA_FUNC_RNDIS); msm_ep_unconfig(rndis->bam_port.out); @@ -1092,18 +1123,14 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f) void rndis_ipa_reset_trigger(void) { struct f_rndis_qc *rndis; - unsigned long flags; - spin_lock_irqsave(&rndis_lock, flags); rndis = _rndis_qc; if (!rndis) { pr_err("%s: No RNDIS instance", __func__); - spin_unlock_irqrestore(&rndis_lock, flags); return; } rndis->net_ready_trigger = false; - spin_unlock_irqrestore(&rndis_lock, flags); } /*