From: Christian Brauner <brauner@kernel.org>
To: "Mickaël Salaün" <mic@digikod.net>
Cc: "Günther Noack" <gnoack@google.com>,
"Paul Moore" <paul@paul-moore.com>,
"Serge E . Hallyn" <serge@hallyn.com>,
"Justin Suess" <utilityemal77@gmail.com>,
"Lennart Poettering" <lennart@poettering.net>,
"Mikhail Ivanov" <ivanov.mikhail1@huawei-partners.com>,
"Nicolas Bouchinet" <nicolas.bouchinet@oss.cyber.gouv.fr>,
"Shervin Oloumi" <enlightened@google.com>,
"Tingmao Wang" <m@maowtm.org>,
kernel-team@cloudflare.com, linux-fsdevel@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-security-module@vger.kernel.org,
"Daniel Durning" <danieldurning.work@gmail.com>
Subject: Re: [RFC PATCH v1 01/11] security: add LSM blob and hooks for namespaces
Date: Fri, 10 Apr 2026 11:35:35 +0200 [thread overview]
Message-ID: <20260410-sterben-intolerant-bf8b880885f9@brauner> (raw)
In-Reply-To: <20260409.Mei6Yei0beeZ@digikod.net>
On Thu, Apr 09, 2026 at 06:40:03PM +0200, Mickaël Salaün wrote:
> On Wed, Mar 25, 2026 at 01:31:30PM +0100, Christian Brauner wrote:
> > On Thu, Mar 12, 2026 at 11:04:34AM +0100, Mickaël Salaün wrote:
> > > From: Christian Brauner <brauner@kernel.org>
> > >
> > > All namespace types now share the same ns_common infrastructure. Extend
> > > this to include a security blob so LSMs can start managing namespaces
> > > uniformly without having to add one-off hooks or security fields to
> > > every individual namespace type.
> > >
> > > Add a ns_security pointer to ns_common and the corresponding lbs_ns
> > > blob size to lsm_blob_sizes. Allocation and freeing hooks are called
> > > from the common __ns_common_init() and __ns_common_free() paths so
> > > every namespace type gets covered in one go. All information about the
> > > namespace type and the appropriate casting helpers to get at the
> > > containing namespace are available via ns_common making it
> > > straightforward for LSMs to differentiate when they need to.
> > >
> > > A namespace_install hook is called from validate_ns() during setns(2)
> > > giving LSMs a chance to enforce policy on namespace transitions.
> > >
> > > Individual namespace types can still have their own specialized security
> > > hooks when needed. This is just the common baseline that makes it easy
> > > to track and manage namespaces from the security side without requiring
> > > every namespace type to reinvent the wheel.
> > >
> > > Cc: Günther Noack <gnoack@google.com>
> > > Cc: Paul Moore <paul@paul-moore.com>
> > > Cc: Serge E. Hallyn <serge@hallyn.com>
> > > Signed-off-by: Christian Brauner <brauner@kernel.org>
> > > Link: https://lore.kernel.org/r/20260216-work-security-namespace-v1-1-075c28758e1f@kernel.org
> > > ---
> > > include/linux/lsm_hook_defs.h | 3 ++
> > > include/linux/lsm_hooks.h | 1 +
> > > include/linux/ns/ns_common_types.h | 3 ++
> > > include/linux/security.h | 20 ++++++++
> > > kernel/nscommon.c | 12 +++++
> > > kernel/nsproxy.c | 8 +++-
> > > security/lsm_init.c | 2 +
> > > security/security.c | 76 ++++++++++++++++++++++++++++++
> > > 8 files changed, 124 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> > > index 8c42b4bde09c..fefd3aa6d8f4 100644
> > > --- a/include/linux/lsm_hook_defs.h
> > > +++ b/include/linux/lsm_hook_defs.h
> > > @@ -260,6 +260,9 @@ LSM_HOOK(int, -ENOSYS, task_prctl, int option, unsigned long arg2,
> > > LSM_HOOK(void, LSM_RET_VOID, task_to_inode, struct task_struct *p,
> > > struct inode *inode)
> > > LSM_HOOK(int, 0, userns_create, const struct cred *cred)
> > > +LSM_HOOK(int, 0, namespace_alloc, struct ns_common *ns)
> > > +LSM_HOOK(void, LSM_RET_VOID, namespace_free, struct ns_common *ns)
> > > +LSM_HOOK(int, 0, namespace_install, const struct nsset *nsset, struct ns_common *ns)
> > > LSM_HOOK(int, 0, ipc_permission, struct kern_ipc_perm *ipcp, short flag)
> > > LSM_HOOK(void, LSM_RET_VOID, ipc_getlsmprop, struct kern_ipc_perm *ipcp,
> > > struct lsm_prop *prop)
> > > diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> > > index d48bf0ad26f4..3e7afe76e86c 100644
> > > --- a/include/linux/lsm_hooks.h
> > > +++ b/include/linux/lsm_hooks.h
> > > @@ -111,6 +111,7 @@ struct lsm_blob_sizes {
> > > unsigned int lbs_ipc;
> > > unsigned int lbs_key;
> > > unsigned int lbs_msg_msg;
> > > + unsigned int lbs_ns;
> > > unsigned int lbs_perf_event;
> > > unsigned int lbs_task;
> > > unsigned int lbs_xattr_count; /* num xattr slots in new_xattrs array */
> > > diff --git a/include/linux/ns/ns_common_types.h b/include/linux/ns/ns_common_types.h
> > > index 0014fbc1c626..170288e2e895 100644
> > > --- a/include/linux/ns/ns_common_types.h
> > > +++ b/include/linux/ns/ns_common_types.h
> > > @@ -115,6 +115,9 @@ struct ns_common {
> > > struct dentry *stashed;
> > > const struct proc_ns_operations *ops;
> > > unsigned int inum;
> > > +#ifdef CONFIG_SECURITY
> > > + void *ns_security;
> > > +#endif
> > > union {
> > > struct ns_tree;
> > > struct rcu_head ns_rcu;
> > > diff --git a/include/linux/security.h b/include/linux/security.h
> > > index 83a646d72f6f..611b9098367d 100644
> > > --- a/include/linux/security.h
> > > +++ b/include/linux/security.h
> > > @@ -67,6 +67,7 @@ enum fs_value_type;
> > > struct watch;
> > > struct watch_notification;
> > > struct lsm_ctx;
> > > +struct nsset;
> > >
> > > /* Default (no) options for the capable function */
> > > #define CAP_OPT_NONE 0x0
> > > @@ -80,6 +81,7 @@ struct lsm_ctx;
> > >
> > > struct ctl_table;
> > > struct audit_krule;
> > > +struct ns_common;
> > > struct user_namespace;
> > > struct timezone;
> > >
> > > @@ -533,6 +535,9 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
> > > unsigned long arg4, unsigned long arg5);
> > > void security_task_to_inode(struct task_struct *p, struct inode *inode);
> > > int security_create_user_ns(const struct cred *cred);
> > > +int security_namespace_alloc(struct ns_common *ns);
> > > +void security_namespace_free(struct ns_common *ns);
> > > +int security_namespace_install(const struct nsset *nsset, struct ns_common *ns);
> > > int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag);
> > > void security_ipc_getlsmprop(struct kern_ipc_perm *ipcp, struct lsm_prop *prop);
> > > int security_msg_msg_alloc(struct msg_msg *msg);
> > > @@ -1407,6 +1412,21 @@ static inline int security_create_user_ns(const struct cred *cred)
> > > return 0;
> > > }
> > >
> > > +static inline int security_namespace_alloc(struct ns_common *ns)
> > > +{
> > > + return 0;
> > > +}
> > > +
> > > +static inline void security_namespace_free(struct ns_common *ns)
> > > +{
> > > +}
> > > +
> > > +static inline int security_namespace_install(const struct nsset *nsset,
> > > + struct ns_common *ns)
> > > +{
> > > + return 0;
> > > +}
> > > +
> > > static inline int security_ipc_permission(struct kern_ipc_perm *ipcp,
> > > short flag)
> > > {
> > > diff --git a/kernel/nscommon.c b/kernel/nscommon.c
> > > index bdc3c86231d3..de774e374f9d 100644
> > > --- a/kernel/nscommon.c
> > > +++ b/kernel/nscommon.c
> > > @@ -4,6 +4,7 @@
> > > #include <linux/ns_common.h>
> > > #include <linux/nstree.h>
> > > #include <linux/proc_ns.h>
> > > +#include <linux/security.h>
> > > #include <linux/user_namespace.h>
> > > #include <linux/vfsdebug.h>
> > >
> > > @@ -59,6 +60,9 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
> > >
> > > refcount_set(&ns->__ns_ref, 1);
> > > ns->stashed = NULL;
> > > +#ifdef CONFIG_SECURITY
> > > + ns->ns_security = NULL;
> > > +#endif
> > > ns->ops = ops;
> > > ns->ns_id = 0;
> > > ns->ns_type = ns_type;
> > > @@ -77,6 +81,13 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
> > > ret = proc_alloc_inum(&ns->inum);
> > > if (ret)
> > > return ret;
> > > +
> > > + ret = security_namespace_alloc(ns);
> > > + if (ret) {
> > > + proc_free_inum(ns->inum);
> >
> > ret = security_namespace_alloc(ns);
> > if (ret && !inum)
> > proc_free_inum(ns->inum);
> > return ret;
> >
> >
> > > + return ret;
> > > + }
> > > +
> > > /*
> > > * Tree ref starts at 0. It's incremented when namespace enters
> > > * active use (installed in nsproxy) and decremented when all
> > > @@ -91,6 +102,7 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
> > >
> > > void __ns_common_free(struct ns_common *ns)
> > > {
> > > + security_namespace_free(ns);
> > > proc_free_inum(ns->inum);
> > > }
> > >
> > > diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
> > > index 259c4b4f1eeb..f0b30d1907e7 100644
> > > --- a/kernel/nsproxy.c
> > > +++ b/kernel/nsproxy.c
> > > @@ -379,7 +379,13 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset)
> > >
> > > static inline int validate_ns(struct nsset *nsset, struct ns_common *ns)
> > > {
> > > - return ns->ops->install(nsset, ns);
> > > + int ret;
> > > +
> > > + ret = ns->ops->install(nsset, ns);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + return security_namespace_install(nsset, ns);
> >
> > In my local tree I had that moved before the ->install() and I think
> > that's the correct thing to do. So please switch to that.
>
> Looks good, I'll include your fixes in the next version.
Thanks!
>
> >
> > The rest looks good to me, thanks.
>
> Another issue raised by Daniel Durning [1] is freeing of anonymous
> namespaces.
>
> I'll extend this patch with this new hunk if that's ok:
>
> diff --git a/fs/namespace.c b/fs/namespace.c
> index 854f4fc66469..f6977e59be7d 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -4186,6 +4186,8 @@ static void free_mnt_ns(struct mnt_namespace *ns)
> {
> if (!is_anon_ns(ns))
> ns_common_free(ns);
> + else
> + security_namespace_free(&ns->ns);
> dec_mnt_namespaces(ns->ucounts);
> mnt_ns_tree_remove(ns);
> }
I think that's fixing it at the wrong layer. It's probably better to do
sm like:
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..e7a3dd2189cc 100644
--- a/kernel/nscommon.c
+++ b/kernel/nscommon.c
@@ -91,7 +91,10 @@ 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);
+ security_namespace_free(&ns->ns);
+
+ if (ns->inum > MNT_NS_INO_SPECIAL_MAX)
+ proc_free_inum(ns->inum);
}
struct ns_common *__must_check ns_owner(struct ns_common *ns)
>
> Daniel, could you please confirm that this fixes the memory leak?
>
> [1] https://lore.kernel.org/all/20260330193100.3603-1-danieldurning.work@gmail.com/
>
>
> > > +/**
> > > + * security_namespace_free() - Release LSM security data from a namespace
> > > + * @ns: the namespace being freed
> > > + *
> > > + * Release security data attached to the namespace. Called before the
> > > + * namespace structure is freed.
> > > + *
> > > + * Note: The namespace may be freed via kfree_rcu(). LSMs must use
> > > + * RCU-safe freeing for any data that might be accessed by concurrent
> > > + * RCU readers.
> > > + */
> > > +void security_namespace_free(struct ns_common *ns)
> > > +{
> > > + if (!ns->ns_security)
> > > + return;
> > > +
> > > + call_void_hook(namespace_free, ns);
> > > +
>
> > > + kfree(ns->ns_security);
> > > + ns->ns_security = NULL;
>
> I think it would be safer to replace these two lines with:
> kfree_rcu_mightsleep(ns->ns_security)
>
> > > +}
next prev parent reply other threads:[~2026-04-10 9:35 UTC|newest]
Thread overview: 51+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-12 10:04 [RFC PATCH v1 00/11] Landlock: Namespace and capability control Mickaël Salaün
2026-03-12 10:04 ` [RFC PATCH v1 01/11] security: add LSM blob and hooks for namespaces Mickaël Salaün
2026-03-25 12:31 ` Christian Brauner
2026-04-09 16:40 ` Mickaël Salaün
2026-04-10 9:35 ` Christian Brauner [this message]
2026-04-22 21:21 ` Günther Noack
2026-04-23 0:19 ` Paul Moore
2026-04-24 18:56 ` Mickaël Salaün
2026-04-24 19:28 ` Paul Moore
2026-04-27 14:57 ` Christian Brauner
2026-04-27 21:46 ` Paul Moore
2026-03-12 10:04 ` [RFC PATCH v1 02/11] security: Add LSM_AUDIT_DATA_NS for namespace audit records Mickaël Salaün
2026-03-25 12:32 ` Christian Brauner
2026-04-01 16:38 ` Mickaël Salaün
2026-04-01 18:48 ` Mickaël Salaün
2026-04-09 13:29 ` Christian Brauner
2026-04-22 21:21 ` Günther Noack
2026-03-12 10:04 ` [RFC PATCH v1 03/11] nsproxy: Add FOR_EACH_NS_TYPE() X-macro and CLONE_NS_ALL Mickaël Salaün
2026-03-25 12:33 ` Christian Brauner
2026-03-25 15:26 ` Mickaël Salaün
2026-03-26 14:22 ` (subset) " Christian Brauner
2026-03-12 10:04 ` [RFC PATCH v1 04/11] landlock: Wrap per-layer access masks in struct layer_rights Mickaël Salaün
2026-04-10 1:45 ` Tingmao Wang
2026-04-22 21:29 ` Günther Noack
2026-03-12 10:04 ` [RFC PATCH v1 05/11] landlock: Enforce namespace entry restrictions Mickaël Salaün
2026-03-29 13:15 ` kernel test robot
2026-04-10 1:45 ` Tingmao Wang
2026-05-08 15:46 ` Günther Noack
2026-03-12 10:04 ` [RFC PATCH v1 06/11] landlock: Enforce capability restrictions Mickaël Salaün
2026-04-22 21:36 ` Günther Noack
2026-05-08 15:54 ` Günther Noack
2026-03-12 10:04 ` [RFC PATCH v1 07/11] selftests/landlock: Drain stale audit records on init Mickaël Salaün
2026-03-24 13:27 ` Günther Noack
2026-03-12 10:04 ` [RFC PATCH v1 08/11] selftests/landlock: Add namespace restriction tests Mickaël Salaün
2026-03-12 10:04 ` [RFC PATCH v1 09/11] selftests/landlock: Add capability " Mickaël Salaün
2026-03-12 10:04 ` [RFC PATCH v1 10/11] samples/landlock: Add capability and namespace restriction support Mickaël Salaün
2026-04-22 21:20 ` Günther Noack
2026-04-23 13:51 ` Mickaël Salaün
2026-03-12 10:04 ` [RFC PATCH v1 11/11] landlock: Add documentation for capability and namespace restrictions Mickaël Salaün
2026-03-12 14:48 ` Justin Suess
2026-04-23 13:51 ` Mickaël Salaün
2026-04-23 16:01 ` Justin Suess
2026-04-23 16:08 ` Justin Suess
2026-04-22 20:38 ` Günther Noack
2026-04-23 13:52 ` Mickaël Salaün
2026-05-08 15:13 ` Günther Noack
2026-03-25 12:34 ` [RFC PATCH v1 00/11] Landlock: Namespace and capability control Christian Brauner
2026-04-20 15:06 ` Günther Noack
2026-04-21 8:24 ` Mickaël Salaün
2026-04-22 21:16 ` Günther Noack
2026-04-23 13:50 ` Mickaël Salaün
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260410-sterben-intolerant-bf8b880885f9@brauner \
--to=brauner@kernel.org \
--cc=danieldurning.work@gmail.com \
--cc=enlightened@google.com \
--cc=gnoack@google.com \
--cc=ivanov.mikhail1@huawei-partners.com \
--cc=kernel-team@cloudflare.com \
--cc=lennart@poettering.net \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=m@maowtm.org \
--cc=mic@digikod.net \
--cc=nicolas.bouchinet@oss.cyber.gouv.fr \
--cc=paul@paul-moore.com \
--cc=serge@hallyn.com \
--cc=utilityemal77@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.