From: Sasha Levin <sashal@kernel.org>
To: stable@vger.kernel.org
Cc: Chris Mason <mason@kernel.org>,
Josef Bacik <josef@toxicpanda.com>,
Amir Goldstein <amir73il@gmail.com>,
Sasha Levin <sashal@kernel.org>
Subject: [PATCH 6.1.y] ovl: hold a reference on the parent in readdir
Date: Wed, 4 Nov 2026 22:44:34 -0500 [thread overview]
Message-ID: <20261105034401.1253832-1-sashal@kernel.org> (raw)
In-Reply-To: <2026110448-running-steadfast-67b5@gregkh>
From: Chris Mason <mason@kernel.org>
[ Upstream commit 16d319e12328acafeafd3eecdf09c46d7fb43452 ]
Listing a directory on an overlay uses a freed dentry and crashes the
kernel when a lookup moves the directory at the same time.
Code that can sleep may only use d_parent without a reference while
something stops a move of the dentry. ovl_iterate_real(),
ovl_need_adjust_d_ino() and ovl_cache_update() use the d_parent of the
listed directory and hold only the lock of that directory. When a
lookup finds the directory under another parent after a change to a
layer, d_splice_alias() moves it without that lock and drops its
reference on the old parent.
With a delay in ovl_iterate_real(), getdents64 on an overlay directory
raced a rename in the lower layer and a lookup that moved its dentry,
and after an rmdir of the old parent KASAN reported a use-after-free in
security_inode_getattr().
Fix this by using dget_parent() in the three functions.
Fixes: b5efccbe0a12 ("ovl: constant d_ino across copy up")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason <mason@kernel.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
[amir: fix comments in ovl_need_adjust_d_ino()]
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
[ sashal: adapted ovl_need_adjust_d_ino() changes to the existing inline checks in ovl_iterate() ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/overlayfs/readdir.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index 36a26a0ec966f..8eacf0e7625a0 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -474,8 +474,8 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
goto get;
}
if (p->len == 2 && p->name[1] == '.') {
- /* we shall not be moved */
- this = dget(dir->d_parent);
+ /* d_splice_alias() can move even a locked dir */
+ this = dget_parent(dir);
goto get;
}
}
@@ -693,6 +693,7 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx)
struct ovl_dir_file *od = file->private_data;
struct dentry *dir = file->f_path.dentry;
const struct ovl_layer *lower_layer = ovl_layer_lower(dir);
+ struct dentry *parent;
struct ovl_readdir_translate rdt = {
.ctx.actor = ovl_fill_real,
.orig_ctx = ctx,
@@ -703,18 +704,26 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx)
if (rdt.xinobits && lower_layer)
rdt.fsid = lower_layer->fsid;
- if (OVL_TYPE_MERGE(ovl_path_type(dir->d_parent))) {
+ /*
+ * The lock on dir does not stop d_splice_alias() from moving it to
+ * another parent, which drops the reference dir holds on this one.
+ */
+ parent = dget_parent(dir);
+ if (OVL_TYPE_MERGE(ovl_path_type(parent))) {
struct kstat stat;
struct path statpath = file->f_path;
- statpath.dentry = dir->d_parent;
+ statpath.dentry = parent;
err = vfs_getattr(&statpath, &stat, STATX_INO, 0);
- if (err)
+ if (err) {
+ dput(parent);
return err;
+ }
WARN_ON_ONCE(dir->d_sb->s_dev != stat.dev);
rdt.parent_ino = stat.ino;
}
+ dput(parent);
if (ovl_is_impure_dir(file)) {
rdt.cache = ovl_cache_get_impure(&file->f_path);
@@ -742,6 +751,8 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
ovl_dir_reset(file);
if (od->is_real) {
+ struct dentry *parent = dget_parent(dentry);
+
/*
* If parent is merge, then need to adjust d_ino for '..', if
* dir is impure then need to adjust d_ino for copied up
@@ -750,11 +761,12 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
if (ovl_xino_bits(dentry->d_sb) ||
(ovl_same_fs(dentry->d_sb) &&
(ovl_is_impure_dir(file) ||
- OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) {
+ OVL_TYPE_MERGE(ovl_path_type(parent))))) {
err = ovl_iterate_real(file, ctx);
} else {
err = iterate_dir(od->realfile, ctx);
}
+ dput(parent);
goto out;
}
--
2.53.0
prev parent reply other threads:[~2026-11-05 3:44 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 14:34 FAILED: patch "[PATCH] ovl: hold a reference on the parent in readdir" failed to apply to 6.1-stable tree gregkh
2026-11-05 3:44 ` Sasha Levin [this message]
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=20261105034401.1253832-1-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=amir73il@gmail.com \
--cc=josef@toxicpanda.com \
--cc=mason@kernel.org \
--cc=stable@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.