FAILED: patch "[PATCH] fuse: wait for FR_FINISHED on abort_on_kill to prevent" failed to apply to 6.18-stable tree
Subsystems (2)
[email protected]
maintainerThe 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 64b0b5cacbd2fea88001464cb712c9dfc795b26e
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026083101-rimless-sadness-3898@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 64b0b5cacbd2fea88001464cb712c9dfc795b26e Mon Sep 17 00:00:00 2001
From: Rochan Avlur <[email protected]>
Date: Wed, 12 Aug 2026 20:58:36 -0700
Subject: [PATCH] fuse: wait for FR_FINISHED on abort_on_kill to prevent
use-after-free
The abort_on_kill path in request_wait_answer() calls fuse_abort_conn() and returns without waiting for FR_FINISHED. If fuse_dev_do_write() is concurrently processing the same request (FR_LOCKED set), the caller frees req->args while it is still being accessed, causing a use-after-free.
Fix this by jumping to the existing wait_event(FR_FINISHED) instead of returning early. The wait will not hang because fuse_abort_conn() ensures all requests are ended.
fs/fuse/dev.c +2 -1
@@ -724,7 +724,7 @@ static void request_wait_answer(struct fuse_req *req)if (req->args->abort_on_kill) {fuse_chan_abort (fch, false); -return ; + goto wait_for_finish; }if (test_bit (FR_URING , &req->flags))
@@ -735,6 +735,7 @@ static void request_wait_answer(struct fuse_req *req)return ; } +wait_for_finish:/* * Either request is already in userspace, or it was forced. * Wait it out.
Sasha Levin
maintainerPATCH 1/4 fuse: abort related layering cleanup
From: Miklos Szeredi <[email protected]>
[ Upstream commit 4eeb5e6cb0fd89b343fcf755ae3aad76fdb5b2c2 ]
- rename fuse_abort_conn() to fuse_chan_abort(), pass fuse_chan pointer instead of fuse_conn
- pass an abort_with_err argument that tells fuse_dev_(read|write) to return with ECONNABORTED instead of ENODEV
- move fc->aborted to fch->abort_with_err
- rename fuse_wait_aborted() to fuse_chan_wait_aborted()
[ 6.18 has no "struct fuse_chan" -- the connection object still plays
that role, and the surrounding series that splits it out is not in stable. Adapted as follows:
- the renames (fuse_abort_conn() -> fuse_chan_abort(), fuse_wait_aborted() -> fuse_chan_wait_aborted(), the "abort_conn" label in fuse_check_timeout() -> "chan_abort") and the new abort_with_err argument are taken as-is, but both helpers keep taking "struct fuse_conn *".
- the "move fc->aborted to fch->abort_with_err" bullet is dropped:
there is no fuse_chan to move the flag into, so fc->aborted stays in struct fuse_conn. fuse_chan_abort() only ever *sets* it, as control.c did before, rather than assigning the new argument unconditionally the way upstream does: on this tree the flag is write-once and is what makes fuse_dev_do_read() and fuse_uring_cmd() report ECONNABORTED instead of ENODEV, so letting a later abort with abort_with_err=false clear it (umount -f, fuse_conn_destroy(), fuse_dev_release(), the timeout worker) would change what userspace sees after a sysfs abort on an abort_err mount. Consequently the fuse_dev_i.h and dev_uring.c hunks, which only rename the field access, are dropped as well.
- fs/fuse/dev.h and fs/fuse/req_timeout.c do not exist here: the declarations stay in fuse_i.h and fuse_check_timeout() lives in dev.c, so those two hunks were replayed there.
- in request_wait_answer() the connection pointer is named "fch", matching the name upstream gives it in 28d733cc3ecad ("fuse: change req->fm to req->chan"), so that the follow-up fix applies here unchanged. ]
[ sashal: Reduced backport -- upstream 4eeb5e6cb0fd8 touches 9 file(s), this
backport carries 5. Not backported here:
fs/fuse/dev.h
fs/fuse/dev_uring.c
fs/fuse/fuse_dev_i.h
fs/fuse/req_timeout.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 64b0b5cacbd2 ("fuse: wait for FR_FINISHED on abort_on_kill to prevent use-after-free")
--- fs/fuse/control.c | 4 +--- fs/fuse/cuse.c | 4 ++-- fs/fuse/dev.c | 35 +++++++++++++++++++---------------- fs/fuse/fuse_i.h | 4 ++-- fs/fuse/inode.c | 6 +++--- 5 files changed, 27 insertions(+), 26 deletions(-)
fs/fuse/control.c +1 -3
@@ -37,9 +37,7 @@ static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf, {struct fuse_conn *fc =fuse_ctl_file_conn_get (file);if (fc) { -if (fc->abort_err) - fc->aborted = true; -fuse_abort_conn (fc); +fuse_chan_abort (fc, fc->abort_err);fuse_conn_put (fc); }return count;
fs/fuse/cuse.c +2 -2
@@ -425,7 +425,7 @@ static void cuse_process_init_reply(struct fuse_mount *fm, err_region:unregister_chrdev_region (devt,1 ); err: -fuse_abort_conn (fc); +fuse_chan_abort (fc, false); goto out; }
@@ -593,7 +593,7 @@ static ssize_t cuse_class_abort_store(struct device *dev, {struct cuse_conn *cc =dev_get_drvdata (dev); -fuse_abort_conn (&cc->fc); +fuse_chan_abort (&cc->fc, false);return count; } static DEVICE_ATTR(abort, 0200, NULL, cuse_class_abort_store);
fs/fuse/dev.c +19 -16
@@ -87,13 +87,13 @@ void fuse_check_timeout(struct work_struct *work) expired =fuse_request_expired (fc, &fiq->pending);spin_unlock (&fiq->lock);if (expired) - goto abort_conn; + goto chan_abort;spin_lock (&fc->bg_lock); expired =fuse_request_expired (fc, &fc->bg_queue);spin_unlock (&fc->bg_lock);if (expired) - goto abort_conn; + goto chan_abort;spin_lock (&fc->lock);if (!fc->connected) {
@@ -107,7 +107,7 @@ void fuse_check_timeout(struct work_struct *work) fuse_fpq_processing_expired(fc, fpq->processing)) {spin_unlock (&fpq->lock);spin_unlock (&fc->lock); - goto abort_conn; + goto chan_abort; }spin_unlock (&fpq->lock);
@@ -115,15 +115,15 @@ void fuse_check_timeout(struct work_struct *work)spin_unlock (&fc->lock);if (fuse_uring_request_expired (fc)) - goto abort_conn; + goto chan_abort; out:queue_delayed_work (system_percpu_wq, &fc->timeout.work, fuse_timeout_timer_freq);return ; -abort_conn: -fuse_abort_conn (fc); +chan_abort: +fuse_chan_abort (fc, false); }static void fuse_request_init (struct fuse_mount *fm,struct fuse_req *req)
@@ -180,7 +180,7 @@ static void fuse_drop_waiting(struct fuse_conn *fc) {/* * lockess check of fc->connected is okay, because atomic_dec_and_test() -* provides a memory barrier matched with the one in fuse_wait_aborted() +* provides a memory barrier matched with the one in fuse_chan_wait_aborted() * to ensure no wake-up is missed. */ if (atomic_dec_and_test(&fc->num_waiting) &&
@@ -551,11 +551,11 @@ bool fuse_remove_pending_req(struct fuse_req *req, spinlock_t *lock)static void request_wait_answer (struct fuse_req *req) { -struct fuse_conn *fc = req->fm->fc; -struct fuse_iqueue *fiq = &fc->iq; +struct fuse_conn *fch = req->fm->fc; +struct fuse_iqueue *fiq = &fch->iq;int err; -if (!fc->no_interrupt) { +if (!fch->no_interrupt) {/* Any signal may interrupt this */ err =wait_event_interruptible (req->waitq,test_bit (FR_FINISHED , &req->flags));
@@ -579,7 +579,7 @@ static void request_wait_answer(struct fuse_req *req)return ;if (req->args->abort_on_kill) { -fuse_abort_conn (fc); +fuse_chan_abort (fch, false);return ; }
@@ -1491,7 +1491,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,spin_lock (&fpq->lock);/* * Must not put request on fpq->io queue after having been shut down by -* fuse_abort_conn() +* fuse_chan_abort() */ if (!fpq->connected) { req->out.h.error = err = -ECONNABORTED ;
@@ -2475,10 +2475,13 @@ static void end_polls(struct fuse_conn *fc)* is OK, the request will in that case be removed from the list before we touch * it. */ -void fuse_abort_conn (struct fuse_conn *fc) +void fuse_chan_abort (struct fuse_conn *fc,bool abort_with_err) {struct fuse_iqueue *fiq = &fc->iq; +if (abort_with_err) + fc->aborted = true; +spin_lock (&fc->lock);if (fc->connected) {struct fuse_dev *fud;
@@ -2547,9 +2550,9 @@ void fuse_abort_conn(struct fuse_conn *fc)spin_unlock (&fc->lock); } } -EXPORT_SYMBOL_GPL (fuse_abort_conn); +EXPORT_SYMBOL_GPL (fuse_chan_abort); -void fuse_wait_aborted (struct fuse_conn *fc) +void fuse_chan_wait_aborted (struct fuse_conn *fc) {/* matches implicit memory barrier in fuse_drop_waiting() */ smp_mb ();
@@ -2579,7 +2582,7 @@ int fuse_dev_release(struct inode *inode, struct file *file)/* Are we the last open device? */ if (atomic_dec_and_test (&fc->dev_count)) {WARN_ON (fc->iq.fasync !=NULL ); -fuse_abort_conn (fc); +fuse_chan_abort (fc, false); }fuse_dev_free (fud); }
fs/fuse/fuse_i.h +2 -2
@@ -1272,8 +1272,8 @@ void fuse_request_assign_unique(struct fuse_iqueue *fiq, struct fuse_req *req);void fuse_request_end (struct fuse_req *req);/* Abort all requests */ -void fuse_abort_conn (struct fuse_conn *fc); -void fuse_wait_aborted (struct fuse_conn *fc); +void fuse_chan_abort (struct fuse_conn *fc,bool abort_with_err); +void fuse_chan_wait_aborted (struct fuse_conn *fc);/* Check if any requests timed out */ void fuse_check_timeout (struct work_struct *work);
fs/fuse/inode.c +3 -3
@@ -621,7 +621,7 @@ static void fuse_umount_begin(struct super_block *sb)if (fc->no_force_umount)return ; -fuse_abort_conn (fc); +fuse_chan_abort (fc, false);// Only retire block-device-based superblocks. if (sb->s_bdev !=NULL )
@@ -2099,8 +2099,8 @@ void fuse_conn_destroy(struct fuse_mount *fm)if (fc->destroy)fuse_send_destroy (fm); -fuse_abort_conn (fc); -fuse_wait_aborted (fc); +fuse_chan_abort (fc, false); +fuse_chan_wait_aborted (fc);if (!list_empty (&fc->entry)) {mutex_lock (&fuse_mutex);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 fuse: split off fuse_args and related definitions into a separate header
From: Miklos Szeredi <[email protected]>
[ Upstream commit 229f9b9b66ab5be9e015422cf30b97740cfbdc8d ]
This is going to be used by both layers (transport and filesystem)
Stable-dep-of: 64b0b5cacbd2 ("fuse: wait for FR_FINISHED on abort_on_kill to prevent use-after-free")
--- fs/fuse/args.h | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 53 +-------------------------------------- 2 files changed, 66 insertions(+), 52 deletions(-) create mode 100644 fs/fuse/args.h
fs/fuse/args.h new file +65 -0
@@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _FS_FUSE_ARGS_H +#define _FS_FUSE_ARGS_H + +#include <linux/types.h> + +struct fuse_mount ; + +/** One input argument of a request */ +struct fuse_in_arg { +unsigned size; +const void *value; +}; + +/** One output argument of a request */ +struct fuse_arg { +unsigned size; +void *value; +}; + +struct fuse_args { +u64 nodeid; +u32 opcode; +u32 uid; +u32 gid; +u32 pid; +u8 in_numargs; +u8 out_numargs; +u8 ext_idx; +bool force:1 ; +bool noreply:1 ; +bool nocreds:1 ; +bool in_pages:1 ; +bool out_pages:1 ; +bool user_pages:1 ; +bool out_argvar:1 ; +bool page_zeroing:1 ; +bool page_replace:1 ; +bool may_block:1 ; +bool is_ext:1 ; +bool is_pinned:1 ; +bool invalidate_vmap:1 ; +bool abort_on_kill:1 ; +struct fuse_in_arg in_args[4 ]; +struct fuse_arg out_args[2 ]; +void (*end)(struct fuse_mount *fm,struct fuse_args *args,int error); +/* Used for kvec iter backed by vmalloc address */ +void *vmap_base; +}; + +/** FUSE folio descriptor */ +struct fuse_folio_desc { +unsigned int length; +unsigned int offset; +}; + +struct fuse_args_pages { +struct fuse_args args; +struct folio **folios; +struct fuse_folio_desc *descs; +unsigned int num_folios; +}; + +#endif /* _FS_FUSE_ARGS_H */
fs/fuse/fuse_i.h +1 -52
65 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 fuse: remove fm arg of args->end callback
In reply to Sasha LevinFrom: Miklos Szeredi <[email protected]>
[ Upstream commit a697d95fcdbb3bbe25cdc29db5542ddcebb831c1 ]
Only used by FUSE_INIT and CUSE_INIT, these can store the relevant pointer in their structs derived from fuse_args.
Stable-dep-of: 64b0b5cacbd2 ("fuse: wait for FR_FINISHED on abort_on_kill to prevent use-after-free")
--- fs/fuse/args.h | 2 +- fs/fuse/cuse.c | 7 ++++--- fs/fuse/dev.c | 7 +++---- fs/fuse/file.c | 22 +++++++++------------- fs/fuse/inode.c | 10 ++++++---- 5 files changed, 23 insertions(+), 25 deletions(-)
fs/fuse/args.h +1 -1
@@ -44,7 +44,7 @@ struct fuse_args { bool abort_on_kill:1;struct fuse_in_arg in_args[4 ];struct fuse_arg out_args[2 ]; -void (*end)(struct fuse_mount *fm,struct fuse_args *args,int error); +void (*end)(struct fuse_args *args,int error);/* Used for kvec iter backed by vmalloc address */ void *vmap_base; };
fs/fuse/cuse.c +4 -3
@@ -306,6 +306,7 @@ struct cuse_init_args {struct cuse_init_out out;struct folio *folio;struct fuse_folio_desc desc; +struct fuse_conn *fc; };/**
@@ -319,11 +320,10 @@ struct cuse_init_args {* required data structures for it. Please read the comment at the * top of this file for high level overview. */ -static void cuse_process_init_reply (struct fuse_mount *fm, -struct fuse_args *args,int error) +static void cuse_process_init_reply (struct fuse_args *args,int error) { -struct fuse_conn *fc = fm->fc;struct cuse_init_args *ia =container_of (args,typeof (*ia), ap.args); +struct fuse_conn *fc = ia->fc;struct fuse_args_pages *ap = &ia->ap;struct cuse_conn *cc =fc_to_cc (fc), *pos;struct cuse_init_out *arg = &ia->out;
@@ -468,6 +468,7 @@ static int cuse_send_init(struct cuse_conn *cc) ap->descs = &ia->desc; ia->folio = folio; ia->desc.length = ap->args.out_args[1 ].size; + ia->fc = &cc->fc; ap->args.end = cuse_process_init_reply; rc =fuse_simple_background (fm, &ap->args,GFP_KERNEL );
fs/fuse/dev.c +3 -4
@@ -512,7 +512,7 @@ void fuse_request_end(struct fuse_req *req) }if (test_bit (FR_ASYNC , &req->flags)) - req->args->end (fm, req->args, req->out.h.error); + req->args->end (req->args, req->out.h.error); put_request:fuse_put_request (req); }
@@ -1859,8 +1859,7 @@ struct fuse_retrieve_args {struct fuse_notify_retrieve_in inarg; }; -static void fuse_retrieve_end (struct fuse_mount *fm,struct fuse_args *args, -int error) +static void fuse_retrieve_end (struct fuse_args *args,int error) {struct fuse_retrieve_args *ra =container_of (args,typeof (*ra), ap.args);
@@ -1955,7 +1954,7 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode, err =fuse_simple_notify_reply (fm, args, outarg->notify_unique);if (err) -fuse_retrieve_end (fm, args, err); +fuse_retrieve_end (args, err);return err; }
fs/fuse/file.c +9 -13
@@ -91,8 +91,7 @@ static struct fuse_file *fuse_file_get(struct fuse_file *ff)return ff; } -static void fuse_release_end (struct fuse_mount *fm,struct fuse_args *args, -int error) +static void fuse_release_end (struct fuse_args *args,int error) {struct fuse_release_args *ra =container_of (args,typeof (*ra), args);
@@ -112,15 +111,15 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)if (!args) {/* Do nothing when server does not implement 'opendir' */ }else if (args->opcode ==FUSE_RELEASE && ff->fm->fc->no_open) { -fuse_release_end (ff->fm, args,0 ); +fuse_release_end (args,0 ); }else if (sync) {fuse_simple_request (ff->fm, args); -fuse_release_end (ff->fm, args,0 ); +fuse_release_end (args,0 ); }else { args->end = fuse_release_end;if (fuse_simple_background (ff->fm, args,GFP_KERNEL | __GFP_NOFAIL)) -fuse_release_end (ff->fm, args, -ENOTCONN ); +fuse_release_end (args, -ENOTCONN ); }kfree (ff); }
@@ -709,8 +708,7 @@ static void fuse_io_free(struct fuse_io_args *ia)kfree (ia); } -static void fuse_aio_complete_req (struct fuse_mount *fm,struct fuse_args *args, -int err) +static void fuse_aio_complete_req (struct fuse_args *args,int err) {struct fuse_io_args *ia =container_of (args,typeof (*ia), ap.args);struct fuse_io_priv *io = ia->io;
@@ -758,7 +756,7 @@ static ssize_t fuse_async_req_send(struct fuse_mount *fm, ia->ap.args.may_block = io->should_dirty; err =fuse_simple_background (fm, &ia->ap.args,GFP_KERNEL );if (err) -fuse_aio_complete_req (fm, &ia->ap.args, err); +fuse_aio_complete_req (&ia->ap.args, err);return num_bytes; }
@@ -881,8 +879,7 @@ static int fuse_iomap_read_folio_range(const struct iomap_iter *iter,return fuse_do_readfolio (file, folio, off, len); } -static void fuse_readpages_end (struct fuse_mount *fm,struct fuse_args *args, -int err) +static void fuse_readpages_end (struct fuse_args *args,int err) {int i;struct fuse_io_args *ia =container_of (args,typeof (*ia), ap.args);
@@ -947,7 +944,7 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file, res =fuse_simple_request (fm, &ap->args); err = res <0 ? res :0 ; } -fuse_readpages_end (fm, &ap->args, err); +fuse_readpages_end (&ap->args, err); }static void fuse_readahead (struct readahead_control *rac)
@@ -1950,8 +1947,7 @@ __acquires(fi->lock) } } -static void fuse_writepage_end (struct fuse_mount *fm,struct fuse_args *args, -int error) +static void fuse_writepage_end (struct fuse_args *args,int error) {struct fuse_writepage_args *wpa =container_of (args,typeof (*wpa), ia.ap.args);
fs/fuse/inode.c +6 -4
@@ -1311,13 +1311,14 @@ struct fuse_init_args {struct fuse_args args;struct fuse_init_in in;struct fuse_init_out out; +struct fuse_mount *fm; }; -static void process_init_reply (struct fuse_mount *fm,struct fuse_args *args, -int error) +static void process_init_reply (struct fuse_args *args,int error) { -struct fuse_conn *fc = fm->fc;struct fuse_init_args *ia =container_of (args,typeof (*ia), args); +struct fuse_mount *fm = ia->fm; +struct fuse_conn *fc = fm->fc;struct fuse_init_out *arg = &ia->out;bool ok = true;
@@ -1486,6 +1487,7 @@ static struct fuse_init_args *fuse_new_init(struct fuse_mount *fm) ia =kzalloc (sizeof (*ia),GFP_KERNEL | __GFP_NOFAIL); + ia->fm = fm; ia->in.major =FUSE_KERNEL_VERSION ; ia->in.minor =FUSE_KERNEL_MINOR_VERSION ; ia->in.max_readahead = fm->sb->s_bdi->ra_pages *PAGE_SIZE ;
@@ -1559,7 +1561,7 @@ int fuse_send_init(struct fuse_mount *fm)if (!err)return 0 ; } -process_init_reply (fm, &ia->args, err); +process_init_reply (&ia->args, err);if (fm->fc->conn_error)return -ENOTCONN ;return 0 ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 fuse: wait for FR_FINISHED on abort_on_kill to prevent use-after-free
In reply to Sasha LevinFrom: Rochan Avlur <[email protected]>
[ Upstream commit 64b0b5cacbd2fea88001464cb712c9dfc795b26e ]
The abort_on_kill path in request_wait_answer() calls fuse_abort_conn() and returns without waiting for FR_FINISHED. If fuse_dev_do_write() is concurrently processing the same request (FR_LOCKED set), the caller frees req->args while it is still being accessed, causing a use-after-free.
Fix this by jumping to the existing wait_event(FR_FINISHED) instead of returning early. The wait will not hang because fuse_abort_conn() ensures all requests are ended.
--- fs/fuse/dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/fuse/dev.c +2 -1
@@ -580,7 +580,7 @@ static void request_wait_answer(struct fuse_req *req)if (req->args->abort_on_kill) {fuse_chan_abort (fch, false); -return ; + goto wait_for_finish; }if (test_bit (FR_URING , &req->flags))
@@ -591,6 +591,7 @@ static void request_wait_answer(struct fuse_req *req)return ; } +wait_for_finish:/* * Either request is already in userspace, or it was forced. * Wait it out.
Signature · 1 line
2.53.0
Re: [PATCH 6.18.y 1/4] fuse: abort related layering cleanup
In reply to Sasha LevinQuoted 273 lines · Sasha Levin
From: Miklos Szeredi <[email protected]>
[ Upstream commit
4eeb5e6cb0fd89b343fcf755ae3aad76fdb5b2c2]- rename
fuse_abort_conn()tofuse_chan_abort(), passfuse_chanpointer instead offuse_conn- pass an
abort_with_errargument that tellsfuse_dev_(read|write)to return with ECONNABORTED instead of ENODEV- move
fc->abortedtofch->abort_with_err- rename
fuse_wait_aborted()tofuse_chan_wait_aborted()Signed-off-by Miklos Szeredi <[email protected]>[ 6.18 has no "struct
fuse_chan" -- the connection object still plays
that role, and the surrounding series that splits it out is not in stable. Adapted as follows:- the renames (
fuse_abort_conn()->fuse_chan_abort(),fuse_wait_aborted()->fuse_chan_wait_aborted(), the "abort_conn" label infuse_check_timeout()-> "chan_abort") and the newabort_with_errargument are taken as-is, but both helpers keep taking "structfuse_conn*".- the "move
fc->abortedtofch->abort_with_err" bullet is dropped:
there is nofuse_chanto move the flag into, sofc->abortedstays in structfuse_conn.fuse_chan_abort()only ever *sets* it, ascontrol.cdid before, rather than assigning the new argument unconditionally the way upstream does: on this tree the flag is write-once and is what makesfuse_dev_do_read()andfuse_uring_cmd()report ECONNABORTED instead of ENODEV, so letting a later abort with abort_with_err=false clear it (umount -f,fuse_conn_destroy(),fuse_dev_release(), the timeout worker) would change what userspace sees after a sysfs abort on anabort_errmount. Consequently thefuse_dev_i.handdev_uring.chunks, which only rename the field access, are dropped as well.-
fs/fuse/dev.handfs/fuse/req_timeout.cdo not exist here: the declarations stay infuse_i.handfuse_check_timeout()lives indev.c, so those two hunks were replayed there.- in
request_wait_answer()the connection pointer is named "fch", matching the name upstream gives it in28d733cc3ecad("fuse: changereq->fmtoreq->chan"), so that the follow-up fix applies here unchanged. ][ sashal: Reduced backport -- upstream
4eeb5e6cb0fd8touches 9 file(s), this
backport carries 5. Not backported here:
fs/fuse/dev.h
fs/fuse/dev_uring.c
fs/fuse/fuse_dev_i.h
fs/fuse/req_timeout.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]Stable-dep-of:
64b0b5cacbd2("fuse: wait forFR_FINISHEDonabort_on_killto prevent use-after-free")Signed-off-by Sasha Levin <[email protected]>--- fs/fuse/control.c | 4 +--- fs/fuse/cuse.c | 4 ++-- fs/fuse/dev.c | 35 +++++++++++++++++++---------------- fs/fuse/fuse_i.h | 4 ++-- fs/fuse/inode.c | 6 +++--- 5 files changed, 27 insertions(+), 26 deletions(-)fs/fuse/control.c +1
-3@@ -37,9 +37,7 @@ static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf, {struct fuse_conn *fc =fuse_ctl_file_conn_get (file);if (fc) { -if (fc->abort_err) - fc->aborted = true; -fuse_abort_conn (fc); +fuse_chan_abort (fc, fc->abort_err);fuse_conn_put (fc); }return count;fs/fuse/cuse.c +2
-2@@ -425,7 +425,7 @@ static void cuse_process_init_reply(struct fuse_mount *fm, err_region:unregister_chrdev_region (devt,1 ); err: -fuse_abort_conn (fc); +fuse_chan_abort (fc, false); goto out; }@@ -593,7 +593,7 @@ static ssize_t cuse_class_abort_store(struct device *dev, {struct cuse_conn *cc =dev_get_drvdata (dev); -fuse_abort_conn (&cc->fc); +fuse_chan_abort (&cc->fc, false);return count; } static DEVICE_ATTR(abort, 0200, NULL, cuse_class_abort_store);fs/fuse/dev.c +19
-16@@ -87,13 +87,13 @@ void fuse_check_timeout(struct work_struct *work) expired =fuse_request_expired (fc, &fiq->pending);spin_unlock (&fiq->lock);if (expired) - goto abort_conn; + goto chan_abort;spin_lock (&fc->bg_lock); expired =fuse_request_expired (fc, &fc->bg_queue);spin_unlock (&fc->bg_lock);if (expired) - goto abort_conn; + goto chan_abort;spin_lock (&fc->lock);if (!fc->connected) {@@ -107,7 +107,7 @@ void fuse_check_timeout(struct work_struct *work) fuse_fpq_processing_expired(fc, fpq->processing)) {spin_unlock (&fpq->lock);spin_unlock (&fc->lock); - goto abort_conn; + goto chan_abort; }spin_unlock (&fpq->lock);@@ -115,15 +115,15 @@ void fuse_check_timeout(struct work_struct *work)spin_unlock (&fc->lock);if (fuse_uring_request_expired (fc)) - goto abort_conn; + goto chan_abort; out:queue_delayed_work (system_percpu_wq, &fc->timeout.work, fuse_timeout_timer_freq);return ; -abort_conn: -fuse_abort_conn (fc); +chan_abort: +fuse_chan_abort (fc, false); }static void fuse_request_init (struct fuse_mount *fm,struct fuse_req *req)@@ -180,7 +180,7 @@ static void fuse_drop_waiting(struct fuse_conn *fc) {/* * lockess check of fc->connected is okay, because atomic_dec_and_test() -* provides a memory barrier matched with the one in fuse_wait_aborted() +* provides a memory barrier matched with the one in fuse_chan_wait_aborted() * to ensure no wake-up is missed. */ if (atomic_dec_and_test(&fc->num_waiting) &&@@ -551,11 +551,11 @@ bool fuse_remove_pending_req(struct fuse_req *req, spinlock_t *lock)static void request_wait_answer (struct fuse_req *req) { -struct fuse_conn *fc = req->fm->fc; -struct fuse_iqueue *fiq = &fc->iq; +struct fuse_conn *fch = req->fm->fc; +struct fuse_iqueue *fiq = &fch->iq;int err; -if (!fc->no_interrupt) { +if (!fch->no_interrupt) {/* Any signal may interrupt this */ err =wait_event_interruptible (req->waitq,test_bit (FR_FINISHED , &req->flags));@@ -579,7 +579,7 @@ static void request_wait_answer(struct fuse_req *req)return ;if (req->args->abort_on_kill) { -fuse_abort_conn (fc); +fuse_chan_abort (fch, false);return ; }@@ -1491,7 +1491,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,spin_lock (&fpq->lock);/* * Must not put request on fpq->io queue after having been shut down by -* fuse_abort_conn() +* fuse_chan_abort() */ if (!fpq->connected) { req->out.h.error = err = -ECONNABORTED ;@@ -2475,10 +2475,13 @@ static void end_polls(struct fuse_conn *fc)* is OK, the request will in that case be removed from the list before we touch * it. */ -void fuse_abort_conn (struct fuse_conn *fc) +void fuse_chan_abort (struct fuse_conn *fc,bool abort_with_err) {struct fuse_iqueue *fiq = &fc->iq; +if (abort_with_err) + fc->aborted = true; +spin_lock (&fc->lock);if (fc->connected) {struct fuse_dev *fud;@@ -2547,9 +2550,9 @@ void fuse_abort_conn(struct fuse_conn *fc)spin_unlock (&fc->lock); } } -EXPORT_SYMBOL_GPL (fuse_abort_conn); +EXPORT_SYMBOL_GPL (fuse_chan_abort); -void fuse_wait_aborted (struct fuse_conn *fc) +void fuse_chan_wait_aborted (struct fuse_conn *fc) {/* matches implicit memory barrier in fuse_drop_waiting() */ smp_mb ();@@ -2579,7 +2582,7 @@ int fuse_dev_release(struct inode *inode, struct file *file)/* Are we the last open device? */ if (atomic_dec_and_test (&fc->dev_count)) {WARN_ON (fc->iq.fasync !=NULL ); -fuse_abort_conn (fc); +fuse_chan_abort (fc, false); }fuse_dev_free (fud); }fs/fuse/fuse_i.h +2
-2@@ -1272,8 +1272,8 @@ void fuse_request_assign_unique(struct fuse_iqueue *fiq, struct fuse_req *req);void fuse_request_end (struct fuse_req *req);/* Abort all requests */ -void fuse_abort_conn (struct fuse_conn *fc); -void fuse_wait_aborted (struct fuse_conn *fc); +void fuse_chan_abort (struct fuse_conn *fc,bool abort_with_err); +void fuse_chan_wait_aborted (struct fuse_conn *fc);/* Check if any requests timed out */ void fuse_check_timeout (struct work_struct *work);fs/fuse/inode.c +3
-3@@ -621,7 +621,7 @@ static void fuse_umount_begin(struct super_block *sb)if (fc->no_force_umount)return ; -fuse_abort_conn (fc); +fuse_chan_abort (fc, false);// Only retire block-device-based superblocks. if (sb->s_bdev !=NULL )@@ -2099,8 +2099,8 @@ void fuse_conn_destroy(struct fuse_mount *fm)if (fc->destroy)fuse_send_destroy (fm); -fuse_abort_conn (fc); -fuse_wait_aborted (fc); +fuse_chan_abort (fc, false); +fuse_chan_wait_aborted (fc);if (!list_empty (&fc->entry)) {mutex_lock (&fuse_mutex);Signature · 1 line
2.53.0
Series did not apply :(