[PATCH v3 01/12] ns: Free anonymous mount namespaces via ns_common_free()
Mickaël Salaün <[email protected]> Sun, 26 Jul 2026 18:13:46 +0200
| Newsgroups | org.kernel.vger.linux-security-module,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Christian Brauner <[email protected]> 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 <[email protected]> Cc: Paul Moore <[email protected]> Signed-off-by: Christian Brauner <[email protected]> Co-developed-by: Mickaël Salaün <[email protected]> Signed-off-by: Mickaël Salaün <[email protected]> --- 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