From: Nirmoy Das <nirmoyd@nvidia.com>
To: Miklos Szeredi <miklos@szeredi.hu>, Amir Goldstein <amir73il@gmail.com>
Cc: <linux-unionfs@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
"Nirmoy Das" <nirmoyd@nvidia.com>,
<syzbot+a16fb0cce329a320661c@syzkaller.appspotmail.com>
Subject: [RFC PATCH] ovl: keep merged and impure readdir caches separate
Date: Sun, 10 May 2026 23:20:57 -0700 [thread overview]
Message-ID: <20260511062057.2365769-1-nirmoyd@nvidia.com> (raw)
Overlayfs uses one inode cache slot for two readdir cache users with
different lifetime rules. Merged directory iteration pins the cache from
open directory files with cache->refcount. Impure real-directory iteration
uses the inode cache as an unrefcounted lookup table.
Those caches cannot be reused interchangeably. If merged iteration finds
an impure cache in the inode slot, it can pin and seek through a cache
that was not built for merged iteration. If impure iteration finds a merged
cache, it can walk an object whose lifetime is controlled by open directory
files. Either direction can leave ovl_iterate() using stale cache entries.
Add ovl_dir_cache_drop() to detach the inode cache before freeing it. Keep
refcounted merged caches alive until ovl_cache_put(), stop publishing new
merged caches through the inode slot, and let impure iteration reuse only
unrefcounted caches.
Fixes: 4edb83bb1041 ("ovl: constant d_ino for non-merge dirs")
Reported-by: syzbot+a16fb0cce329a320661c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=a16fb0cce329a320661c
Assisted-by: Codex:GPT-5 [lore] [checkpatch]
Signed-off-by: Nirmoy Das <nirmoyd@nvidia.com>
---
fs/overlayfs/readdir.c | 38 ++++++++++++++++++++++++++------------
1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index 1dcc75b3a90f9..326d8ad173881 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -292,6 +292,27 @@ void ovl_dir_cache_free(struct inode *inode)
}
}
+static void ovl_dir_cache_drop(struct inode *inode)
+{
+ struct ovl_dir_cache *cache = ovl_dir_cache(inode);
+
+ if (!cache)
+ return;
+
+ ovl_set_dir_cache(inode, NULL);
+
+ /*
+ * Merged dir caches are refcounted by open directory files. If the
+ * inode cache is replaced while such a file still references it, keep
+ * the old cache alive until ovl_cache_put().
+ */
+ if (cache->refcount)
+ return;
+
+ ovl_cache_free(&cache->entries);
+ kfree(cache);
+}
+
static void ovl_cache_put(struct ovl_dir_file *od, struct inode *inode)
{
struct ovl_dir_cache *cache = od->cache;
@@ -485,13 +506,7 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
struct ovl_dir_cache *cache;
struct inode *inode = d_inode(dentry);
- cache = ovl_dir_cache(inode);
- if (cache && ovl_inode_version_get(inode) == cache->version) {
- WARN_ON(!cache->refcount);
- cache->refcount++;
- return cache;
- }
- ovl_set_dir_cache(d_inode(dentry), NULL);
+ ovl_dir_cache_drop(inode);
cache = kzalloc_obj(struct ovl_dir_cache);
if (!cache)
@@ -509,7 +524,6 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
}
cache->version = ovl_inode_version_get(inode);
- ovl_set_dir_cache(inode, cache);
return cache;
}
@@ -699,12 +713,12 @@ static struct ovl_dir_cache *ovl_cache_get_impure(const struct path *path)
struct ovl_dir_cache *cache;
cache = ovl_dir_cache(inode);
- if (cache && ovl_inode_version_get(inode) == cache->version)
+ if (cache && !cache->refcount &&
+ ovl_inode_version_get(inode) == cache->version)
return cache;
- /* Impure cache is not refcounted, free it here */
- ovl_dir_cache_free(inode);
- ovl_set_dir_cache(inode, NULL);
+ /* Drop stale or incompatible inode cache before building impure cache */
+ ovl_dir_cache_drop(inode);
cache = kzalloc_obj(struct ovl_dir_cache);
if (!cache)
base-commit: e98d21c170b01ddef366f023bbfcf6b31509fa83
--
2.43.0
next reply other threads:[~2026-05-11 6:21 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-11 6:20 Nirmoy Das [this message]
2026-05-11 20:54 ` [RFC PATCH] ovl: keep merged and impure readdir caches separate Amir Goldstein
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260511062057.2365769-1-nirmoyd@nvidia.com \
--to=nirmoyd@nvidia.com \
--cc=amir73il@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-unionfs@vger.kernel.org \
--cc=miklos@szeredi.hu \
--cc=syzbot+a16fb0cce329a320661c@syzkaller.appspotmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox