[PATCH v3 01/12] ns: Free anonymous mount namespaces via ns_common_free()
From: Mickaël Salaün <mic@digikod.net>
Date: 2026-07-26 16:14:23
Also in:
linux-fsdevel, lkml
Subsystem:
filesystems (vfs and infrastructure), the rest · Maintainers:
Alexander Viro, Christian Brauner, Linus Torvalds
From: Christian Brauner <brauner@kernel.org> free_mnt_ns() skipped ns_common_free() for anonymous mount namespaces (the "if (!is_anon_ns(ns))" guard) because they carry the reserved inum MNT_NS_ANON_INO, which proc_free_inum() must never release. A following change needs ns_common_free() to run for every namespace, to release per-namespace state attached during __ns_common_init(). Move the reserved-inum decision into __ns_common_free() and let free_mnt_ns() call ns_common_free() unconditionally. __ns_common_free() is shared by all namespace types, so it gates proc_free_inum() on ns->inum > MNT_NS_INO_SPECIAL_MAX (a new alias for MNT_NS_ANON_INO) rather than the mount-specific is_anon_ns(). The reserved inums (MNT_NS_ANON_INO and the *_NS_INIT_INO values just above it) belong to namespaces that are never freed, except the anonymous mount namespace; dynamically allocated inums are >= PROC_DYNAMIC_FIRST. So the comparison frees every dynamic inum and skips exactly the anonymous mount namespace, matching the previous guard. Cc: Günther Noack <gnoack@google.com> Cc: Paul Moore <paul@paul-moore.com> Signed-off-by: Christian Brauner <brauner@kernel.org> Co-developed-by: Mickaël Salaün <mic@digikod.net> Signed-off-by: Mickaël Salaün <mic@digikod.net> --- Changes since v2: - New patch, split from "security: add LSM blob and hooks for namespaces" (suggested by Paul Moore). --- fs/namespace.c | 3 +-- include/uapi/linux/nsfs.h | 1 + kernel/nscommon.c | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index 3d5cd5bf3b05..0602f133f3a0 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c@@ -4186,8 +4186,7 @@ static void dec_mnt_namespaces(struct ucounts *ucounts) static void free_mnt_ns(struct mnt_namespace *ns) { - if (!is_anon_ns(ns)) - ns_common_free(ns); + ns_common_free(ns); dec_mnt_namespaces(ns->ucounts); mnt_ns_tree_remove(ns); }
diff --git a/include/uapi/linux/nsfs.h b/include/uapi/linux/nsfs.h
index a25e38d1c874..ea0f0267d90f 100644
--- a/include/uapi/linux/nsfs.h
+++ b/include/uapi/linux/nsfs.h@@ -55,6 +55,7 @@ enum init_ns_ino { MNT_NS_INIT_INO = 0xEFFFFFF8U, #ifdef __KERNEL__ MNT_NS_ANON_INO = 0xEFFFFFF7U, + MNT_NS_INO_SPECIAL_MAX = MNT_NS_ANON_INO, #endif };
diff --git a/kernel/nscommon.c b/kernel/nscommon.c
index 3166c1fd844a..e6f623e1bc37 100644
--- a/kernel/nscommon.c
+++ b/kernel/nscommon.c@@ -91,7 +91,8 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope void __ns_common_free(struct ns_common *ns) { - proc_free_inum(ns->inum); + if (ns->inum > MNT_NS_INO_SPECIAL_MAX) + proc_free_inum(ns->inum); } struct ns_common *__must_check ns_owner(struct ns_common *ns)
--
2.54.0