Re: [PATCH 2/2] USB: gadget: inode: fix mm lifetime handling

From: Alan Stern

Date: Sun May 31 2026 - 21:55:58 EST


On Sun, May 31, 2026 at 11:04:41PM +0200, Gabriel Prostitis via B4 Relay wrote:
> From: Gabriel Prostitis <prostitisgabriel@xxxxxxxxx>
>
> priv stores a pointer to the submitting task's mm_struct,
> but does not currently hold a reference to it while async
> requests are pending.
>
> This can result in a use-after-free if the task exits before
> completion handling finishes.
>
> Take a reference with mmgrab() when queuing the read request
> and release it with mmdrop() on request completion.
>
> Signed-off-by: Gabriel Prostitis <prostitisgabriel@xxxxxxxxx>
> ---
> drivers/usb/gadget/legacy/inode.c | 17 +++++++++++++----
> 1 file changed, 13 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
> index d87a8ab51510..f9b7199afc9e 100644
> --- a/drivers/usb/gadget/legacy/inode.c
> +++ b/drivers/usb/gadget/legacy/inode.c
> @@ -471,11 +471,17 @@ static void ep_user_copy_worker(struct work_struct *work)
> struct kiocb *iocb = priv->iocb;
> size_t ret;
>
> - kthread_use_mm(mm);
> - ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
> - kthread_unuse_mm(mm);
> - if (!ret)
> + if (mmget_not_zero(mm)) {
> + kthread_use_mm(mm);
> + ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
> + kthread_unuse_mm(mm);
> + mmput(mm);
> + if (!ret)
> + ret = -EFAULT;
> + } else {
> ret = -EFAULT;
> + }
> + mmdrop(mm);
>
> /* completing the iocb can drop the ctx and mm, don't touch mm after */
> iocb->ki_complete(iocb, ret);
> @@ -501,6 +507,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
> * complete the aio request immediately.
> */
> if (priv->to_free == NULL || unlikely(req->actual == 0)) {
> + mmdrop(priv->mm);
> kfree(req->buf);
> kfree(priv->to_free);
> kfree(priv);
> @@ -540,6 +547,7 @@ static ssize_t ep_aio(struct kiocb *iocb,
> get_ep(epdata);
> priv->epdata = epdata;
> priv->actual = 0;
> + mmgrab(priv->mm);

This new statement is certainly in the wrong place, because priv->mm
isn't assigned until the next line. The two lines should be swapped.

After you fix that glaring mistake you can add:

Acked-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>

Alan Stern

> priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */
>
> /* each kiocb is coupled to one usb_request, but we can't
> @@ -570,6 +578,7 @@ static ssize_t ep_aio(struct kiocb *iocb,
>
> fail:
> spin_unlock_irq(&epdata->dev->lock);
> + mmdrop(priv->mm);
> kfree(priv->to_free);
> kfree(priv);
> put_ep(epdata);
>
> --
> 2.54.0
>
>
>