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 <mgautam@codeaurora.org>
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
This commit is contained in:
Manu Gautam 2016-01-28 11:09:27 -08:00 committed by David Keitel
parent e822c0fcf5
commit f5ae88ff36

View file

@ -848,7 +848,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
} else if (unlikely(
wait_for_completion_interruptible(&done))) {
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)
usb_ep_dequeue(ep->ep, req);
spin_unlock_irq(&epfile->ffs->eps_lock);
ret = -EINTR;
@ -861,7 +866,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
* data then user space has space for.
*/
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;
else
ret = -ENODEV;
@ -3268,6 +3278,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;
} while (--count);
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);