diff options
| author | Mayank Rana <mrana@codeaurora.org> | 2016-04-11 16:32:48 -0700 |
|---|---|---|
| committer | Jeevan Shriram <jshriram@codeaurora.org> | 2016-04-18 17:06:17 -0700 |
| commit | 96fc91d1663957226113a0e19187f4fe9099c5d4 (patch) | |
| tree | 1a8901e99158b79b5db9706811a4d0b5dd755fc9 | |
| parent | 8266c2d117a731a3bb1ecf93b6198d4754e864b0 (diff) | |
usb: gadget: f_diag: Fix list corruption due to diag_context freed
While performing USB composition switch or adb is being killed,
list_add corruption related crash is seen when SLUB_DEBUG is
enabled. diag_function_unbind() API decrements kref count and when
it becomes zero, it is calling diag_context_release() which frees
diag_context. This list corruption is seen from purge_configfs_funcs()
API which is trying to move function list as part of func->list. Fix
this issue by releasing diag_context with free_func() instead of
diag_function_unbind().
CRs-Fixed: 1002041
Change-Id: Ie49e47f2a0f26144e0107759fedc67c3af80032c
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
| -rw-r--r-- | drivers/usb/gadget/function/f_diag.c | 22 |
1 files changed, 13 insertions, 9 deletions
diff --git a/drivers/usb/gadget/function/f_diag.c b/drivers/usb/gadget/function/f_diag.c index 7a451b77c4d0..87939140f82e 100644 --- a/drivers/usb/gadget/function/f_diag.c +++ b/drivers/usb/gadget/function/f_diag.c @@ -657,7 +657,16 @@ static void diag_function_disable(struct usb_function *f) static void diag_free_func(struct usb_function *f) { - kfree(func_to_diag(f)); + struct diag_context *ctxt = func_to_diag(f); + unsigned long flags; + + spin_lock_irqsave(&ctxt->lock, flags); + list_del(&ctxt->list_item); + if (kref_put(&ctxt->kref, diag_context_release)) + /* diag_context_release called spin_unlock already */ + local_irq_restore(flags); + else + spin_unlock_irqrestore(&ctxt->lock, flags); } static int diag_function_set_alt(struct usb_function *f, @@ -734,16 +743,11 @@ static void diag_function_unbind(struct usb_configuration *c, */ if (ctxt->ch && ctxt->ch->priv_usb == ctxt) ctxt->ch->priv_usb = NULL; - list_del(&ctxt->list_item); - /* Free any pending USB requests from last session */ + spin_lock_irqsave(&ctxt->lock, flags); + /* Free any pending USB requests from last session */ free_reqs(ctxt); - - if (kref_put(&ctxt->kref, diag_context_release)) - /* diag_context_release called spin_unlock already */ - local_irq_restore(flags); - else - spin_unlock_irqrestore(&ctxt->lock, flags); + spin_unlock_irqrestore(&ctxt->lock, flags); } static int diag_function_bind(struct usb_configuration *c, |
