Skip to contentSkip to conversation

FAILED: patch "[PATCH] usb: gadget: f_fs: Fix Use-After-Free in AIO error path" failed to apply to 6.18-stable tree

PATCH New [email protected] 2 participants 3 messages Sep 3 – Sep 4

Subsystems (2)
lore
Share & tools
lore

[email protected]

author maintainer
#0 lore raw

The patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x e78dcb1f7ec271449c54984dc90c62a5ba272de7
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090331-width-covenant-cd4e@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'

Possible dependencies:

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From e78dcb1f7ec271449c54984dc90c62a5ba272de7 Mon Sep 17 00:00:00 2001
From: Neill Kapron <[email protected]>
Date: Fri, 24 Jul 2026 23:51:00 +0000
Subject: [PATCH] usb: gadget: f_fs: Fix Use-After-Free in AIO error path

In ffs_epfile_write_iter() and ffs_epfile_read_iter(), when ffs_epfile_io() fails with an error other than -EIOCBQUEUED, the io_data structure (p) is freed. However, for AIO operations, the kiocb cancel function was already armed and kiocb->private was set to p.

If a concurrent cancel operation (such as sys_io_cancel()) executes after ffs_epfile_io() fails but before the function frees p, a Use-After-Free can occur when the cancellation handler accesses the freed pointer.

To securely fix this race condition, we must properly un-arm the cancellation. Invoking kiocb->ki_complete() does exactly this by acquiring ctx->ctx_lock and safely removing the kiocb from the active sequence. In doing so, it ensures that a parallel io_cancel can no longer discover the kiocb, effectively closing the race window.

We then return -EIOCBQUEUED to notify the VFS layer that the kiocb has been consumed and it should avoid attempting to complete the request again or triggering subsequent completion handlers.

Fixes de2080d41b5d ("gadget/function/f_fs.c: close leaks")
Assisted-by Antigravity:gemini-3.1-pro
drivers/usb/gadget/function/f_fs.c +4 -0
index cfa05266e551..43962e05eacf 100644
@@ -1296,8 +1296,10 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
 	if (res == -EIOCBQUEUED)
 		return res;
 	if (p->aio) {
+		kiocb->ki_complete(kiocb, res);
 		mmdrop(p->mm);
 		kfree(p);
+		return -EIOCBQUEUED;
 	} else {
 		*from = p->data;
 	}
@@ -1345,9 +1347,11 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
 		return res;
 
 	if (p->aio) {
+		kiocb->ki_complete(kiocb, res);
 		mmdrop(p->mm);
 		kfree(p->to_free);
 		kfree(p);
+		return -EIOCBQUEUED;
 	} else {
 		*to = p->data;
 	}
maintainer
#1 lore raw

PATCH 1/2 USB: gadget: ffs: fix mm lifetime handling

From: Gabriel Prostitis <[email protected]>

[ 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.

Stable-dep-of: e78dcb1f7ec2 ("usb: gadget: f_fs: Fix Use-After-Free in AIO error path")

---
 drivers/usb/gadget/function/f_fs.c | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)
drivers/usb/gadget/function/f_fs.c +20 -7
index 3116060977185..a00bf66bdf878 100644
@@ -857,9 +857,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);
@@ -1240,16 +1246,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;
 }
 
@@ -1284,14 +1294,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 {
Signature · 1 line
2.53.0
maintainer
#2 lore raw

PATCH 2/2 usb: gadget: f_fs: Fix Use-After-Free in AIO error path

From: Neill Kapron <[email protected]>

[ Upstream commit e78dcb1f7ec271449c54984dc90c62a5ba272de7 ]

In ffs_epfile_write_iter() and ffs_epfile_read_iter(), when ffs_epfile_io() fails with an error other than -EIOCBQUEUED, the io_data structure (p) is freed. However, for AIO operations, the kiocb cancel function was already armed and kiocb->private was set to p.

If a concurrent cancel operation (such as sys_io_cancel()) executes after ffs_epfile_io() fails but before the function frees p, a Use-After-Free can occur when the cancellation handler accesses the freed pointer.

To securely fix this race condition, we must properly un-arm the cancellation. Invoking kiocb->ki_complete() does exactly this by acquiring ctx->ctx_lock and safely removing the kiocb from the active sequence. In doing so, it ensures that a parallel io_cancel can no longer discover the kiocb, effectively closing the race window.

We then return -EIOCBQUEUED to notify the VFS layer that the kiocb has been consumed and it should avoid attempting to complete the request again or triggering subsequent completion handlers.

Fixes de2080d41b5d ("gadget/function/f_fs.c: close leaks")
Assisted-by Antigravity:gemini-3.1-pro
---
 drivers/usb/gadget/function/f_fs.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/usb/gadget/function/f_fs.c +4 -0
index a00bf66bdf878..df66441bec46f 100644
@@ -1255,8 +1255,10 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
 	if (res == -EIOCBQUEUED)
 		return res;
 	if (p->aio) {
+		kiocb->ki_complete(kiocb, res);
 		mmdrop(p->mm);
 		kfree(p);
+		return -EIOCBQUEUED;
 	} else {
 		*from = p->data;
 	}
@@ -1304,9 +1306,11 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
 		return res;
 
 	if (p->aio) {
+		kiocb->ki_complete(kiocb, res);
 		mmdrop(p->mm);
 		kfree(p->to_free);
 		kfree(p);
+		return -EIOCBQUEUED;
 	} else {
 		*to = p->data;
 	}
Signature · 1 line
2.53.0