Re: [PATCH] fuse: virtiofs: fix illegal inode address access in fuse_release_end

From: Zhihao Cheng

Date: Mon Jun 15 2026 - 07:08:49 EST


在 2026/5/16 11:20, Zhihao Cheng 写道:
在 2026/5/16 5:07, Darrick J. Wong 写道:
[add fuse-devel]


friendly ping
On Thu, May 14, 2026 at 08:41:02PM +0800, Zhihao Cheng wrote:
When a submount (e.g. virtiofs with fc->auto_submounts=true) is umounted
while an async RELEASE request is still pending, an illegal inode address
accessing occurs in fuse_release_end()->iput().

Trigger process:
  1. virtiofs has a submount; user opens and closes a file under it
  2. Close calls fuse_file_put() with sync=false, sending RELEASE
     asynchronously
  3. fuse_release_end() is scheduled to run later via igrab() holding
     inode ref
  4. File is freed, mount/dentry refcounts are released
  5. User umounts the submount; fuse connection detects remaining
     superblock and does NOT flush the connection's requests
  6. generic_shutdown_super() destroys the superblock and poisons busy
     inodes' inode->i_sb = VFS_PTR_POISON
  7. Later, fuse_request_end() calls fuse_release_end() which does
     iput(inode)
  8. iput() accesses inode->i_sb->s_op at the poisoned address, crash!

There are two solutions to fix it:
  Solution A: Hold path reference in fuse_file_put, and put path
  synchronously, which could reintroduce the issue fixed by commit
  5a18ec176c934 ("fuse: fix hang of single threaded fuseblk filesystem").

  Solution B (chosen): Use synchronous RELEASE for auto_submounts (which
  is only supported by virtiofs). The virtiofsd(fuse daemon) and virtiofs
  won't work together under one same kernel instance, so the problem fixed
  by commit 26e5c67deb2e ("fuse: fix livelock in synchronous file put from
  fuseblk workers") won't be brought back in virtiofs.

Does the weird AIO "kernel breaks if you close the fd before all the IOs
complete" behavior happen on virtiofsd?


Hi Darrick,
IMHO, the problem fixed by commit 26e5c67deb2e1f42a9("fuse: fix livelock in synchronous file put from fuseblk workers") happens as following process:
   fuse user              fuse daemon
fd = open(file)
io_submit_one
 req->ki_filp = fget(iocb->aio_fildes)
close(fd) // file->f_ref = 1, file won't be released
                        process FUSE_WRITE req
                        write(/dev/fuse, reply)
                         fuse_request_end
                          fuse_aio_complete_req
                           aio_complete_rw
                            fput(iocb->ki_filp)
                             task_work_add
                         task_work_run
                          __fput
                           fuse_release
                            __fuse_simple_request
                             request_wait_answer

For viriofs, the user(virtiofs) runs in kernel A(guest), the daemon(virtiofsd) runs kernel B(host), and the fuse_request_end is called by virtio_fs_requests_done_work -> virtio_fs_complete_req_work -> virtio_fs_request_complete, which is under the kworker context(kernel A, guest), so it won't block the daemon thread(kernel B, host).

--D


Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=221519.

Fixes: 26e5c67deb2e ("fuse: fix livelock in synchronous file put from fuseblk workers")
Signed-off-by: Zhihao Cheng <chengzhihao1@xxxxxxxxxx>
---
  fs/fuse/file.c | 19 +++++++++++++------
  1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index c59452d60b8d..a6192b96d861 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -375,13 +375,20 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff,
       * synchronous RELEASE is allowed (and desirable) in this case
       * because the server can be trusted not to screw up.
       *
-     * Always use the asynchronous file put because the current thread
-     * might be the fuse server.  This can happen if a process starts some
-     * aio and closes the fd before the aio completes.  Since aio takes its
-     * own ref to the file, the IO completion has to drop the ref, which is
-     * how the fuse server can end up closing its clients' files.
+     * For auto_submounts (e.g. virtiofs), always use synchronous
+     * release to avoid illegal inode address access when umount
+     * happens before async release completes. The async release
+     * holds inode reference via igrab(), but umount can shutdown
+     * superblock and poison inode->i_sb before release ends,
+     * causing crash in fuse_release_end()->iput(). Otherwise,
+     * always use the asynchronous file put because the current
+     * thread might be the fuse server. This can happen if a
+     * process starts some aio and closes the fd before the aio
+     * completes. Since aio takes its own ref to the file, the IO
+     * completion has to drop the ref, which is how the fuse server
+     * can end up closing its clients' files.
       */
-    fuse_file_put(ff, false);
+    fuse_file_put(ff, ff->fm->fc->auto_submounts);
  }
  void fuse_release_common(struct file *file, bool isdir)
--
2.52.0


.




.