USB: gadget: inode: fix mm lifetime handling

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@gmail.com>
Signed-off-by: Gabriel Prostitis <prostitisgabriel@gmail.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Link: https://patch.msgid.link/20260601-mm-uaf-fix-v2-2-3c942a707bce@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Gabriel Prostitis 2026-06-01 08:44:11 +02:00 committed by Greg Kroah-Hartman
parent 5eb5c72c72
commit a1c33c80e1

View File

@ -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);