Message ID | 1654056916-2062-3-git-send-email-quic_linyyuan@quicinc.com |
---|---|
State | Superseded |
Headers | show |
Series | usb: f_fs: safe operation in ffs_epfile_io() | expand |
On 6/1/2022 12:15 PM, Linyu Yuan wrote: > In ffs_epfile_io(), when read/write data in blocking mode, it will wait > the completion in interruptible mode, if task receive a signal, it will > terminate the wait, at same time, if function unbind occurs, > ffs_func_unbind() will kfree all eps, ffs_epfile_io() still try to > dequeue request by dereferencing ep which may become invalid. > > Fix it by add ep spinlock and will not dereference ep if it is not valid. > > Reported-by: Michael Wu <michael@allwinnertech.com> > Reviewed-by: John Keeping <john@metanate.com> > Signed-off-by: Linyu Yuan <quic_linyyuan@quicinc.com> > --- > v2: add Reviewed-by from John keeping > v3: add Reported-by from Michael Wu > > drivers/usb/gadget/function/f_fs.c | 6 ++++++ > 1 file changed, 6 insertions(+) > > diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c > index d4d8940..9bf9287 100644 > --- a/drivers/usb/gadget/function/f_fs.c > +++ b/drivers/usb/gadget/function/f_fs.c > @@ -1077,6 +1077,11 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) > spin_unlock_irq(&epfile->ffs->eps_lock); > > if (wait_for_completion_interruptible(&io_data->done)) { > + spin_lock_irq(&epfile->ffs->eps_lock); > + if (epfile->ep != ep) { > + ret = -ESHUTDOWN; > + goto error_lock; > + } > /* > * To avoid race condition with ffs_epfile_io_complete, > * dequeue the request first then check > @@ -1084,6 +1089,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) > * condition with req->complete callback. > */ > usb_ep_dequeue(ep->ep, req); > + spin_unlock_irq(&epfile->ffs->eps_lock); > wait_for_completion(&io_data->done); > interrupted = io_data->status < 0; > } Tested-by: Michael Wu <michael@allwinnertech.com> I've tested Linyu's patches [PATCH v3 1/2] [PATCH v3 2/2]. I believe it fixes the bug I reported. What's more, John's solution [1] also works in my tests. It looks simpler. I'm not sure if it's as complete as Linyu's solution. [1] https://lore.kernel.org/linux-usb/YpUJkxWBNuZiW7Xk@donbot/
On Thu, Jun 02, 2022 at 06:39:30PM +0800, Michael Wu wrote: > On 6/1/2022 12:15 PM, Linyu Yuan wrote: > > In ffs_epfile_io(), when read/write data in blocking mode, it will wait > > the completion in interruptible mode, if task receive a signal, it will > > terminate the wait, at same time, if function unbind occurs, > > ffs_func_unbind() will kfree all eps, ffs_epfile_io() still try to > > dequeue request by dereferencing ep which may become invalid. > > > > Fix it by add ep spinlock and will not dereference ep if it is not valid. > > > > Reported-by: Michael Wu <michael@allwinnertech.com> > > Reviewed-by: John Keeping <john@metanate.com> > > Signed-off-by: Linyu Yuan <quic_linyyuan@quicinc.com> > > --- > > v2: add Reviewed-by from John keeping > > v3: add Reported-by from Michael Wu > > > > drivers/usb/gadget/function/f_fs.c | 6 ++++++ > > 1 file changed, 6 insertions(+) > > > > diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c > > index d4d8940..9bf9287 100644 > > --- a/drivers/usb/gadget/function/f_fs.c > > +++ b/drivers/usb/gadget/function/f_fs.c > > @@ -1077,6 +1077,11 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) > > spin_unlock_irq(&epfile->ffs->eps_lock); > > if (wait_for_completion_interruptible(&io_data->done)) { > > + spin_lock_irq(&epfile->ffs->eps_lock); > > + if (epfile->ep != ep) { > > + ret = -ESHUTDOWN; > > + goto error_lock; > > + } > > /* > > * To avoid race condition with ffs_epfile_io_complete, > > * dequeue the request first then check > > @@ -1084,6 +1089,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) > > * condition with req->complete callback. > > */ > > usb_ep_dequeue(ep->ep, req); > > + spin_unlock_irq(&epfile->ffs->eps_lock); > > wait_for_completion(&io_data->done); > > interrupted = io_data->status < 0; > > } > > Tested-by: Michael Wu <michael@allwinnertech.com> > > I've tested Linyu's patches [PATCH v3 1/2] [PATCH v3 2/2]. I believe it > fixes the bug I reported. > > What's more, John's solution [1] also works in my tests. It looks simpler. > I'm not sure if it's as complete as Linyu's solution. It's not as comprehensive, let's focus on this thread. > [1] https://lore.kernel.org/linux-usb/YpUJkxWBNuZiW7Xk@donbot/
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index d4d8940..9bf9287 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1077,6 +1077,11 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) spin_unlock_irq(&epfile->ffs->eps_lock); if (wait_for_completion_interruptible(&io_data->done)) { + spin_lock_irq(&epfile->ffs->eps_lock); + if (epfile->ep != ep) { + ret = -ESHUTDOWN; + goto error_lock; + } /* * To avoid race condition with ffs_epfile_io_complete, * dequeue the request first then check @@ -1084,6 +1089,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) * condition with req->complete callback. */ usb_ep_dequeue(ep->ep, req); + spin_unlock_irq(&epfile->ffs->eps_lock); wait_for_completion(&io_data->done); interrupted = io_data->status < 0; }