diff options
author | Sujeet Kumar <ksujeet@codeaurora.org> | 2016-01-28 11:18:13 -0800 |
---|---|---|
committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:10:03 -0700 |
commit | 32abf3b67f7f57e792382e68bb6a79c1ec062fd8 (patch) | |
tree | a4b3ecc6deb3328f2265e3f8a929caae0816f023 /drivers | |
parent | f5ae88ff3622603a46e86fd2ed8728c87e26b4b6 (diff) |
USB: f_fs: Avoid using completion variable on stack
done completion variable is local stack variable to ffs_epfile_io().
It is being used to unblock ffs_epfile_io() from USB request
completion context where done is accessed through req->context. If
ffs_epfile_io() is unblocked or interrupted due to epfile close or
any signal before USB request completion is handled, req->context is
having stale "done" reference causing invalid access. Fix this issue
by storing done completion reference with epfile structure instead of
having it on stack to have valid req->context in completion handler.
Change-Id: I15102538d1b5bee14dfa3c7b3fa1f8e3f767cf71
Signed-off-by: Sujeet Kumar <ksujeet@codeaurora.org>
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/gadget/function/f_fs.c | 22 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_fs.h | 3 |
2 files changed, 21 insertions, 4 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index e7cb9deb2028..2be964e26279 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -633,8 +633,11 @@ static const struct file_operations ffs_ep0_operations = { static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) { + struct ffs_ep *ep = _ep->driver_data; ENTER(); - if (likely(req->context)) { + + /* req may be freed during unbind */ + if (ep && ep->req && likely(req->context)) { struct ffs_ep *ep = _ep->driver_data; ep->status = req->status ? req->status : req->actual; complete(req->context); @@ -686,6 +689,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) { struct ffs_epfile *epfile = file->private_data; struct ffs_ep *ep; + struct ffs_data *ffs = epfile->ffs; char *data = NULL; ssize_t ret, data_len = -EINVAL; int halt; @@ -830,15 +834,23 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) spin_unlock_irq(&epfile->ffs->eps_lock); } else { - DECLARE_COMPLETION_ONSTACK(done); + struct completion *done; req = ep->req; req->buf = data; req->length = data_len; - req->context = &done; req->complete = ffs_epfile_io_complete; + if (io_data->read) { + reinit_completion(&epfile->ffs->epout_completion); + done = &epfile->ffs->epout_completion; + } else { + reinit_completion(&epfile->ffs->epin_completion); + done = &epfile->ffs->epin_completion; + } + req->context = done; + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); spin_unlock_irq(&epfile->ffs->eps_lock); @@ -846,7 +858,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) if (unlikely(ret < 0)) { ret = -EIO; } else if (unlikely( - wait_for_completion_interruptible(&done))) { + wait_for_completion_interruptible(done))) { spin_lock_irq(&epfile->ffs->eps_lock); /* * While we were acquiring lock endpoint got @@ -1488,6 +1500,8 @@ static struct ffs_data *ffs_data_new(void) spin_lock_init(&ffs->eps_lock); init_waitqueue_head(&ffs->ev.waitq); init_completion(&ffs->ep0req_completion); + init_completion(&ffs->epout_completion); + init_completion(&ffs->epin_completion); /* XXX REVISIT need to update it in some places, or do we? */ ffs->ev.can_stall = 1; diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h index 60139854e0b1..6e6318c94e93 100644 --- a/drivers/usb/gadget/function/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -176,6 +176,9 @@ struct ffs_data { struct usb_request *ep0req; /* P: mutex */ struct completion ep0req_completion; /* P: mutex */ + struct completion epin_completion; + struct completion epout_completion; + /* reference counter */ atomic_t ref; /* how many files are opened (EP0 and others) */ |