openat2 had a bug: if we pass RESOLVE_NO_XDEV, then openat2 doesn't traverse through automounts, but may still trigger them. (See the link for full bug report with reproducer.) This commit fixes this bug. Link: https://lore.kernel.org/linux-fsdevel/20250817075252.4137628-1-safinaskar@xxxxxxxxxxxx/ Fixes: fddb5d430ad9fa91b49b1 ("open: introduce openat2(2) syscall") Signed-off-by: Askar Safin <safinaskar@xxxxxxxxxxxx> --- fs/namei.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 6f43f96f506d..55e2447699a4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1449,6 +1449,18 @@ static int follow_automount(struct path *path, int *count, unsigned lookup_flags dentry->d_inode) return -EISDIR; + /* "if" above returned -EISDIR if we want to get automount point itself + * as opposed to new mount. Getting automount point itself is, of course, + * totally okay even if we have LOOKUP_NO_XDEV. + * + * But if we got here, then we want to get + * new mount. Let's deny this if LOOKUP_NO_XDEV is specified. + * If we have LOOKUP_NO_XDEV, then we want to deny not only + * traversing through automounts, but also triggering them + */ + if (lookup_flags & LOOKUP_NO_XDEV) + return -EXDEV; + if (count && (*count)++ >= MAXSYMLINKS) return -ELOOP; @@ -1472,6 +1484,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) -- 2.47.2