[PATCH RFC] fuse: abort connection on /dev/fuse flush to prevent deadlock

"syzbot" <[email protected]> Thu, 9 Jul 2026 14:30:55 +0000 (UTC)
Newsgroups dev.linux.lists.syzbot
Message-ID <[email protected]>
A single-threaded FUSE daemon can deadlock if it mounts a filesystem and
opens a file on it. When the process exits or calls close_range(), the
kernel closes file descriptors in ascending numerical order.

If the /dev/fuse file descriptor (e.g., fd 5) is closed first, filp_close()
decrements the file reference count but defers the actual release (fput) to
task work. As a result, the FUSE connection remains active.

Next, when the FUSE file descriptor (e.g., fd 6) is closed, filp_close()
calls fuse_flush(), which sends a synchronous FUSE_FLUSH request to the
daemon. Since the daemon is the same thread that is currently blocked in
the close() syscall, it cannot process the request. The FUSE_FLUSH request
uses args.force = true, making the wait uninterruptible. The thread hangs
forever, eventually triggering a hung task panic:

INFO: task syz.0.17:6117 blocked for more than 143 seconds.
...
Call Trace:
 <TASK>
 __schedule+0x17d9/0x56c0 kernel/sched/core.c:7234
 schedule+0x164/0x2b0 kernel/sched/core.c:7326
 request_wait_answer fs/fuse/dev.c:743 [inline]
 __fuse_request_send fs/fuse/dev.c:757 [inline]
 fuse_chan_send+0x1065/0x1ab0 fs/fuse/dev.c:833
 fuse_simple_request fs/fuse/fuse_i.h:1012 [inline]
 fuse_flush+0x66e/0x8b0 fs/fuse/file.c:504
 filp_flush+0xbd/0x190 fs/open.c:1471
 filp_close+0x1d/0x40 fs/open.c:1484
 __range_close fs/file.c:793 [inline]
 __do_sys_close_range fs/file.c:854 [inline]
 __se_sys_close_range+0x3d3/0x900 fs/file.c:818
 do_syscall_64+0x15f/0x560 arch/x86/entry/syscall_64.c:94

To fix this, implement a .flush method for /dev/fuse (fuse_dev_operations).
The .flush method is called synchronously by filp_close() during the
close() syscall, bypassing the fput delay.

In fuse_dev_flush(), if this is the last reference to the file descriptor
(file_count(file) == 1) and it is the last device for the FUSE connection,
we proactively abort the FUSE connection. By aborting the connection
synchronously in .flush, any subsequent fuse_flush() calls on FUSE files
will immediately fail with -ENOTCONN instead of blocking indefinitely,
avoiding the deadlock.

Fixes: 4a9d4b024a31 ("switch fput to task_work_add")
Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=0dbb0d6fda088e78a4d8
Link: https://syzkaller.appspot.com/ai_job?id=2b6a3623-6dc9-49c8-ada9-03747a9847d2
To: <[email protected]>
To: "Miklos Szeredi" <[email protected]>
Cc: <[email protected]>

---
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 5763a7cd3..89e2dea87 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2375,23 +2375,43 @@ static void fuse_dev_show_fdinfo(struct seq_file *seq, struct file *file)
 }
 #endif
 
+static int fuse_dev_flush(struct file *file, fl_owner_t id)
+{
+	struct fuse_dev *fud = fuse_file_to_fud(file);
+	struct fuse_chan *fch = READ_ONCE(fud->chan);
+
+	if (fch && fch != FUSE_DEV_CHAN_DISCONNECTED && file_count(file) == 1) {
+		bool last;
+
+		spin_lock(&fch->lock);
+		last = list_is_singular(&fch->devices);
+		spin_unlock(&fch->lock);
+
+		if (last)
+			fuse_chan_abort(fch, false);
+	}
+
+	return 0;
+}
+
 const struct file_operations fuse_dev_operations = {
-	.owner		= THIS_MODULE,
-	.open		= fuse_dev_open,
-	.read_iter	= fuse_dev_read,
-	.splice_read	= fuse_dev_splice_read,
-	.write_iter	= fuse_dev_write,
-	.splice_write	= fuse_dev_splice_write,
-	.poll		= fuse_dev_poll,
-	.release	= fuse_dev_release,
-	.fasync		= fuse_dev_fasync,
+	.owner = THIS_MODULE,
+	.open = fuse_dev_open,
+	.read_iter = fuse_dev_read,
+	.splice_read = fuse_dev_splice_read,
+	.write_iter = fuse_dev_write,
+	.splice_write = fuse_dev_splice_write,
+	.poll = fuse_dev_poll,
+	.flush = fuse_dev_flush,
+	.release = fuse_dev_release,
+	.fasync = fuse_dev_fasync,
 	.unlocked_ioctl = fuse_dev_ioctl,
-	.compat_ioctl   = compat_ptr_ioctl,
+	.compat_ioctl = compat_ptr_ioctl,
 #ifdef CONFIG_FUSE_IO_URING
-	.uring_cmd	= fuse_uring_cmd,
+	.uring_cmd = fuse_uring_cmd,
 #endif
 #ifdef CONFIG_PROC_FS
-	.show_fdinfo	= fuse_dev_show_fdinfo,
+	.show_fdinfo = fuse_dev_show_fdinfo,
 #endif
 };
 EXPORT_SYMBOL_GPL(fuse_dev_operations);


base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda
-- 
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).

See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at [email protected].