SELinux Security Module development
 help / color / mirror / Atom feed
* [PATCH] lsm: initialize the security blob for the initial namespaces
@ 2026-09-16 19:48 Stephen Smalley
  2026-09-16 19:58 ` sashiko-bot
  2026-09-18  7:27 ` Christian Brauner
  0 siblings, 2 replies; 5+ messages in thread
From: Stephen Smalley @ 2026-09-16 19:48 UTC (permalink / raw)
  To: selinux
  Cc: paul, omosnacek, linux-security-module, brauner, mic, serge, jack,
	jmorris, Stephen Smalley

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@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@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");
+
 	return 0;
 }
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] lsm: initialize the security blob for the initial namespaces
  2026-09-16 19:48 [PATCH] lsm: initialize the security blob for the initial namespaces Stephen Smalley
@ 2026-09-16 19:58 ` sashiko-bot
  2026-09-18  7:27 ` Christian Brauner
  1 sibling, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-09-16 19:58 UTC (permalink / raw)
  Cc: selinux

> 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@gmail.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260916194824.37332-1-stephen.smalley.work@gmail.com?part=1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] lsm: initialize the security blob for the initial namespaces
  2026-09-16 19:48 [PATCH] lsm: initialize the security blob for the initial namespaces Stephen Smalley
  2026-09-16 19:58 ` sashiko-bot
@ 2026-09-18  7:27 ` Christian Brauner
  2026-09-18 12:55   ` Stephen Smalley
  1 sibling, 1 reply; 5+ messages in thread
From: Christian Brauner @ 2026-09-18  7:27 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: selinux, paul, omosnacek, linux-security-module, mic, serge, jack,
	jmorris

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@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@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.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] lsm: initialize the security blob for the initial namespaces
  2026-09-18  7:27 ` Christian Brauner
@ 2026-09-18 12:55   ` Stephen Smalley
  2026-09-18 13:55     ` Stephen Smalley
  0 siblings, 1 reply; 5+ messages in thread
From: Stephen Smalley @ 2026-09-18 12:55 UTC (permalink / raw)
  To: Christian Brauner
  Cc: selinux, paul, omosnacek, linux-security-module, mic, serge, jack,
	jmorris

On Fri, Sep 18, 2026 at 3:28 AM Christian Brauner <brauner@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@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@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.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] lsm: initialize the security blob for the initial namespaces
  2026-09-18 12:55   ` Stephen Smalley
@ 2026-09-18 13:55     ` Stephen Smalley
  0 siblings, 0 replies; 5+ messages in thread
From: Stephen Smalley @ 2026-09-18 13:55 UTC (permalink / raw)
  To: Christian Brauner
  Cc: selinux, paul, omosnacek, linux-security-module, mic, serge, jack,
	jmorris

On Fri, Sep 18, 2026 at 8:55 AM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Fri, Sep 18, 2026 at 3:28 AM Christian Brauner <brauner@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@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@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.

The v2 patch can be found at
https://lore.kernel.org/linux-security-module/20260918134422.17053-2-stephen.smalley.work@gmail.com/T/#u

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-18 13:55 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-16 19:48 [PATCH] lsm: initialize the security blob for the initial namespaces Stephen Smalley
2026-09-16 19:58 ` sashiko-bot
2026-09-18  7:27 ` Christian Brauner
2026-09-18 12:55   ` Stephen Smalley
2026-09-18 13:55     ` Stephen Smalley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox