Skip to contentSkip to conversation

FAILED: patch "[PATCH] fuse: fix readdir cache race" failed to apply to 5.15-stable tree

PATCH [email protected] 1 participant 1 message Nov 7, 2022

Subsystems (2)
lore
Share & tools
lore

[email protected]

author maintainer
#0 lore raw

The patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

Possible dependencies:

9fa248c65bdb ("fuse: fix readdir cache race")
5fe0fc9f1de6 ("fuse: use kmap_local_page()")

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From 9fa248c65bdbf5af0a2f74dd38575acfc8dfd2bf Mon Sep 17 00:00:00 2001
From: Miklos Szeredi <[email protected]>
Date: Thu, 20 Oct 2022 17:18:58 +0200
Subject: [PATCH] fuse: fix readdir cache race

There's a race in fuse's readdir cache that can result in an uninitilized page being read. The page lock is supposed to prevent this from happening but in the following case it doesn't:

Two fuse_add_dirent_to_cache() start out and get the same parameters (size=0,offset=0). One of them wins the race to create and lock the page, after which it fills in data, sets rdc.size and unlocks the page.

In the meantime the page gets evicted from the cache before the other instance gets to run. That one also creates the page, but finds the size to be mismatched, bails out and leaves the uninitialized page in the cache.

Fix by marking a filled page uptodate and ignoring non-uptodate pages.

Fixes 5d7bc7e8680c ("fuse: allow using readdir cache")
Cc <[email protected]> # v4.20
fs/fuse/readdir.c +9 -1
index b4e565711045..e8deaacf1832 100644
@@ -77,8 +77,10 @@ static void fuse_add_dirent_to_cache(struct file *file,
 		goto unlock;
 
 	addr = kmap_local_page(page);
-	if (!offset)
+	if (!offset) {
 		clear_page(addr);
+		SetPageUptodate(page);
+	}
 	memcpy(addr + offset, dirent, reclen);
 	kunmap_local(addr);
 	fi->rdc.size = (index << PAGE_SHIFT) + offset + reclen;
@@ -516,6 +518,12 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
 
 	page = find_get_page_flags(file->f_mapping, index,
 				   FGP_ACCESSED | FGP_LOCK);
+	/* Page gone missing, then re-added to cache, but not initialized? */
+	if (page && !PageUptodate(page)) {
+		unlock_page(page);
+		put_page(page);
+		page = NULL;
+	}
 	spin_lock(&fi->rdc.lock);
 	if (!page) {
 		/*