[PATCH v2 1/3] ovl: disable nfs_export for same-sb lower layers with different idmaps
From: Jérémy Jean
Date: Sun Sep 20 2026 - 16:33:53 EST
Overlay file handles identify a backing filesystem, but not the lower
mount used to decode it. If lower layers share a superblock and use
different idmaps, a file handle can decode through the wrong layer and
initialize the overlay inode with the wrong owner.
Track the first lower idmap seen for each ovl_sb and disable nfs_export
when another lower layer on the same backing superblock uses a different
idmap. Keep index enabled: copy up starts from the overlay dentry selected
by lookup and does not need to decode a lower file handle through another
layer.
Fixes: bc70682a497c ("ovl: support idmapped layers")
Assisted-by: Codex:gpt-5
Signed-off-by: Jérémy Jean <Jeremy.Jean@xxxxxxxxxxxxxxxxx>
---
fs/overlayfs/ovl_entry.h | 2 ++
fs/overlayfs/super.c | 28 +++++++++++++++++++++++++++-
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
index 80cad4ea96a3..2a6e12a0d4dd 100644
--- a/fs/overlayfs/ovl_entry.h
+++ b/fs/overlayfs/ovl_entry.h
@@ -24,6 +24,8 @@ struct ovl_config {
struct ovl_sb {
struct super_block *sb;
dev_t pseudo_dev;
+ /* Idmap of the first lower layer on this fs */
+ struct mnt_idmap *lower_idmap;
/* Unusable (conflicting) uuid */
bool bad_uuid;
/* Used as a lower layer (but maybe also as upper) */
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index bd0a3f9039d2..8e78da420b75 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -944,6 +944,24 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
return true;
}
+/*
+ * Lower file handles identify a lower fs, but not the mount whose idmap was
+ * used to decode them. Remember the first lower idmap seen for each fs so
+ * decodable export handles can be disabled if another one appears.
+ */
+static bool ovl_lower_mnt_idmap_mismatch(struct ovl_sb *fs,
+ const struct path *path)
+{
+ struct mnt_idmap *idmap = mnt_idmap(path->mnt);
+
+ if (!fs->lower_idmap) {
+ fs->lower_idmap = idmap;
+ return false;
+ }
+
+ return fs->lower_idmap != idmap;
+}
+
/* Get a unique fsid for the layer */
static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
{
@@ -956,8 +974,15 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
bool warn = false;
for (i = 0; i < ofs->numfs; i++) {
- if (ofs->fs[i].sb == sb)
+ if (ofs->fs[i].sb == sb) {
+ if (ofs->config.nfs_export &&
+ ovl_lower_mnt_idmap_mismatch(&ofs->fs[i], path)) {
+ ofs->config.nfs_export = false;
+ pr_warn("different idmaps in same lower fs '%pd2', falling back to nfs_export=off.\n",
+ path->dentry);
+ }
return i;
+ }
}
if (!ovl_lower_uuid_ok(ofs, uuid)) {
@@ -987,6 +1012,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
ofs->fs[ofs->numfs].sb = sb;
ofs->fs[ofs->numfs].pseudo_dev = dev;
ofs->fs[ofs->numfs].bad_uuid = bad_uuid;
+ ovl_lower_mnt_idmap_mismatch(&ofs->fs[ofs->numfs], path);
return ofs->numfs++;
}
--
2.47.3