FAILED: patch "[PATCH] usb: gadget: f_fs: Fix Use-After-Free in AIO error path" failed to apply to 5.10-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.10-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-5.10.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 '2026090334-tiring-good-fffe@gregkh' --subject-prefix 'PATCH 5.10.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.
drivers/usb/gadget/function/f_fs.c +4 -0
@@ -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; }
Sasha Levin
maintainerPATCH 1/4 block: split out operations on block special files
From: Christoph Hellwig <[email protected]>
[ Upstream commit cd82cca7ebfe9c6c74a8e5b28382ba88177ba5f1 ]
Add a new block/fops.c for all the file and address_space operations that provide the block special file support.
[ sashal: Reduced backport -- upstream cd82cca7ebfe9 touches 4 file(s), this
backport carries 1. Not backported here:
block/blk.h
block/fops.c
block/Makefile
fs/block_dev.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
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
@@ -831,9 +831,15 @@ static void ffs_user_copy_worker(struct work_struct *work)unsigned long flags;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, ret);
@@ -1210,16 +1216,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; }
@@ -1256,14 +1266,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
Sasha Levin
maintainerPATCH 2/4 usb: remove res2 argument from gadget code completions
From: Jens Axboe <[email protected]>
[ Upstream commit 4c3d8accdce26afa8089609593aa92965b500f72 ]
The USB gadget code is the only code that every tried to utilize the 2nd argument of the aio completions, but there are strong suspicions that it was never actually used by anything on the userspace side.
Out of the 3 cases that touch it, two of them just pass in the same as res, and the last one passes in error/transfer in res like any other normal use case.
Remove the 2nd argument, pass 0 like the rest of the in-kernel users of kiocb based IO.
[ sashal: Reduced backport -- upstream 4c3d8accdce26 touches 2 file(s), this
backport carries 1. Not backported here:
drivers/usb/gadget/legacy/inode.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: e78dcb1f7ec2 ("usb: gadget: f_fs: Fix Use-After-Free in AIO error path")
--- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/gadget/function/f_fs.c +1 -1
@@ -842,7 +842,7 @@ static void ffs_user_copy_worker(struct work_struct *work)mmdrop (io_data->mm); } - io_data->kiocb->ki_complete (io_data->kiocb, ret, ret); + io_data->kiocb->ki_complete (io_data->kiocb, ret,0 );if (io_data->ffs->ffs_eventfd && !kiocb_has_eventfd)eventfd_signal (io_data->ffs->ffs_eventfd,1 );
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 fs: get rid of the res2 iocb->ki_complete argument
In reply to Sasha LevinFrom: Jens Axboe <[email protected]>
[ Upstream commit 6b19b766e8f077f29cdb47da5003469a85bbfb9c ]
The second argument was only used by the USB gadget code, yet everyone pays the overhead of passing a zero to be passed into aio, where it ends up being part of the aio res2 value.
Now that everybody is passing in zero, kill off the extra argument.
[ sashal: Reduced backport -- upstream 6b19b766e8f07 touches 18 file(s), this
backport carries 17. Not backported here:
block/fops.c
fs/cachefiles/io.c
fs/io_uring.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: e78dcb1f7ec2 ("usb: gadget: f_fs: Fix Use-After-Free in AIO error path")
--- crypto/af_alg.c | 2 +- drivers/block/loop.c | 4 ++-- drivers/nvme/target/io-cmd-file.c | 4 ++-- drivers/target/target_core_file.c | 4 ++-- drivers/usb/gadget/function/f_fs.c | 2 +- drivers/usb/gadget/legacy/inode.c | 9 +++------ fs/aio.c | 6 +++--- fs/block_dev.c | 2 +- fs/ceph/file.c | 2 +- fs/cifs/file.c | 4 ++-- fs/direct-io.c | 2 +- fs/fuse/file.c | 2 +- fs/iomap/direct-io.c | 2 +- fs/nfs/direct.c | 2 +- fs/overlayfs/file.c | 4 ++-- include/linux/fs.h | 2 +- io_uring/io_uring.c | 6 +++--- 17 files changed, 28 insertions(+), 31 deletions(-)
crypto/af_alg.c +1 -1
@@ -1030,7 +1030,7 @@ void af_alg_async_cb(struct crypto_async_request *_req, int err)af_alg_free_resources (areq);sock_put (sk); - iocb->ki_complete (iocb, err ? err : (int )resultlen,0 ); + iocb->ki_complete (iocb, err ? err : (int )resultlen); }EXPORT_SYMBOL_GPL (af_alg_async_cb);
drivers/block/loop.c +2 -2
@@ -497,7 +497,7 @@ static void lo_rw_aio_do_completion(struct loop_cmd *cmd)blk_mq_complete_request (rq); } -static void lo_rw_aio_complete (struct kiocb *iocb,long ret,long ret2) +static void lo_rw_aio_complete (struct kiocb *iocb,long ret) { struct loop_cmd *cmd = container_of(iocb, struct loop_cmd, iocb);
@@ -575,7 +575,7 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,kthread_associate_blkcg (NULL );if (ret != -EIOCBQUEUED ) - cmd->iocb.ki_complete (&cmd->iocb, ret,0 ); +lo_rw_aio_complete (&cmd->iocb, ret);return 0 ; }
drivers/nvme/target/io-cmd-file.c +2 -2
@@ -126,7 +126,7 @@ static ssize_t nvmet_file_submit_bvec(struct nvmet_req *req, loff_t pos,return call_iter (iocb, &iter); } -static void nvmet_file_io_done (struct kiocb *iocb,long ret,long ret2) +static void nvmet_file_io_done (struct kiocb *iocb,long ret) { struct nvmet_req *req = container_of(iocb, struct nvmet_req, f.iocb);u16 status =NVME_SC_SUCCESS ;
@@ -223,7 +223,7 @@ static bool nvmet_file_execute_io(struct nvmet_req *req, int ki_flags) } complete: -nvmet_file_io_done (&req->f.iocb, ret,0 ); +nvmet_file_io_done (&req->f.iocb, ret);return true; }
drivers/target/target_core_file.c +2 -2
@@ -243,7 +243,7 @@ struct target_core_file_cmd {struct kiocb iocb; }; -static void cmd_rw_aio_complete (struct kiocb *iocb,long ret,long ret2) +static void cmd_rw_aio_complete (struct kiocb *iocb,long ret) {struct target_core_file_cmd *cmd;
@@ -310,7 +310,7 @@ fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,kfree (bvec);if (ret != -EIOCBQUEUED ) -cmd_rw_aio_complete (&aio_cmd->iocb, ret,0 ); +cmd_rw_aio_complete (&aio_cmd->iocb, ret);return 0 ; }
drivers/usb/gadget/function/f_fs.c +1 -1
@@ -842,7 +842,7 @@ static void ffs_user_copy_worker(struct work_struct *work)mmdrop (io_data->mm); } - io_data->kiocb->ki_complete (io_data->kiocb, ret,0 ); + io_data->kiocb->ki_complete (io_data->kiocb, ret);if (io_data->ffs->ffs_eventfd && !kiocb_has_eventfd)eventfd_signal (io_data->ffs->ffs_eventfd,1 );
drivers/usb/gadget/legacy/inode.c +3 -6
@@ -473,7 +473,7 @@ static void ep_user_copy_worker(struct work_struct *work) ret = -EFAULT ;/* completing the iocb can drop the ctx and mm, don't touch mm after */ - iocb->ki_complete (iocb, ret, ret); + iocb->ki_complete (iocb, ret);kfree (priv->buf);kfree (priv->to_free);
@@ -500,10 +500,8 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)kfree (priv->to_free);kfree (priv); iocb->private =NULL ; -/* aio_complete() reports bytes-transferred _and_ faults */ - - iocb->ki_complete (iocb, req->actual ? req->actual : req->status, - req->status); + iocb->ki_complete (iocb, + req->actual ? req->actual : (long )req->status); }else {/* ep_copy_to_user() won't report both; we hide some faults */ if (unlikely (0 != req->status))
@@ -2136,4 +2134,3 @@ static void __exit cleanup (void)unregister_filesystem (&gadgetfs_type); }module_exit (cleanup); -
fs/aio.c +3 -3
@@ -1434,7 +1434,7 @@ static void aio_remove_iocb(struct aio_kiocb *iocb)spin_unlock_irqrestore (&ctx->ctx_lock, flags); } -static void aio_complete_rw (struct kiocb *kiocb,long res,long res2) +static void aio_complete_rw (struct kiocb *kiocb,long res) { struct aio_kiocb *iocb = container_of(kiocb, struct aio_kiocb, rw);
@@ -1454,7 +1454,7 @@ static void aio_complete_rw(struct kiocb *kiocb, long res, long res2) } iocb->ki_res.res = res; - iocb->ki_res.res2 = res2; + iocb->ki_res.res2 =0 ;iocb_put (iocb); }
@@ -1525,7 +1525,7 @@ static inline void aio_rw_done(struct kiocb *req, ssize_t ret) ret = -EINTR ; fallthrough;default : - req->ki_complete (req, ret,0 ); + req->ki_complete (req, ret); } }
fs/block_dev.c +1 -1
@@ -352,7 +352,7 @@ static void blkdev_bio_end_io(struct bio *bio) ret =blk_status_to_errno (dio->bio.bi_status); } - dio->iocb->ki_complete (iocb, ret,0 ); + dio->iocb->ki_complete (iocb, ret);if (dio->multi_bio)bio_put (&dio->bio); }else {
fs/ceph/file.c +1 -1
@@ -1052,7 +1052,7 @@ static void ceph_aio_complete(struct inode *inode,ceph_put_cap_refs (ci, (aio_req->write ?CEPH_CAP_FILE_WR :CEPH_CAP_FILE_RD )); - aio_req->iocb->ki_complete (aio_req->iocb, ret,0 ); + aio_req->iocb->ki_complete (aio_req->iocb, ret);ceph_free_cap_flush (aio_req->prealloc_cf);kfree (aio_req);
fs/cifs/file.c +2 -2
@@ -3125,7 +3125,7 @@ static void collect_uncached_write_data(struct cifs_aio_ctx *ctx)mutex_unlock (&ctx->aio_mutex);if (ctx->iocb && ctx->iocb->ki_complete) - ctx->iocb->ki_complete (ctx->iocb, ctx->rc,0 ); + ctx->iocb->ki_complete (ctx->iocb, ctx->rc);else complete (&ctx->done); }
@@ -3861,7 +3861,7 @@ collect_uncached_read_data(struct cifs_aio_ctx *ctx)mutex_unlock (&ctx->aio_mutex);if (ctx->iocb && ctx->iocb->ki_complete) - ctx->iocb->ki_complete (ctx->iocb, ctx->rc,0 ); + ctx->iocb->ki_complete (ctx->iocb, ctx->rc);else complete (&ctx->done); }
fs/direct-io.c +1 -1
@@ -308,7 +308,7 @@ static ssize_t dio_complete(struct dio *dio, ssize_t ret, unsigned int flags)if (ret >0 && dio->op ==REQ_OP_WRITE ) ret =generic_write_sync (dio->iocb, ret); - dio->iocb->ki_complete (dio->iocb, ret,0 ); + dio->iocb->ki_complete (dio->iocb, ret); }kmem_cache_free (dio_cache, dio);
fs/fuse/file.c +1 -1
@@ -693,7 +693,7 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)spin_unlock (&fi->lock); } - io->iocb->ki_complete (io->iocb, res,0 ); + io->iocb->ki_complete (io->iocb, res); }kref_put (&io->refcnt, fuse_io_release);
fs/iomap/direct-io.c +1 -1
@@ -139,7 +139,7 @@ static void iomap_dio_complete_work(struct work_struct *work) struct iomap_dio *dio = container_of(work, struct iomap_dio, aio.work);struct kiocb *iocb = dio->iocb; - iocb->ki_complete (iocb,iomap_dio_complete (dio),0 ); + iocb->ki_complete (iocb,iomap_dio_complete (dio)); }/*
fs/nfs/direct.c +1 -1
@@ -275,7 +275,7 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq) res = (long ) dreq->count;WARN_ON_ONCE (dreq->count <0 ); } - dreq->iocb->ki_complete (dreq->iocb, res,0 ); + dreq->iocb->ki_complete (dreq->iocb, res); }complete (&dreq->completion);
fs/overlayfs/file.c +2 -2
@@ -279,14 +279,14 @@ static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req)ovl_aio_put (aio_req); } -static void ovl_aio_rw_complete (struct kiocb *iocb,long res,long res2) +static void ovl_aio_rw_complete (struct kiocb *iocb,long res) {struct ovl_aio_req *aio_req =container_of (iocb, struct ovl_aio_req, iocb);struct kiocb *orig_iocb = aio_req->orig_iocb;ovl_aio_cleanup_handler (aio_req); - orig_iocb->ki_complete (orig_iocb, res, res2); + orig_iocb->ki_complete (orig_iocb, res); }static ssize_t ovl_read_iter (struct kiocb *iocb,struct iov_iter *iter)
include/linux/fs.h +1 -1
@@ -332,7 +332,7 @@ struct kiocb {randomized_struct_fields_start loff_t ki_pos; -void (*ki_complete)(struct kiocb *iocb,long ret,long ret2); +void (*ki_complete)(struct kiocb *iocb,long ret);void *private;int ki_flags;u16 ki_hint;
io_uring/io_uring.c +3 -3
@@ -2796,7 +2796,7 @@ static void io_req_rw_complete(struct io_kiocb *req, bool *locked)io_req_task_complete (req, locked); } -static void io_complete_rw (struct kiocb *kiocb,long res,long res2) +static void io_complete_rw (struct kiocb *kiocb,long res) { struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw.kiocb);
@@ -2807,7 +2807,7 @@ static void io_complete_rw(struct kiocb *kiocb, long res, long res2)io_req_task_work_add (req); } -static void io_complete_rw_iopoll (struct kiocb *kiocb,long res,long res2) +static void io_complete_rw_iopoll (struct kiocb *kiocb,long res) { struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw.kiocb);
@@ -3032,7 +3032,7 @@ static inline void io_rw_done(struct kiocb *kiocb, ssize_t ret) ret = -EINTR ; fallthrough;default : - kiocb->ki_complete (kiocb, ret,0 ); + kiocb->ki_complete (kiocb, ret); } }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 usb: gadget: f_fs: Fix Use-After-Free in AIO error path
In reply to Sasha LevinFrom: 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.
--- drivers/usb/gadget/function/f_fs.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/usb/gadget/function/f_fs.c +4 -0
@@ -1225,8 +1225,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; }
@@ -1276,9 +1278,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