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

From: Gabriel Prostitis via B4 Relay

Date: Mon Jun 01 2026 - 02:44:50 EST


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.

Reported-by: Gabriel Prostitis <prostitisgabriel@xxxxxxxxx>
Signed-off-by: Gabriel Prostitis <prostitisgabriel@xxxxxxxxx>
Acked-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>
---
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..db961aaa3740 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);
@@ -541,6 +548,7 @@ static ssize_t ep_aio(struct kiocb *iocb,
priv->epdata = epdata;
priv->actual = 0;
priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */
+ mmgrab(priv->mm);

/* each kiocb is coupled to one usb_request, but we can't
* allocate or submit those if the host disconnected.
@@ -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