[PATCH] lsm: initialize the security blob for the initial namespaces
Stephen Smalley
stephen.smalley.work at gmail.com
Fri Sep 18 12:55:00 UTC 2026
On Fri, Sep 18, 2026 at 3:28 AM Christian Brauner <brauner at kernel.org> wrote:
>
> On Wed, Sep 16, 2026 at 03:48:24PM -0400, Stephen Smalley wrote:
> > Commit f675d2e95569 ("lsm: add LSM blob and hooks for namespaces")
> > added a security blob to struct ns_common and allocates it from
> > __ns_common_init(). Most initial namespaces (init_user_ns,
> > init_uts_ns, init_pid_ns, init_mnt_ns, and, depending on
> > configuration, init_ipc_ns, init_cgroup_ns and init_time_ns) are
> > however set up statically via NS_COMMON_INIT() and never pass through
> > __ns_common_init(), so their ns_security pointer is left NULL for the
> > lifetime of the system. Any LSM that registers an lbs_ns blob and
> > dereferences its slice on one of those namespaces (for example from
> > the namespace_install hook when a task setns()'s back into an initial
> > namespace) will fault.
> >
> > Allocate the blob and run the namespace_init hook for each of these
> > namespaces at the end of security_init(), after all ordered LSMs have
> > registered their hooks, so every LSM sees the initial namespaces the
> > same way it sees init_net (which is initialized at runtime via
> > ns_common_init(&init_net) from net_ns_init() immediately after
> > security_init()) and does not need to special-case them in its own
> > ->init() callback.
> >
> > Provide an init_ns_common_for_each() iterator in kernel/nscommon.c so
> > the list of statically-defined initial namespaces and their Kconfig
> > guards live next to the rest of the ns_common infrastructure. init_net
> > is intentionally excluded: pre-initializing it here would leak once
> > __ns_common_init() clears ns_security and re-allocates.
> >
> > Fixes: f675d2e95569 ("lsm: add LSM blob and hooks for namespaces")
> > Signed-off-by: Stephen Smalley <stephen.smalley.work at gmail.com>
> > ---
> > include/linux/ns_common.h | 1 +
> > kernel/nscommon.c | 45 +++++++++++++++++++++++++++++++++++++++
> > security/lsm_init.c | 11 ++++++++++
> > 3 files changed, 57 insertions(+)
> >
> > diff --git a/include/linux/ns_common.h b/include/linux/ns_common.h
> > index c8e227a3f9e2..742627000453 100644
> > --- a/include/linux/ns_common.h
> > +++ b/include/linux/ns_common.h
> > @@ -11,6 +11,7 @@
> > bool is_current_namespace(struct ns_common *ns);
> > int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_operations *ops, int inum);
> > void __ns_common_free(struct ns_common *ns);
> > +int init_ns_common_for_each(int (*fn)(struct ns_common *ns));
> > struct ns_common *__must_check ns_owner(struct ns_common *ns);
> >
> > static __always_inline bool is_ns_init_inum(const struct ns_common *ns)
> > diff --git a/kernel/nscommon.c b/kernel/nscommon.c
> > index e72426bba29a..4f3cba5518d7 100644
> > --- a/kernel/nscommon.c
> > +++ b/kernel/nscommon.c
> > @@ -1,11 +1,17 @@
> > // SPDX-License-Identifier: GPL-2.0-only
> > /* Copyright (c) 2025 Christian Brauner <brauner at kernel.org> */
> >
> > +#include <linux/cgroup.h>
> > +#include <linux/ipc_namespace.h>
> > +#include <linux/mnt_namespace.h>
> > #include <linux/ns_common.h>
> > #include <linux/nstree.h>
> > +#include <linux/pid_namespace.h>
> > #include <linux/proc_ns.h>
> > #include <linux/security.h>
> > +#include <linux/time_namespace.h>
> > #include <linux/user_namespace.h>
> > +#include <linux/uts_namespace.h>
> > #include <linux/vfsdebug.h>
> >
> > #ifdef CONFIG_DEBUG_VFS
> > @@ -101,6 +107,45 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
> > return 0;
> > }
> >
> > +/**
> > + * init_ns_common_for_each - iterate the statically-defined initial namespaces
> > + * @fn: callback invoked with each initial ns_common
> > + *
> > + * Walk the initial namespaces that are set up via NS_COMMON_INIT() at compile
> > + * time and therefore never pass through __ns_common_init(). The initial
> > + * network namespace is intentionally excluded: it is initialized at runtime
> > + * via ns_common_init(&init_net) from net_ns_init().
> > + *
> > + * Return: the first non-zero return from @fn, or 0.
> > + */
> > +int __init init_ns_common_for_each(int (*fn)(struct ns_common *ns))
> > +{
> > + struct ns_common *set[] = {
> > + to_ns_common(&init_user_ns),
> > + to_ns_common(&init_uts_ns),
> > + to_ns_common(&init_pid_ns),
> > + from_mnt_ns(&init_mnt_ns),
> > +#if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)
> > + to_ns_common(&init_ipc_ns),
> > +#endif
> > +#ifdef CONFIG_CGROUPS
> > + to_ns_common(&init_cgroup_ns),
> > +#endif
> > +#ifdef CONFIG_TIME_NS
> > + to_ns_common(&init_time_ns),
> > +#endif
> > + };
> > + unsigned int i;
> > + int ret;
> > +
> > + for (i = 0; i < ARRAY_SIZE(set); i++) {
> > + ret = fn(set[i]);
> > + if (ret)
> > + return ret;
> > + }
> > + return 0;
> > +}
> > +
> > void __ns_common_free(struct ns_common *ns)
> > {
> > security_namespace_free(ns);
> > diff --git a/security/lsm_init.c b/security/lsm_init.c
> > index dbda7771013b..60e15215f6bd 100644
> > --- a/security/lsm_init.c
> > +++ b/security/lsm_init.c
> > @@ -7,6 +7,7 @@
> >
> > #include <linux/init.h>
> > #include <linux/lsm_hooks.h>
> > +#include <linux/ns_common.h>
> >
> > #include "lsm.h"
> >
> > @@ -490,6 +491,16 @@ int __init security_init(void)
> > lsm_init_single(*lsm);
> > }
> >
> > + /*
> > + * The initial namespaces are set up statically via NS_COMMON_INIT()
> > + * and never pass through __ns_common_init(), so allocate their
> > + * security blobs and run the namespace_init hooks now that all LSMs
> > + * are registered. init_net is not covered here; it is initialized
> > + * at runtime from net_ns_init() shortly after security_init().
> > + */
> > + if (init_ns_common_for_each(security_namespace_init))
> > + panic("initial LSM ns alloc failed\n");
>
> Wouldn't it be nicer to turn this around and call
> security_namespace_init from the ns common code and avoid the callback?
> Especially since there's only one.
Good idea. I'll send a v2 that does this.
More information about the Linux-security-module-archive
mailing list