[PATCH RFC] fuse: allow fatal signals to interrupt forced requests

"syzbot" <[email protected]> Thu, 9 Jul 2026 13:32:25 +0000 (UTC)
Newsgroups dev.linux.lists.syzbot
Message-ID <[email protected]>
A single-threaded FUSE server can deadlock itself and cause a hung task
panic if it calls close_range(). This syscall closes all open file
descriptors, including the FUSE file descriptor. Closing the FUSE file
triggers a FUSE_FLUSH request. Because the process is single-threaded and
blocked in the kernel waiting for the flush reply, it cannot read the
request from /dev/fuse and reply to it.

FUSE_FLUSH requests are sent with the FR_FORCE flag. In
request_wait_answer(), forced requests skip the wait_event_killable() block
and wait uninterruptibly (wait_event()). This makes the task completely
unkillable (D state) and triggers the hung task watchdog:

INFO: task 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/0x1ac0 fs/fuse/dev.c:833
 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+0x174/0x580 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
 </TASK>

Historically, FR_FORCE was introduced to prevent FUSE_FLUSH from being
interrupted by normal signals (like SIGINT), as cancelling it could lead to
leaked POSIX locks on the server. However, making it completely
uninterruptible even by fatal signals (SIGKILL) causes this unrecoverable
deadlock.

If a process is killed by a fatal signal, it is acceptable to cancel the
FUSE_FLUSH request. The process is exiting, and all its file descriptors
will be closed, which will eventually trigger a FUSE_RELEASE request. Since
FUSE protocol 7.8, FUSE_RELEASE contains the lock_owner, allowing a
well-written server to clean up any remaining POSIX locks. Even if a poorly
written server leaks locks, a resource leak in a userspace server is vastly
preferable to a kernel panic.

By removing the !test_bit(FR_FORCE, &req->flags) condition around the
wait_event_killable() block in request_wait_answer(), we allow forced
requests to be interrupted by fatal signals. This places the task in the
TASK_KILLABLE state, which the hung task watchdog ignores, preventing the
panic. It also allows the task to be killed by SIGKILL, breaking the
deadlock and allowing the task to exit gracefully.

Fixes: 7142125937e1 ("[PATCH] fuse: add POSIX file locking support")
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=a531d1b1fb0fa2a75a73
Link: https://syzkaller.appspot.com/ai_job?id=864cba22-20c3-4bfa-b461-b7208b3fac7d
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..75943da8c 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -714,10 +714,17 @@ static void request_wait_answer(struct fuse_req *req)
 			queue_interrupt(req);
 	}
 
-	if (!test_bit(FR_FORCE, &req->flags)) {
+	{
 		bool removed;
 
-		/* Only fatal signals may interrupt this */
+		/*
+		 * Only fatal signals may interrupt this.
+		 * Forced requests (like FUSE_FLUSH) used to be completely
+		 * uninterruptible, but it can cause self-deadlocks.
+		 * If killed, we cancel the request. POSIX locks might be
+		 * leaked on the server, but the process is dying anyway,
+		 * and FUSE_RELEASE will be sent.
+		 */
 		err = wait_event_killable(req->waitq,
 					test_bit(FR_FINISHED, &req->flags));
 		if (!err)
@@ -737,8 +744,7 @@ static void request_wait_answer(struct fuse_req *req)
 	}
 
 	/*
-	 * Either request is already in userspace, or it was forced.
-	 * Wait it out.
+	 * Request is already in userspace, wait it out.
 	 */
 	wait_event(req->waitq, test_bit(FR_FINISHED, &req->flags));
 }


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