summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-09-26 15:44:31 +0200
committerChristian Brauner <brauner@kernel.org>2026-09-26 15:47:41 +0200
commit7253a6ff640df85fdccabe7e3dd38cce38e0a7b4 (patch)
tree60fa065adbaa1f137979546110d48d9fbd305e53
parent57088a37b2e4d194a2ae5027134b780a36acd900 (diff)
downloadlinux-next-7253a6ff640df85fdccabe7e3dd38cce38e0a7b4.tar.gz
linux-next-7253a6ff640df85fdccabe7e3dd38cce38e0a7b4.zip
namespace: drop the file's reference first in dissolve_on_fput()
A detached mount tree is held alive by a file. dissolve_on_fput() unmounts the detached tree attached to that file. Currently dissolve_on_fput() leaves the file's reference to the root of the tree to the caller. So either __fput(), open_detached_copy(), or fsmount() drop the mount. That's backwards. The reference keeps the tree around and is dropped when the tree is already gone. Instead, let dissolve_on_fput() the mount and drop it after umount_tree() with namespace_sem still held. After umount_tree() the mntput() is never the last reference and so the root gets unmounted before its children. Link: https://patch.msgid.link/20260926-work-mount-fixes-2-v1-8-f357abf3d17b@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
-rw-r--r--fs/file_table.c3
-rw-r--r--fs/namespace.c9
2 files changed, 8 insertions, 4 deletions
diff --git a/fs/file_table.c b/fs/file_table.c
index c68b8c0a4097..e2665eaa6f8c 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -520,7 +520,8 @@ static void __fput(struct file *file)
dput(dentry);
if (unlikely(mode & FMODE_NEED_UNMOUNT))
dissolve_on_fput(mnt);
- mntput(mnt);
+ else
+ mntput(mnt);
out:
file_free(file);
}
diff --git a/fs/namespace.c b/fs/namespace.c
index de28fe9058ab..5b44eee28f39 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2312,9 +2312,11 @@ void drop_collected_paths(const struct path *paths, const struct path *prealloc)
static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *, bool);
+/* Consumes the caller's reference to @mnt. */
void dissolve_on_fput(struct vfsmount *mnt)
{
- struct mount *m = real_mount(mnt);
+ struct vfsmount *p __free(mntput) = mnt;
+ struct mount *m = real_mount(p);
/*
* m used to be the root of anon namespace; if it still is one,
@@ -2339,6 +2341,7 @@ void dissolve_on_fput(struct vfsmount *mnt)
lock_mount_hash();
umount_tree(m, UMOUNT_CONNECTED);
unlock_mount_hash();
+ mntput(no_free_ptr(p));
}
}
@@ -3109,7 +3112,7 @@ static struct file *open_detached_copy(struct path *path, unsigned int flags)
path->mnt = mntget(&ns->root->mnt);
file = dentry_open(path, O_PATH, current_cred());
if (IS_ERR(file))
- dissolve_on_fput(path->mnt);
+ dissolve_on_fput(no_free_ptr(path->mnt));
else
file->f_mode |= FMODE_NEED_UNMOUNT;
return file;
@@ -4566,7 +4569,7 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
FD_PREPARE(fdf, (flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0,
dentry_open(&new_path, O_PATH, fc->cred));
if (fdf.err) {
- dissolve_on_fput(new_path.mnt);
+ dissolve_on_fput(no_free_ptr(new_path.mnt));
return fdf.err;
}