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 <vvreddy@codeaurora.org>
Signed-off-by: Chandana Kishori Chiluveru <cchiluve@codeaurora.org>
This commit is contained in:
Vijayavardhan Vennapusa 2016-03-24 17:34:21 +05:30 committed by Gerrit - the friendly Code Review server
parent 3162449f7d
commit fc2fd4a788

View file

@ -472,16 +472,26 @@ static void rndis_qc_response_available(void *_rndis)
static void rndis_qc_response_complete(struct usb_ep *ep, static void rndis_qc_response_complete(struct usb_ep *ep,
struct usb_request *req) struct usb_request *req)
{ {
struct f_rndis_qc *rndis = req->context; struct f_rndis_qc *rndis;
int status = req->status; int status = req->status;
struct usb_composite_dev *cdev; 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) { if (!rndis->func.config || !rndis->func.config->cdev) {
pr_err("%s(): cdev or config is NULL.\n", __func__); pr_err("%s(): cdev or config is NULL.\n", __func__);
spin_unlock(&rndis_lock);
return; return;
} }
cdev = rndis->func.config->cdev; cdev = rndis->func.config->cdev;
/* after TX: /* after TX:
* - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
* - RNDIS_RESPONSE_AVAILABLE (status/irq) * - RNDIS_RESPONSE_AVAILABLE (status/irq)
@ -491,7 +501,7 @@ static void rndis_qc_response_complete(struct usb_ep *ep,
case -ESHUTDOWN: case -ESHUTDOWN:
/* connection gone */ /* connection gone */
atomic_set(&rndis->notify_count, 0); atomic_set(&rndis->notify_count, 0);
break; goto out;
default: default:
pr_info("RNDIS %s response error %d, %d/%d\n", pr_info("RNDIS %s response error %d, %d/%d\n",
ep->name, status, ep->name, status,
@ -499,30 +509,47 @@ static void rndis_qc_response_complete(struct usb_ep *ep,
/* FALLTHROUGH */ /* FALLTHROUGH */
case 0: case 0:
if (ep != rndis->notify) if (ep != rndis->notify)
break; goto out;
/* handle multiple pending RNDIS_RESPONSE_AVAILABLE /* handle multiple pending RNDIS_RESPONSE_AVAILABLE
* notifications by resending until we're done * notifications by resending until we're done
*/ */
if (atomic_dec_and_test(&rndis->notify_count)) if (atomic_dec_and_test(&rndis->notify_count))
break; goto out;
status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); notify_ep = rndis->notify;
spin_unlock(&rndis_lock);
status = usb_ep_queue(notify_ep, req, GFP_ATOMIC);
if (status) { 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); 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, static void rndis_qc_command_complete(struct usb_ep *ep,
struct usb_request *req) struct usb_request *req)
{ {
struct f_rndis_qc *rndis = req->context; struct f_rndis_qc *rndis;
int status; int status;
rndis_init_msg_type *buf; rndis_init_msg_type *buf;
u32 ul_max_xfer_size, dl_max_xfer_size; 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 */ /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
status = rndis_msg_parser(rndis->params, (u8 *) req->buf); status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
if (status < 0) 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); rndis_get_dl_max_xfer_size(rndis->params);
ipa_data_set_dl_max_xfer_size(dl_max_xfer_size); ipa_data_set_dl_max_xfer_size(dl_max_xfer_size);
} }
spin_unlock(&rndis_lock);
} }
static int 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 f_rndis_qc *rndis = func_to_rndis_qc(f);
struct usb_composite_dev *cdev = f->config->cdev; struct usb_composite_dev *cdev = f->config->cdev;
unsigned long flags;
if (!rndis->notify->driver_data) if (!rndis->notify->driver_data)
return; return;
DBG(cdev, "rndis deactivated\n"); DBG(cdev, "rndis deactivated\n");
spin_lock_irqsave(&rndis_lock, flags);
rndis_uninit(rndis->params); rndis_uninit(rndis->params);
spin_unlock_irqrestore(&rndis_lock, flags);
ipa_data_disconnect(&rndis->bam_port, USB_IPA_FUNC_RNDIS); ipa_data_disconnect(&rndis->bam_port, USB_IPA_FUNC_RNDIS);
msm_ep_unconfig(rndis->bam_port.out); 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) void rndis_ipa_reset_trigger(void)
{ {
struct f_rndis_qc *rndis; struct f_rndis_qc *rndis;
unsigned long flags;
spin_lock_irqsave(&rndis_lock, flags);
rndis = _rndis_qc; rndis = _rndis_qc;
if (!rndis) { if (!rndis) {
pr_err("%s: No RNDIS instance", __func__); pr_err("%s: No RNDIS instance", __func__);
spin_unlock_irqrestore(&rndis_lock, flags);
return; return;
} }
rndis->net_ready_trigger = false; rndis->net_ready_trigger = false;
spin_unlock_irqrestore(&rndis_lock, flags);
} }
/* /*