| From sashal@kernel.org Fri Sep 4 16:29:42 2026 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 4 Sep 2026 10:29:38 -0400 |
| Subject: USB: gadget: ffs: fix mm lifetime handling |
| To: stable@vger.kernel.org |
| Cc: Gabriel Prostitis <prostitisgabriel@gmail.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org> |
| Message-ID: <20260904142939.798242-1-sashal@kernel.org> |
| |
| From: Gabriel Prostitis <prostitisgabriel@gmail.com> |
| |
| [ Upstream commit 5eb5c72c72fef76cb765ef1669b62b6a3ba1bfc8 ] |
| |
| 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> |
| Link: https://patch.msgid.link/20260601-mm-uaf-fix-v2-1-3c942a707bce@gmail.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Stable-dep-of: e78dcb1f7ec2 ("usb: gadget: f_fs: Fix Use-After-Free in AIO error path") |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/usb/gadget/function/f_fs.c | 27 ++++++++++++++++++++------- |
| 1 file changed, 20 insertions(+), 7 deletions(-) |
| |
| --- a/drivers/usb/gadget/function/f_fs.c |
| +++ b/drivers/usb/gadget/function/f_fs.c |
| @@ -863,9 +863,15 @@ static void ffs_user_copy_worker(struct |
| 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); |
| @@ -1246,16 +1252,20 @@ static ssize_t ffs_epfile_write_iter(str |
| |
| 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; |
| } |
| |
| @@ -1290,14 +1300,17 @@ static ssize_t ffs_epfile_read_iter(stru |
| |
| 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 { |