From 32abf3b67f7f57e792382e68bb6a79c1ec062fd8 Mon Sep 17 00:00:00 2001 From: Sujeet Kumar Date: Thu, 28 Jan 2016 11:18:13 -0800 Subject: [PATCH] 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 Signed-off-by: Mayank Rana --- drivers/usb/gadget/function/f_fs.c | 22 ++++++++++++++++++---- 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) */