[PATCH] fuse: preserve ENOTCONN after failed initialization and disconnect

From: hengyul

Date: Sat Oct 17 2026 - 05:48:26 EST


From: Hengyu Liang <hengyul@xxxxxxxxxx>

Commit 794e811d1443 ("fuse: split out filesystem part of request
sending") moved the INIT failure check into fuse_req_prep(), before
the channel checks whether it is still connected.

If a daemon closes /dev/fuse before replying to INIT, aborting the
channel completes INIT with an error. stat() and open() on the mount
then return ECONNREFUSED instead of the previous ENOTCONN. The same
change occurs when INIT is rejected before the daemon closes the fd.

Record INIT failure in the channel before publishing initialized, and
check it in fuse_get_req() after waiting for initialization and checking
for disconnection. This preserves ECONNREFUSED for failed INIT while
the device remains open, and restores ENOTCONN after disconnection.

Set the failure flag only while the channel is connected, since abort
also marks it initialized to wake waiters after a successful INIT.

Fixes: 794e811d1443 ("fuse: split out filesystem part of request sending")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Hengyu Liang <hengyul@xxxxxxxxxx>
---
Tested on x86-64 KVM with KASAN, comparing v7.1, unpatched v7.3-rc7
and patched v7.3-rc7. Checked stat/open before INIT replies, after INIT
refusal with the device open/closed, and after successful INIT and
disconnect. Also checked pending INIT requests, 400 concurrent
INIT-reply/abort iterations, normal FUSE directory operations and
CUSE open/read/write/close. Results match v7.1; no kernel diagnostics.

fs/fuse/dev.c | 8 ++++++++
fs/fuse/fuse_dev_i.h | 3 +++
fs/fuse/req.c | 14 +++-----------
3 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 4fec31fc0b84..168ad7e00510 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -80,6 +80,10 @@ void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *pa
fuse_uring_conn_init(fch);
}

+ /* An aborted channel is also marked initialized to wake waiters. */
+ if (!param && READ_ONCE(fch->connected))
+ fch->conn_error = true;
+
/* Pairs with smp_load_acquire() readers of fch->initialized */
smp_store_release(&fch->initialized, 1);
wake_up_all(&fch->blocked_waitq);
@@ -130,6 +134,10 @@ static struct fuse_req *fuse_get_req(struct fuse_chan *fch, bool for_background)
if (!fch->connected)
goto out;

+ err = -ECONNREFUSED;
+ if (fch->conn_error)
+ goto out;
+
req = fuse_request_alloc(fch, GFP_KERNEL);
err = -ENOMEM;
if (!req) {
diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h
index 4b412a76225f..0ffdc7f57963 100644
--- a/fs/fuse/fuse_dev_i.h
+++ b/fs/fuse/fuse_dev_i.h
@@ -245,6 +245,9 @@ struct fuse_chan {
/** Is interrupt not implemented by fs? */
bool no_interrupt;

+ /** Initialization failed */
+ bool conn_error;
+
/* Use io_uring for communication */
unsigned int io_uring;

diff --git a/fs/fuse/req.c b/fs/fuse/req.c
index a01ee743d31e..eede265b06c1 100644
--- a/fs/fuse/req.c
+++ b/fs/fuse/req.c
@@ -49,19 +49,11 @@ static int fuse_fill_creds(struct fuse_mount *fm, struct fuse_args *args, struct
return 0;
}

-static int fuse_req_prep(struct fuse_mount *fm, struct fuse_args *args, struct mnt_idmap *idmap)
-{
- if (!args->force && fm->fc->conn_error)
- return -ECONNREFUSED;
-
- return fuse_fill_creds(fm, args, idmap);
-}
-
ssize_t __fuse_simple_request(struct mnt_idmap *idmap, struct fuse_mount *fm,
struct fuse_args *args)
{
struct fuse_conn *fc = fm->fc;
- int err = fuse_req_prep(fm, args, idmap);
+ int err = fuse_fill_creds(fm, args, idmap);

if (err)
return err;
@@ -76,7 +68,7 @@ int fuse_simple_background(struct fuse_mount *fm, struct fuse_args *args, gfp_t

WARN_ON(args->force && !args->nocreds);

- err = fuse_req_prep(fm, args, &invalid_mnt_idmap);
+ err = fuse_fill_creds(fm, args, &invalid_mnt_idmap);
if (err)
return err;

@@ -91,7 +83,7 @@ int fuse_simple_notify_reply(struct fuse_mount *fm, struct fuse_args *args, u64

WARN_ON(args->force && !args->nocreds);

- err = fuse_req_prep(fm, args, &invalid_mnt_idmap);
+ err = fuse_fill_creds(fm, args, &invalid_mnt_idmap);
if (err)
return err;

--
2.53.0