USB: gadget: ffs: fix mm lifetime handling

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>
This commit is contained in:
Gabriel Prostitis 2026-06-01 08:44:10 +02:00 committed by Greg Kroah-Hartman
parent 3a1c90aeb9
commit 5eb5c72c72

View File

@ -869,9 +869,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);
@ -1274,16 +1280,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;
}
@ -1318,14 +1328,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 {