* [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths
@ 2026-06-01 6:44 Gabriel Prostitis via B4 Relay
2026-06-01 6:44 ` [PATCH v2 1/2] USB: gadget: ffs: fix mm lifetime handling Gabriel Prostitis via B4 Relay
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Gabriel Prostitis via B4 Relay @ 2026-06-01 6:44 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb, linux-kernel, Alan Stern
There is a use-after-free in the USB gadget FunctionFS and legacy
GadgetFS asynchronous read paths. Both implementations store
current->mm in per-request state without taking a reference, and
later use it in a workqueue context via kthread_use_mm().
If the submitting task exits before the USB request completes,
the stored mm_struct may be freed while a pending request still
references it, leading to a use-after-free.
The issue affects:
- FunctionFS: drivers/usb/gadget/function/f_fs.c
- GadgetFS legacy: drivers/usb/gadget/legacy/inode.c
Fix this by taking a reference to mm_struct with mmgrab() when
queueing the request and releasing it with mmdrop() after
completion. Before using the saved mm_struct, acquire a temporary
reference with mmget_not_zero() to ensure it is still alive.
The issue can be triggered by submitting asynchronous reads on
OUT endpoints (e.g. via io_uring for FunctionFS) and exiting the
submitting task before completion. This may result in memory
corruption in the address space of another process if the freed
mm_struct is reclaimed during the race window.
KASAN reports confirm use-after-free in the workqueue completion
path when accessing the stale mm_struct.
Signed-off-by: Gabriel Prostitis <prostitisgabriel@gmail.com>
---
Changes in v2:
- Fix swapped lines in drivers/usb/gadget/legacy/inode.c
- Link to v1: https://patch.msgid.link/20260531-mm-uaf-fix-v1-0-91571cc6ca46@gmail.com
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-usb@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
Gabriel Prostitis (2):
USB: gadget: ffs: fix mm lifetime handling
USB: gadget: inode: fix mm lifetime handling
drivers/usb/gadget/function/f_fs.c | 27 ++++++++++++++++++++-------
drivers/usb/gadget/legacy/inode.c | 17 +++++++++++++----
2 files changed, 33 insertions(+), 11 deletions(-)
---
base-commit: 22d91cef94b5b86cff0d68ebfce7741740672704
change-id: 20260531-mm-uaf-fix-91d9dacac692
Best regards,
--
Gabriel Prostitis <prostitisgabriel@gmail.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] USB: gadget: ffs: fix mm lifetime handling
2026-06-01 6:44 [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths Gabriel Prostitis via B4 Relay
@ 2026-06-01 6:44 ` Gabriel Prostitis via B4 Relay
2026-06-01 6:44 ` [PATCH v2 2/2] USB: gadget: inode: " Gabriel Prostitis via B4 Relay
2026-06-30 15:02 ` [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths Gabriel Prostitis
2 siblings, 0 replies; 5+ messages in thread
From: Gabriel Prostitis via B4 Relay @ 2026-06-01 6:44 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb, linux-kernel
From: Gabriel Prostitis <prostitisgabriel@gmail.com>
io_data 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>
---
drivers/usb/gadget/function/f_fs.c | 27 ++++++++++++++++++++-------
1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 002c3441bea3..674f2fd5450f 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -864,9 +864,15 @@ static void ffs_user_copy_worker(struct work_struct *work)
bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD;
if (io_data->read && ret > 0) {
- kthread_use_mm(io_data->mm);
- ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data);
- kthread_unuse_mm(io_data->mm);
+ if (mmget_not_zero(io_data->mm)) {
+ kthread_use_mm(io_data->mm);
+ ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data);
+ kthread_unuse_mm(io_data->mm);
+ mmput(io_data->mm);
+ } else {
+ ret = -EFAULT;
+ }
+ mmdrop(io_data->mm);
}
io_data->kiocb->ki_complete(io_data->kiocb, ret);
@@ -1261,16 +1267,20 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
kiocb->private = p;
- if (p->aio)
+ if (p->aio) {
+ mmgrab(p->mm);
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+ }
res = ffs_epfile_io(kiocb->ki_filp, p);
if (res == -EIOCBQUEUED)
return res;
- if (p->aio)
+ if (p->aio) {
+ mmdrop(p->mm);
kfree(p);
- else
+ } else {
*from = p->data;
+ }
return res;
}
@@ -1305,14 +1315,17 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
kiocb->private = p;
- if (p->aio)
+ if (p->aio) {
+ mmgrab(p->mm);
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+ }
res = ffs_epfile_io(kiocb->ki_filp, p);
if (res == -EIOCBQUEUED)
return res;
if (p->aio) {
+ mmdrop(p->mm);
kfree(p->to_free);
kfree(p);
} else {
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] USB: gadget: inode: fix mm lifetime handling
2026-06-01 6:44 [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths Gabriel Prostitis via B4 Relay
2026-06-01 6:44 ` [PATCH v2 1/2] USB: gadget: ffs: fix mm lifetime handling Gabriel Prostitis via B4 Relay
@ 2026-06-01 6:44 ` Gabriel Prostitis via B4 Relay
2026-06-30 15:02 ` [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths Gabriel Prostitis
2 siblings, 0 replies; 5+ messages in thread
From: Gabriel Prostitis via B4 Relay @ 2026-06-01 6:44 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb, linux-kernel, Alan Stern
From: Gabriel Prostitis <prostitisgabriel@gmail.com>
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>
---
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
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths
2026-06-01 6:44 [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths Gabriel Prostitis via B4 Relay
2026-06-01 6:44 ` [PATCH v2 1/2] USB: gadget: ffs: fix mm lifetime handling Gabriel Prostitis via B4 Relay
2026-06-01 6:44 ` [PATCH v2 2/2] USB: gadget: inode: " Gabriel Prostitis via B4 Relay
@ 2026-06-30 15:02 ` Gabriel Prostitis
2026-07-01 5:40 ` Greg KH
2 siblings, 1 reply; 5+ messages in thread
From: Gabriel Prostitis @ 2026-06-30 15:02 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, linux-kernel
Hi,
Just a gentle ping on this series.
I noticed that it has not shown up in -next yet, so I wanted to check
whether there is anything else I should address.
Thanks,
Gabriel
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths
2026-06-30 15:02 ` [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths Gabriel Prostitis
@ 2026-07-01 5:40 ` Greg KH
0 siblings, 0 replies; 5+ messages in thread
From: Greg KH @ 2026-07-01 5:40 UTC (permalink / raw)
To: Gabriel Prostitis; +Cc: linux-usb, linux-kernel
On Tue, Jun 30, 2026 at 05:02:45PM +0200, Gabriel Prostitis wrote:
> Hi,
>
> Just a gentle ping on this series.
I have no context here :(
> I noticed that it has not shown up in -next yet, so I wanted to check
> whether there is anything else I should address.
The merge window happened which kept things from being able to be merged
for 2 weeks. Now I have 3000+ patches to dig through and review.
Please help out by reviewing other patches sent to the list so that
yours can move up faster.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-01 5:40 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-01 6:44 [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths Gabriel Prostitis via B4 Relay
2026-06-01 6:44 ` [PATCH v2 1/2] USB: gadget: ffs: fix mm lifetime handling Gabriel Prostitis via B4 Relay
2026-06-01 6:44 ` [PATCH v2 2/2] USB: gadget: inode: " Gabriel Prostitis via B4 Relay
2026-06-30 15:02 ` [PATCH v2 0/2] USB: gadget: fix mm lifetime use-after-free in async read paths Gabriel Prostitis
2026-07-01 5:40 ` Greg KH
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox