]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fuse: re-lock request before replacing page cache folio
authorJoanne Koong <joannelkoong@gmail.com>
Tue, 19 May 2026 05:28:06 +0000 (22:28 -0700)
committerMiklos Szeredi <mszeredi@redhat.com>
Tue, 9 Jun 2026 14:11:02 +0000 (16:11 +0200)
fuse_try_move_folio() unlocks the request on entry but does not
re-lock it on the success path. This means fuse_chan_abort() can end the
request and free the fuse_io_args (eg fuse_readpages_end()) while the
subsequent copy chain logic after fuse_try_move_folio() accesses the
fuse_io_args, leading to use-after-free issues.

Fix this by calling lock_request() before replace_page_cache_folio().
This ensures the request is locked on the success path which will
prevent the fuse_io_args from being freed while the later copying logic
runs, and also ensures that the ap->folios[i]->mapping is never null
since ap->folios[i] will always point to the newfolio after
replace_page_cache_folio().

Fixes: ce534fb05292 ("fuse: allow splice to move pages")
Cc: stable@vger.kernel.org
Reported-by: Lei Lu <llfamsec@gmail.com>
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/dev.c

index 05b6d15afe61e3fbc9c7d8df8fbf013abff809ef..86b62a1d37466526ed0a4dfb93d1819b93a9f9a0 100644 (file)
@@ -1037,6 +1037,10 @@ static int fuse_try_move_folio(struct fuse_copy_state *cs, struct folio **foliop
        if (WARN_ON(folio_test_mlocked(oldfolio)))
                goto out_fallback_unlock;
 
+       err = lock_request(cs->req);
+       if (err)
+               goto out_fallback_unlock;
+
        replace_page_cache_folio(oldfolio, newfolio);
 
        folio_get(newfolio);
@@ -1050,20 +1054,7 @@ static int fuse_try_move_folio(struct fuse_copy_state *cs, struct folio **foliop
         */
        pipe_buf_release(cs->pipe, buf);
 
-       err = 0;
-       spin_lock(&cs->req->waitq.lock);
-       if (test_bit(FR_ABORTED, &cs->req->flags))
-               err = -ENOENT;
-       else
-               *foliop = newfolio;
-       spin_unlock(&cs->req->waitq.lock);
-
-       if (err) {
-               folio_unlock(newfolio);
-               folio_put(newfolio);
-               goto out_put_old;
-       }
-
+       *foliop = newfolio;
        folio_unlock(oldfolio);
        /* Drop ref for ap->pages[] array */
        folio_put(oldfolio);