From 016895d08e34ab1869826c283e8062cac6938763 Mon Sep 17 00:00:00 2001 From: Manu Gautam Date: Fri, 11 Nov 2016 09:58:16 -0800 Subject: [PATCH] USB: f_fs: Fix disconnect check during ongoing IO F_FS function driver allocated ffs_eps and updates ffs_ep->ep to corresponding usb_ep during func->bind and never clears it. On bind it also saves ffs_ep context in epfile->ep. During func->disable, it clears only ffs_ep context in epfile->ep and on func->unbind it frees ffs_eps memory. ffs_epfile_io routine currently relies on ffs_ep->ep (which is never cleared and ffs_ep could be freed on unbind) to detect any disconnect during active IO. This can result in various issues e.g. use after free use of ffs_ep if unbind finished before epfile_io could resume or "stop adbd" trying to dequeue a freed USB request when epfile_io could execute only after F_FS got disabled as 'if (ep->ep)' check would be TRUE. Fix this by checking stored ffs_ep context against latest epfile->ep to figure out if endpoint got disabled or changed before acquiring spin_lock. Change-Id: I6bdcdf0dff0813ed7b2af8c24f544a22796b0369 Signed-off-by: Manu Gautam Signed-off-by: Mayank Rana Signed-off-by: Jack Pham --- drivers/usb/gadget/function/f_fs.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index ecb6395eddd8..67f7c2b69222 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1183,7 +1183,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) */ spin_lock_irq(&epfile->ffs->eps_lock); interrupted = true; - if (ep->ep) { + /* + * While we were acquiring lock endpoint got + * disabled (disconnect) or changed + (composition switch) ? + */ + if (epfile->ep == ep) { usb_ep_dequeue(ep->ep, req); spin_unlock_irq(&epfile->ffs->eps_lock); wait_for_completion(&done); @@ -1203,7 +1208,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ret = -ENODEV; spin_lock_irq(&epfile->ffs->eps_lock); - if (ep->ep) + /* + * While we were acquiring lock endpoint got + * disabled (disconnect) or changed + * (composition switch) ? + */ + if (epfile->ep == ep) ret = ep->status; spin_unlock_irq(&epfile->ffs->eps_lock); if (io_data->read && ret > 0) @@ -3901,6 +3911,7 @@ static void ffs_func_unbind(struct usb_configuration *c, if (ep->ep && ep->req) usb_ep_free_request(ep->ep, ep->req); ep->req = NULL; + ep->ep = NULL; ++ep; } spin_unlock_irqrestore(&func->ffs->eps_lock, flags);