diff options
| author | Christian Brauner <brauner@kernel.org> | 2025-09-02 10:40:58 +0200 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2025-09-02 10:40:58 +0200 |
| commit | 998541db0ed257ab0682e4a392d8ced5f2d5ff6b (patch) | |
| tree | bd038568e28dddc9ac5167b4e909663c9dd7c0c4 | |
| parent | cramfs: Verify inode mode when loading from disk (diff) | |
| parent | openat2: don't trigger automounts with RESOLVE_NO_XDEV (diff) | |
| download | linux-998541db0ed257ab0682e4a392d8ced5f2d5ff6b.tar.gz linux-998541db0ed257ab0682e4a392d8ced5f2d5ff6b.zip | |
Merge patch series "vfs: if RESOLVE_NO_XDEV passed to openat2, don't *trigger* automounts"
Askar Safin <safinaskar@zohomail.com> says:
openat2 had a bug: if we pass RESOLVE_NO_XDEV, then openat2
doesn't traverse through automounts, but may still trigger them.
See this link for full bug report with reproducer:
https://lore.kernel.org/linux-fsdevel/20250817075252.4137628-1-safinaskar@zohomail.com/
This patchset fixes the bug.
RESOLVE_NO_XDEV logic hopefully becomes more clear:
now we immediately fail when we cross mountpoints.
* patches from https://lore.kernel.org/20250825181233.2464822-1-safinaskar@zohomail.com:
openat2: don't trigger automounts with RESOLVE_NO_XDEV
namei: move cross-device check to __traverse_mounts
namei: remove LOOKUP_NO_XDEV check from handle_mounts
namei: move cross-device check to traverse_mounts
Link: https://lore.kernel.org/20250825181233.2464822-1-safinaskar@zohomail.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
| -rw-r--r-- | fs/namei.c | 20 |
1 files changed, 14 insertions, 6 deletions
diff --git a/fs/namei.c b/fs/namei.c index 138a693c2346..44856b70ea3b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1449,6 +1449,10 @@ static int follow_automount(struct path *path, int *count, unsigned lookup_flags dentry->d_inode) return -EISDIR; + /* No need to trigger automounts if mountpoint crossing is disabled. */ + if (lookup_flags & LOOKUP_NO_XDEV) + return -EXDEV; + if (count && (*count)++ >= MAXSYMLINKS) return -ELOOP; @@ -1472,6 +1476,10 @@ static int __traverse_mounts(struct path *path, unsigned flags, bool *jumped, /* Allow the filesystem to manage the transit without i_rwsem * being held. */ if (flags & DCACHE_MANAGE_TRANSIT) { + if (lookup_flags & LOOKUP_NO_XDEV) { + ret = -EXDEV; + break; + } ret = path->dentry->d_op->d_manage(path, false); flags = smp_load_acquire(&path->dentry->d_flags); if (ret < 0) @@ -1489,6 +1497,10 @@ static int __traverse_mounts(struct path *path, unsigned flags, bool *jumped, // here we know it's positive flags = path->dentry->d_flags; need_mntput = true; + if (unlikely(lookup_flags & LOOKUP_NO_XDEV)) { + ret = -EXDEV; + break; + } continue; } } @@ -1630,12 +1642,8 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry, return -ECHILD; } ret = traverse_mounts(path, &jumped, &nd->total_link_count, nd->flags); - if (jumped) { - if (unlikely(nd->flags & LOOKUP_NO_XDEV)) - ret = -EXDEV; - else - nd->state |= ND_JUMPED; - } + if (jumped) + nd->state |= ND_JUMPED; if (unlikely(ret)) { dput(path->dentry); if (path->mnt != nd->path.mnt) |
