Linux filesystem development
 help / color / mirror / Atom feed
* [PATCH 0/8] ns: fixes for the namespace LSM hooks
@ 2026-10-26  9:57 Christian Brauner
  2026-10-26  9:57 ` [PATCH 1/8] lsm: always call the namespace_free hook Christian Brauner
                   ` (9 more replies)
  0 siblings, 10 replies; 16+ messages in thread
From: Christian Brauner @ 2026-10-26  9:57 UTC (permalink / raw)
  To: linux-fsdevel, bpf
  Cc: linux-security-module, Alexei Starovoitov, Daniel Borkmann,
	Paul Moore, netdev, Christian Brauner (Amutable), stable

I tried to switch systemd-nsresourced over to the namespace_free LSM
hook that went in this cycle. I explicitly developed it to work nicely
with that. But the version that is merged is broken for that case.

The namespace_free hook is never called. security_namespace_free() bails
if ns->ns_security is NULL and that's only allocated if an LSM requests
a namespace blob. Nobody does. Landlock doesn't care as it only uses
namespace_init and namespace_install but a bpf program attached to
namespace_free never runs. And it can't work around that either. We
could make the bpf LSM request a blob but that would just be a way to
make the hook fire. So call it unconditionally like we do for
superblocks, files and bpf maps.

The bpf LSM also marks the arguments of namespace_init and
namespace_free as trusted. Curiously that patch was also dropped.

namespace_init is called before the namespace is set up and if anything
fails afterwards the namespace is freed without looking at its reference
count. namespace_free is called on the way out. There's no kfunc that
takes a namespace yet but once there is one a program could take a
reference to a namespace that's about to be freed. So add both hooks to
untrusted_lsm_hooks.

The namespace_init hook also always sees a namespace id of zero because
the id is only allocated after the hook has run. The id is the only
identifier of a namespace that is never recycled. So it's what a bpf LSM
wants to use to track a namespace from namespace_init until
namespace_free.

The inode number is recycled way too quickly for that. So allocate the
id in __ns_common_init() before the hook is called. Then ns_tree_add()
doesn't need to allocate ids anymore and ns_tree_add_raw() can go.
Landlock keeps logging zero for denied namespace creation because that's
what it documents.

I'd want to get the first three patches into v7.4 since the hooks are
new this cycle. The systemd change I have for this uses the
namespace_free hook whenever the kernel has it. On a kernel with the
hook but without the first fix it would never learn about dead user
namespaces.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Christian Brauner (8):
      lsm: always call the namespace_free hook
      bpf: don't mark namespace_init and namespace_free arguments as trusted
      selftests/bpf: add verifier tests for the namespace LSM hooks
      landlock: pass the namespace id to log to check_ns_type()
      ns: allocate the namespace id before the namespace_init hook
      nstree: drop ns_tree_add_raw()
      lsm: fix the security_namespace_init() kernel-doc
      selftests/bpf: add a lifecycle test for the namespace LSM hooks

 fs/namespace.c                                     |   6 +-
 include/linux/ns_common.h                          |  16 ++-
 include/linux/nstree.h                             |  29 +----
 ipc/namespace.c                                    |   3 +-
 kernel/bpf/bpf_lsm.c                               |   2 +
 kernel/nscommon.c                                  |   7 +-
 kernel/nstree.c                                    |   2 +-
 net/core/net_namespace.c                           |   4 +-
 security/landlock/ns.c                             |  11 +-
 security/security.c                                |   9 +-
 .../selftests/bpf/prog_tests/ns_lifecycle.c        | 145 +++++++++++++++++++++
 tools/testing/selftests/bpf/prog_tests/verifier.c  |   2 +
 .../selftests/bpf/progs/test_ns_lifecycle.c        |  88 +++++++++++++
 .../selftests/bpf/progs/verifier_lsm_namespace.c   |  38 ++++++
 .../testing/selftests/bpf/test_kmods/bpf_testmod.c |   5 +
 .../selftests/bpf/test_kmods/bpf_testmod_kfunc.h   |   1 +
 16 files changed, 314 insertions(+), 54 deletions(-)
---
base-commit: 21a7f44fd0765fcc5194130b28767786eba1d1e7
change-id: 20261026-work-security-namespace_free-7aaf89b5b00e


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

* [PATCH 1/8] lsm: always call the namespace_free hook
  2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
@ 2026-10-26  9:57 ` Christian Brauner
  2026-10-26  9:57 ` [PATCH 2/8] bpf: don't mark namespace_init and namespace_free arguments as trusted Christian Brauner
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Christian Brauner @ 2026-10-26  9:57 UTC (permalink / raw)
  To: linux-fsdevel, bpf
  Cc: linux-security-module, Alexei Starovoitov, Daniel Borkmann,
	Paul Moore, netdev, Christian Brauner (Amutable), stable

security_namespace_free() returns early if ns->ns_security is NULL and
skips the namespace_free hook. But ns->ns_security is only allocated if
an LSM requests a namespace blob via lbs_ns and currently no LSM does.
So the namespace_free hook is never called at all.

Landlock only uses namespace_init and namespace_install so it doesn't
care. But the bpf LSM attaches to every hook and a program attached to
namespace_free never runs. We want to use it in systemd-nsresourced to
get notified when a user namespace goes away. Right now it has to kprobe
retire_userns_sysctls() for that.

Note that a bpf program can't work around this. The size of the
namespace blob is fixed at boot and the bpf LSM doesn't request one.
And security_namespace_init() allocates the blob before the
namespace_init hooks are called anyway.

The check isn't needed. __ns_common_free() is only ever called after
__ns_common_init() succeeded and security_namespace_init() only calls
security_namespace_free() after the blob has been allocated. So if an
LSM requests a namespace blob it is always there when the hook runs.
Call the hook unconditionally like security_sb_free(),
security_file_free() and security_bpf_map_free() do.

The bpf LSM attaches programs to its bpf_lsm_*() stubs so fentry on the
stubs shows what a bpf LSM program sees. Booted with lsm.debug, which
prints "LSM: blob(ns) size 0", and created 100 user and uts namespaces:

  bpftrace -e 'fentry:bpf_lsm_namespace_init { @init = count(); }
               fentry:bpf_lsm_namespace_free { @free = count(); }' &
  for i in $(seq 100); do unshare -Uu true; done

  before: @init: 200 @free: 0
  after:  @init: 200 @free: 200

Fixes: f675d2e95569 ("lsm: add LSM blob and hooks for namespaces")
Cc: <stable@kernel.org> # mainline only
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 security/security.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/security/security.c b/security/security.c
index f6583d507395..1e76c097b28d 100644
--- a/security/security.c
+++ b/security/security.c
@@ -3412,9 +3412,6 @@ int security_namespace_init(struct ns_common *ns)
  */
 void security_namespace_free(struct ns_common *ns)
 {
-	if (!ns->ns_security)
-		return;
-
 	call_void_hook(namespace_free, ns);
 
 	kfree(ns->ns_security);

-- 
2.53.0


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

* [PATCH 2/8] bpf: don't mark namespace_init and namespace_free arguments as trusted
  2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
  2026-10-26  9:57 ` [PATCH 1/8] lsm: always call the namespace_free hook Christian Brauner
@ 2026-10-26  9:57 ` Christian Brauner
  2026-10-26  9:57 ` [PATCH 3/8] selftests/bpf: add verifier tests for the namespace LSM hooks Christian Brauner
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Christian Brauner @ 2026-10-26  9:57 UTC (permalink / raw)
  To: linux-fsdevel, bpf
  Cc: linux-security-module, Alexei Starovoitov, Daniel Borkmann,
	Paul Moore, netdev, Christian Brauner (Amutable), stable

The bpf LSM marks the arguments of all hooks as trusted unless the hook
is listed in untrusted_lsm_hooks. That list was added in commit
c0c852dd1876 ("bpf: Do not mark certain LSM hook arguments as trusted")
for hooks that are called on objects that are only partially set up or
that are already on their way out. The namespace_init and namespace_free
hooks are exactly that but they aren't listed.

namespace_init is called from __ns_common_init() before the namespace
has been set up. It isn't in the namespace trees yet and for all types
but network namespaces even the owning user namespace isn't set yet. If
any LSM rejects the namespace or if anything after ns_common_init()
fails the namespace is freed right away without looking at its reference
count.

namespace_free is called from __ns_common_free() when the namespace is
torn down or when its initialization failed.

Currently there's no kfunc that takes a namespace. But once there is
one, e.g., for namespace local storage, a program could take a reference
to or create storage on a namespace that is about to be freed. So don't
mark the arguments of these hooks as trusted. namespace_install is
called with a reference to a fully set up namespace and stays trusted.

Fixes: f675d2e95569 ("lsm: add LSM blob and hooks for namespaces")
Cc: <stable@kernel.org> # mainline only
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/bpf/bpf_lsm.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index f58dce888ff4..468e63dbd922 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -411,6 +411,8 @@ BTF_ID(func, bpf_lsm_bpf_map_free)
 BTF_ID(func, bpf_lsm_bpf_prog_free)
 BTF_ID(func, bpf_lsm_file_alloc_security)
 BTF_ID(func, bpf_lsm_file_free_security)
+BTF_ID(func, bpf_lsm_namespace_init)
+BTF_ID(func, bpf_lsm_namespace_free)
 #ifdef CONFIG_SECURITY_NETWORK
 BTF_ID(func, bpf_lsm_sk_alloc_security)
 BTF_ID(func, bpf_lsm_sk_free_security)

-- 
2.53.0


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

* [PATCH 3/8] selftests/bpf: add verifier tests for the namespace LSM hooks
  2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
  2026-10-26  9:57 ` [PATCH 1/8] lsm: always call the namespace_free hook Christian Brauner
  2026-10-26  9:57 ` [PATCH 2/8] bpf: don't mark namespace_init and namespace_free arguments as trusted Christian Brauner
@ 2026-10-26  9:57 ` Christian Brauner
  2026-10-26  9:57 ` [PATCH 4/8] landlock: pass the namespace id to log to check_ns_type() Christian Brauner
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Christian Brauner @ 2026-10-26  9:57 UTC (permalink / raw)
  To: linux-fsdevel, bpf
  Cc: linux-security-module, Alexei Starovoitov, Daniel Borkmann,
	Paul Moore, netdev, Christian Brauner (Amutable)

Add a test kfunc that takes a trusted struct ns_common and check that:

- namespace_init and namespace_free arguments can't be passed to it

- namespace_install arguments can

The test fails if namespace_init and namespace_free are treated as
trusted.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 tools/testing/selftests/bpf/prog_tests/verifier.c  |  2 ++
 .../selftests/bpf/progs/verifier_lsm_namespace.c   | 38 ++++++++++++++++++++++
 .../testing/selftests/bpf/test_kmods/bpf_testmod.c |  5 +++
 .../selftests/bpf/test_kmods/bpf_testmod_kfunc.h   |  1 +
 4 files changed, 46 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 04740ba37e91..3963a4f71d98 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -147,6 +147,7 @@
 #include "verifier_set_retval.skel.h"
 #include "verifier_lsm.skel.h"
 #include "verifier_lsm_init_xattr.skel.h"
+#include "verifier_lsm_namespace.skel.h"
 #include "verifier_jit_inline.skel.h"
 #include "irq.skel.h"
 #include "verifier_ctx_ptr_param.skel.h"
@@ -337,6 +338,7 @@ void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }
 void test_verifier_set_retval(void)            { RUN(verifier_set_retval); }
 void test_verifier_lsm(void)                  { RUN(verifier_lsm); }
 void test_verifier_lsm_init_xattr(void)       { RUN(verifier_lsm_init_xattr); }
+void test_verifier_lsm_namespace(void)        { RUN_TESTS(verifier_lsm_namespace); }
 void test_irq(void)			      { RUN(irq); }
 void test_verifier_mtu(void)		      { RUN(verifier_mtu); }
 void test_verifier_jit_inline(void)               { RUN(verifier_jit_inline); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_lsm_namespace.c b/tools/testing/selftests/bpf/progs/verifier_lsm_namespace.c
new file mode 100644
index 000000000000..d14fa2363dcc
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_lsm_namespace.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+#include "bpf_misc.h"
+#include "../test_kmods/bpf_testmod_kfunc.h"
+
+/* The namespace hasn't been set up yet. */
+SEC("lsm/namespace_init")
+__failure __msg("R1 must be referenced or trusted")
+int BPF_PROG(namespace_init_untrusted, struct ns_common *ns)
+{
+	bpf_kfunc_trusted_ns_test(ns);
+	return 0;
+}
+
+/* The namespace is being torn down. */
+SEC("lsm/namespace_free")
+__failure __msg("R1 must be referenced or trusted")
+int BPF_PROG(namespace_free_untrusted, struct ns_common *ns)
+{
+	bpf_kfunc_trusted_ns_test(ns);
+	return 0;
+}
+
+/* setns() holds a reference to a fully set up namespace. */
+SEC("lsm/namespace_install")
+__success
+int BPF_PROG(namespace_install_trusted, const struct nsset *nsset,
+	     struct ns_common *ns)
+{
+	bpf_kfunc_trusted_ns_test(ns);
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 6380f52f4dd2..8d6bd2237f34 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -350,6 +350,10 @@ __bpf_kfunc void bpf_kfunc_trusted_task_test(struct task_struct *ptr)
 {
 }
 
+__bpf_kfunc void bpf_kfunc_trusted_ns_test(struct ns_common *ptr)
+{
+}
+
 __bpf_kfunc struct task_struct *bpf_kfunc_trusted_null_test(void)
 {
 	return NULL;
@@ -1030,6 +1034,7 @@ BTF_ID_FLAGS(func, bpf_kfunc_nested_acquire_zero_offset_test, KF_ACQUIRE)
 BTF_ID_FLAGS(func, bpf_kfunc_nested_release_test, KF_RELEASE)
 BTF_ID_FLAGS(func, bpf_kfunc_trusted_vma_test)
 BTF_ID_FLAGS(func, bpf_kfunc_trusted_task_test)
+BTF_ID_FLAGS(func, bpf_kfunc_trusted_ns_test)
 BTF_ID_FLAGS(func, bpf_kfunc_trusted_null_test, KF_RET_NULL)
 BTF_ID_FLAGS(func, bpf_kfunc_trusted_nullable_test)
 BTF_ID_FLAGS(func, bpf_kfunc_trusted_num_test)
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
index 3b810f1e438f..bc32c4078d27 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
@@ -355,6 +355,7 @@ int bpf_kfunc_st_ops_inc10(struct st_ops_args *args) __ksym;
 
 void bpf_kfunc_trusted_vma_test(struct vm_area_struct *ptr) __ksym;
 void bpf_kfunc_trusted_task_test(struct task_struct *ptr) __ksym;
+void bpf_kfunc_trusted_ns_test(struct ns_common *ptr) __ksym;
 struct task_struct *bpf_kfunc_trusted_null_test(void) __ksym;
 void bpf_kfunc_trusted_nullable_test(struct task_struct *ptr__nullable) __ksym;
 void bpf_kfunc_trusted_num_test(int *ptr) __ksym;

-- 
2.53.0


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

* [PATCH 4/8] landlock: pass the namespace id to log to check_ns_type()
  2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
                   ` (2 preceding siblings ...)
  2026-10-26  9:57 ` [PATCH 3/8] selftests/bpf: add verifier tests for the namespace LSM hooks Christian Brauner
@ 2026-10-26  9:57 ` Christian Brauner
  2026-10-26  9:57 ` [PATCH 5/8] ns: allocate the namespace id before the namespace_init hook Christian Brauner
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Christian Brauner @ 2026-10-26  9:57 UTC (permalink / raw)
  To: linux-fsdevel, bpf
  Cc: linux-security-module, Alexei Starovoitov, Daniel Borkmann,
	Paul Moore, netdev, Christian Brauner (Amutable), stable

Landlock logs the namespace id when it denies the use of a namespace.
check_ns_type() takes it from the namespace itself. When it is called
from the namespace_init hook the id is always zero as it is only
allocated after the hook has run.

A follow-up patch will allocate the id before the namespace_init hook is
called. But a namespace whose creation is denied never becomes visible
and the Landlock documentation says that namespace_id is zero for
namespace creation (cf. Documentation/admin-guide/LSM/landlock.rst and
Documentation/trace/events-landlock.rst). So pass the id to log to
check_ns_type() and keep passing zero from hook_namespace_init().

No functional changes.

Fixes: f675d2e95569 ("lsm: add LSM blob and hooks for namespaces")
Cc: <stable@kernel.org> # mainline only
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 security/landlock/ns.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/security/landlock/ns.c b/security/landlock/ns.c
index cd44c927f83a..39795c4e1404 100644
--- a/security/landlock/ns.c
+++ b/security/landlock/ns.c
@@ -90,15 +90,16 @@ static const struct access_masks ns_permission = {
  * check_ns_type - Check namespace use permission
  *
  * @ns: The namespace being allocated or installed.
+ * @ns_id: The namespace id to log.
  *
  * Shared check for namespace_init (creation via clone(2), unshare(2),
  * open_tree(2), or fsmount(2)) and namespace_install (use via setns(2)): denies
  * when the namespace type is not in the domain's allowed set.  At allocation
- * time @ns->ns_id is still zero and is logged as such.
+ * time the namespace isn't visible yet so @ns_id is zero.
  *
  * Return: 0 if allowed, -EPERM if denied.
  */
-static int check_ns_type(struct ns_common *const ns)
+static int check_ns_type(struct ns_common *const ns, const u64 ns_id)
 {
 	const struct landlock_cred_security *subject;
 	size_t denied_layer;
@@ -121,7 +122,7 @@ static int check_ns_type(struct ns_common *const ns)
 				    .permission =
 					    LANDLOCK_PERMISSION_NAMESPACE_USE,
 				    .audit.u.ns.ns_type = ns->ns_type,
-				    .audit.u.ns.ns_id = ns->ns_id,
+				    .audit.u.ns.ns_id = ns_id,
 				    .layer_plus_one = denied_layer,
 			    });
 	return -EPERM;
@@ -129,13 +130,13 @@ static int check_ns_type(struct ns_common *const ns)
 
 static int hook_namespace_init(struct ns_common *const ns)
 {
-	return check_ns_type(ns);
+	return check_ns_type(ns, 0);
 }
 
 static int hook_namespace_install(const struct nsset *const nsset,
 				  struct ns_common *const ns)
 {
-	return check_ns_type(ns);
+	return check_ns_type(ns, ns->ns_id);
 }
 
 static struct security_hook_list landlock_hooks[] __ro_after_init = {

-- 
2.53.0


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

* [PATCH 5/8] ns: allocate the namespace id before the namespace_init hook
  2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
                   ` (3 preceding siblings ...)
  2026-10-26  9:57 ` [PATCH 4/8] landlock: pass the namespace id to log to check_ns_type() Christian Brauner
@ 2026-10-26  9:57 ` Christian Brauner
  2026-10-26  9:57 ` [PATCH 6/8] nstree: drop ns_tree_add_raw() Christian Brauner
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Christian Brauner @ 2026-10-26  9:57 UTC (permalink / raw)
  To: linux-fsdevel, bpf
  Cc: linux-security-module, Alexei Starovoitov, Daniel Borkmann,
	Paul Moore, netdev, Christian Brauner (Amutable), stable

Currently the namespace id is allocated only after __ns_common_init()
has called the namespace_init LSM hook. Most namespace types allocate it
in ns_tree_add() right before the namespace is added to the namespace
trees. So the namespace_init hook always sees an id of zero.

The namespace id is the only identifier of a namespace that is never
recycled. A bpf LSM wants to use it to track a namespace from
namespace_init until namespace_free, e.g., by using it as the key into a
map that associates data with the namespace. The inode number doesn't
work for that as it is quickly recycled.

So allocate the id in __ns_common_init() before calling the
namespace_init hook. Every namespace then has its id by the time it is
added to the namespace trees. The initial namespaces still get their
fixed ids, either statically from NS_COMMON_INIT() or for init_net from
ns_common_init(). So ns_tree_add() doesn't need to allocate an id
anymore. Ids are still handed out in creation order so mnt_ns_loop()
keeps working. Mount and ipc namespaces already allocated their id right
after ns_common_init() anyway.

If creating the namespace fails after the id has been allocated the id
is simply never used.

While at it, drop ns_tree_gen_id() as it has no users left.

Reproducer, 100 user and uts namespaces:

  bpftrace -e 'fentry:bpf_lsm_namespace_init {
      @[args.ns->ns_id ? "set" : "zero"] = count(); }' &
  for i in $(seq 100); do unshare -Uu true; done

  before: @[zero]: 200
  after:  @[set]: 200

Fixes: f675d2e95569 ("lsm: add LSM blob and hooks for namespaces")
Cc: <stable@kernel.org> # mainline only
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/namespace.c            |  2 --
 include/linux/ns_common.h | 16 +++++++++-------
 include/linux/nstree.h    | 18 +++---------------
 ipc/namespace.c           |  1 -
 kernel/nscommon.c         |  7 +++++--
 net/core/net_namespace.c  |  2 +-
 6 files changed, 18 insertions(+), 28 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index 9f6ec35c7b9c..f81d6a4bc3fe 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4394,8 +4394,6 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns, bool a
 		dec_mnt_namespaces(ucounts);
 		return ERR_PTR(ret);
 	}
-	ns_tree_gen_id(new_ns);
-
 	new_ns->is_anon = anon;
 	refcount_set(&new_ns->passive, 1);
 	new_ns->mounts = RB_ROOT;
diff --git a/include/linux/ns_common.h b/include/linux/ns_common.h
index c8e227a3f9e2..e8b2bf0da5ed 100644
--- a/include/linux/ns_common.h
+++ b/include/linux/ns_common.h
@@ -9,7 +9,8 @@
 #include <uapi/linux/nsfs.h>
 
 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);
+int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_operations *ops,
+		     int inum, u64 ns_id);
 void __ns_common_free(struct ns_common *ns);
 struct ns_common *__must_check ns_owner(struct ns_common *ns);
 
@@ -41,17 +42,18 @@ static __always_inline bool is_ns_init_id(const struct ns_common *ns)
 	.ns_owner_root.ns_list_head	= LIST_HEAD_INIT(nsname.ns.ns_owner_root.ns_list_head),		\
 }
 
-#define ns_common_init(__ns)                     \
-	__ns_common_init(to_ns_common(__ns),     \
-			 ns_common_type(__ns),   \
-			 to_ns_operations(__ns), \
-			 (((__ns) == ns_init_ns(__ns)) ? ns_init_inum(__ns) : 0))
+#define ns_common_init(__ns)						\
+	__ns_common_init(to_ns_common(__ns),				\
+			 ns_common_type(__ns),				\
+			 to_ns_operations(__ns),			\
+			 (((__ns) == ns_init_ns(__ns)) ? ns_init_inum(__ns) : 0), \
+			 (((__ns) == ns_init_ns(__ns)) ? ns_init_id(__ns) : 0))
 
 #define ns_common_init_inum(__ns, __inum)        \
 	__ns_common_init(to_ns_common(__ns),     \
 			 ns_common_type(__ns),   \
 			 to_ns_operations(__ns), \
-			 __inum)
+			 __inum, 0)
 
 #define ns_common_free(__ns) __ns_common_free(to_ns_common((__ns)))
 
diff --git a/include/linux/nstree.h b/include/linux/nstree.h
index 5b64d4572881..3293df17397d 100644
--- a/include/linux/nstree.h
+++ b/include/linux/nstree.h
@@ -41,10 +41,6 @@ void ns_tree_node_del(struct ns_tree_node *node, struct ns_tree_root *root);
 		struct user_namespace *:   &(user_ns_tree),	\
 		struct uts_namespace *:    &(uts_ns_tree))
 
-#define ns_tree_gen_id(__ns)                 \
-	__ns_tree_gen_id(to_ns_common(__ns), \
-			 (((__ns) == ns_init_ns(__ns)) ? ns_init_id(__ns) : 0))
-
 u64 __ns_tree_gen_id(struct ns_common *ns, u64 id);
 void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree_root *ns_tree);
 void __ns_tree_remove(struct ns_common *ns, struct ns_tree_root *ns_tree);
@@ -53,12 +49,6 @@ struct ns_common *__ns_tree_adjoined_rcu(struct ns_common *ns,
 					 struct ns_tree_root *ns_tree,
 					 bool previous);
 
-static inline void __ns_tree_add(struct ns_common *ns, struct ns_tree_root *ns_tree, u64 id)
-{
-	__ns_tree_gen_id(ns, id);
-	__ns_tree_add_raw(ns, ns_tree);
-}
-
 /**
  * ns_tree_add_raw - Add a namespace to a namespace
  * @__ns: Namespace to add
@@ -72,12 +62,10 @@ static inline void __ns_tree_add(struct ns_common *ns, struct ns_tree_root *ns_t
  * ns_tree_add - Add a namespace to a namespace tree
  * @__ns: Namespace to add
  *
- * This function assigns a new id to the namespace and adds it to the
- * appropriate namespace tree and list.
+ * This function adds a namespace to the appropriate namespace tree and
+ * list. The namespace must already have been assigned an id.
  */
-#define ns_tree_add(__ns)                                   \
-	__ns_tree_add(to_ns_common(__ns), to_ns_tree(__ns), \
-		      (((__ns) == ns_init_ns(__ns)) ? ns_init_id(__ns) : 0))
+#define ns_tree_add(__ns) __ns_tree_add_raw(to_ns_common(__ns), to_ns_tree(__ns))
 
 /**
  * ns_tree_remove - Remove a namespace from a namespace tree
diff --git a/ipc/namespace.c b/ipc/namespace.c
index 75aa7b469348..9084e0643017 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -66,7 +66,6 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
 	if (err)
 		goto fail_free;
 
-	ns_tree_gen_id(ns);
 	ns->user_ns = get_user_ns(user_ns);
 	ns->ucounts = ucounts;
 
diff --git a/kernel/nscommon.c b/kernel/nscommon.c
index e72426bba29a..4438c6b663f9 100644
--- a/kernel/nscommon.c
+++ b/kernel/nscommon.c
@@ -54,7 +54,8 @@ static void ns_debug(struct ns_common *ns, const struct proc_ns_operations *ops)
 }
 #endif
 
-int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_operations *ops, int inum)
+int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_operations *ops,
+		     int inum, u64 ns_id)
 {
 	int ret = 0;
 
@@ -64,7 +65,6 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
 	ns->ns_security = NULL;
 #endif
 	ns->ops = ops;
-	ns->ns_id = 0;
 	ns->ns_type = ns_type;
 	ns_tree_node_init(&ns->ns_tree_node);
 	ns_tree_node_init(&ns->ns_unified_node);
@@ -82,6 +82,9 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
 	if (ret)
 		return ret;
 
+	/* Allocate the id before the LSM hook so it can track the namespace by it. */
+	__ns_tree_gen_id(ns, ns_id);
+
 	ret = security_namespace_init(ns);
 	if (ret) {
 		if (!inum)
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index da5f881fbd3b..ec4077b81389 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -437,7 +437,7 @@ static __net_init int setup_net(struct net *net)
 	LIST_HEAD(net_exit_list);
 	int error = 0;
 
-	net->net_cookie = ns_tree_gen_id(net);
+	net->net_cookie = to_ns_common(net)->ns_id;
 
 	list_for_each_entry(ops, &pernet_list, list) {
 		error = ops_init(ops, net);

-- 
2.53.0


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

* [PATCH 6/8] nstree: drop ns_tree_add_raw()
  2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
                   ` (4 preceding siblings ...)
  2026-10-26  9:57 ` [PATCH 5/8] ns: allocate the namespace id before the namespace_init hook Christian Brauner
@ 2026-10-26  9:57 ` Christian Brauner
  2026-10-26  9:57 ` [PATCH 7/8] lsm: fix the security_namespace_init() kernel-doc Christian Brauner
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Christian Brauner @ 2026-10-26  9:57 UTC (permalink / raw)
  To: linux-fsdevel, bpf
  Cc: linux-security-module, Alexei Starovoitov, Daniel Borkmann,
	Paul Moore, netdev, Christian Brauner (Amutable), stable

Now that ns_tree_add() doesn't allocate an id anymore it does the same
thing as ns_tree_add_raw(). Switch the remaining ns_tree_add_raw() users
over to ns_tree_add() and drop it. Rename __ns_tree_add_raw() to
__ns_tree_add() while at it.

No functional changes.

Fixes: f675d2e95569 ("lsm: add LSM blob and hooks for namespaces")
Cc: <stable@kernel.org> # mainline only
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/namespace.c           |  4 ++--
 include/linux/nstree.h   | 13 ++-----------
 ipc/namespace.c          |  2 +-
 kernel/nstree.c          |  2 +-
 net/core/net_namespace.c |  2 +-
 5 files changed, 7 insertions(+), 16 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index f81d6a4bc3fe..2f9a1460b4ac 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3296,7 +3296,7 @@ static struct mnt_namespace *create_new_namespace(struct path *path,
 	}
 
 	new_ns->root = new_ns_root;
-	ns_tree_add_raw(new_ns);
+	ns_tree_add(new_ns);
 	return new_ns;
 }
 
@@ -4497,7 +4497,7 @@ struct mnt_namespace *copy_mnt_ns(u64 flags, struct mnt_namespace *ns,
 				p = next_mnt(skip_mnt_tree(p), old);
 		}
 	}
-	ns_tree_add_raw(new_ns);
+	ns_tree_add(new_ns);
 	return new_ns;
 }
 
diff --git a/include/linux/nstree.h b/include/linux/nstree.h
index 3293df17397d..eeed713804be 100644
--- a/include/linux/nstree.h
+++ b/include/linux/nstree.h
@@ -42,22 +42,13 @@ void ns_tree_node_del(struct ns_tree_node *node, struct ns_tree_root *root);
 		struct uts_namespace *:    &(uts_ns_tree))
 
 u64 __ns_tree_gen_id(struct ns_common *ns, u64 id);
-void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree_root *ns_tree);
+void __ns_tree_add(struct ns_common *ns, struct ns_tree_root *ns_tree);
 void __ns_tree_remove(struct ns_common *ns, struct ns_tree_root *ns_tree);
 struct ns_common *ns_tree_lookup_rcu(u64 ns_id, int ns_type);
 struct ns_common *__ns_tree_adjoined_rcu(struct ns_common *ns,
 					 struct ns_tree_root *ns_tree,
 					 bool previous);
 
-/**
- * ns_tree_add_raw - Add a namespace to a namespace
- * @__ns: Namespace to add
- *
- * This function adds a namespace to the appropriate namespace tree
- * without assigning a id.
- */
-#define ns_tree_add_raw(__ns) __ns_tree_add_raw(to_ns_common(__ns), to_ns_tree(__ns))
-
 /**
  * ns_tree_add - Add a namespace to a namespace tree
  * @__ns: Namespace to add
@@ -65,7 +56,7 @@ struct ns_common *__ns_tree_adjoined_rcu(struct ns_common *ns,
  * This function adds a namespace to the appropriate namespace tree and
  * list. The namespace must already have been assigned an id.
  */
-#define ns_tree_add(__ns) __ns_tree_add_raw(to_ns_common(__ns), to_ns_tree(__ns))
+#define ns_tree_add(__ns) __ns_tree_add(to_ns_common(__ns), to_ns_tree(__ns))
 
 /**
  * ns_tree_remove - Remove a namespace from a namespace tree
diff --git a/ipc/namespace.c b/ipc/namespace.c
index 9084e0643017..b4fab86aee27 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -86,7 +86,7 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
 
 	sem_init_ns(ns);
 	shm_init_ns(ns);
-	ns_tree_add_raw(ns);
+	ns_tree_add(ns);
 
 	return ns;
 
diff --git a/kernel/nstree.c b/kernel/nstree.c
index 831f279d174a..94153dfddb3d 100644
--- a/kernel/nstree.c
+++ b/kernel/nstree.c
@@ -194,7 +194,7 @@ static int ns_cmp_owner(struct rb_node *a, const struct rb_node *b)
 	return ns_id_cmp(node_to_ns_owner(a)->ns_id, node_to_ns_owner(b)->ns_id);
 }
 
-void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree_root *ns_tree)
+void __ns_tree_add(struct ns_common *ns, struct ns_tree_root *ns_tree)
 {
 	struct rb_node *node;
 	const struct proc_ns_operations *ops = ns->ops;
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index ec4077b81389..54ba07eba003 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -447,7 +447,7 @@ static __net_init int setup_net(struct net *net)
 	down_write(&net_rwsem);
 	list_add_tail_rcu(&net->list, &net_namespace_list);
 	up_write(&net_rwsem);
-	ns_tree_add_raw(net);
+	ns_tree_add(net);
 out:
 	return error;
 

-- 
2.53.0


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

* [PATCH 7/8] lsm: fix the security_namespace_init() kernel-doc
  2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
                   ` (5 preceding siblings ...)
  2026-10-26  9:57 ` [PATCH 6/8] nstree: drop ns_tree_add_raw() Christian Brauner
@ 2026-10-26  9:57 ` Christian Brauner
  2026-10-26  9:57 ` [PATCH 8/8] selftests/bpf: add a lifecycle test for the namespace LSM hooks Christian Brauner
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Christian Brauner @ 2026-10-26  9:57 UTC (permalink / raw)
  To: linux-fsdevel, bpf
  Cc: linux-security-module, Alexei Starovoitov, Daniel Borkmann,
	Paul Moore, netdev, Christian Brauner (Amutable), stable

The kernel-doc claims that the owning user namespace is available via
ns->ops->owner(ns). But security_namespace_init() is called from
__ns_common_init() before the namespace has been set up. Apart from
network namespaces the owning user namespace isn't set at that point so
->owner() returns NULL. And for init_net with CONFIG_NET_NS=n ns->ops
itself is NULL.

What a namespace_init hook can rely on is the namespace type and, now
that it is allocated before the hook is called, the namespace id. So
document that instead.

No functional changes.

Fixes: f675d2e95569 ("lsm: add LSM blob and hooks for namespaces")
Cc: <stable@kernel.org> # mainline only
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 security/security.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/security/security.c b/security/security.c
index 1e76c097b28d..81dac9e48a51 100644
--- a/security/security.c
+++ b/security/security.c
@@ -3382,9 +3382,9 @@ int security_create_user_ns(const struct cred *cred)
  * security_namespace_init() - Initialize LSM security data for a namespace
  * @ns: the namespace being initialized
  *
- * Initialize the LSM security blob attached to the namespace. The namespace type
- * is available via ns->ns_type, and the owning user namespace (if any)
- * via ns->ops->owner(ns).
+ * Initialize the LSM security blob attached to the namespace. The namespace
+ * hasn't been set up yet but its type and id are available via ns->ns_type
+ * and ns->ns_id.
  *
  * Return: Returns 0 if successful, otherwise < 0 error code.
  */

-- 
2.53.0


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

* [PATCH 8/8] selftests/bpf: add a lifecycle test for the namespace LSM hooks
  2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
                   ` (6 preceding siblings ...)
  2026-10-26  9:57 ` [PATCH 7/8] lsm: fix the security_namespace_init() kernel-doc Christian Brauner
@ 2026-10-26  9:57 ` Christian Brauner
  2026-10-26 20:31 ` [PATCH 0/8] ns: fixes " Paul Moore
  2026-10-26 23:31 ` Alexei Starovoitov
  9 siblings, 0 replies; 16+ messages in thread
From: Christian Brauner @ 2026-10-26  9:57 UTC (permalink / raw)
  To: linux-fsdevel, bpf
  Cc: linux-security-module, Alexei Starovoitov, Daniel Borkmann,
	Paul Moore, netdev, Christian Brauner (Amutable)

Track new uts, ipc and net namespaces from a bpf LSM by their namespace
id and check that:

- namespace_init already sees the id that NS_GET_ID reports

- namespace_init, namespace_install and namespace_free fire exactly once
  for each of them

The test fails if the namespace_free hook isn't called or if the
namespace id is allocated after namespace_init.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 .../selftests/bpf/prog_tests/ns_lifecycle.c        | 145 +++++++++++++++++++++
 .../selftests/bpf/progs/test_ns_lifecycle.c        |  88 +++++++++++++
 2 files changed, 233 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/ns_lifecycle.c b/tools/testing/selftests/bpf/prog_tests/ns_lifecycle.c
new file mode 100644
index 000000000000..8ea4939efb23
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/ns_lifecycle.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Exercise all namespace LSM hooks from a BPF LSM: unshare() new
+ * namespaces (namespace_init), setns() into them (namespace_install) and
+ * drop the last reference (namespace_free).
+ */
+
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <sched.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <linux/nsfs.h>
+#include "task_local_storage_helpers.h"
+#include "test_ns_lifecycle.skel.h"
+
+struct ns_stat {
+	__u32 ns_type;
+	__u32 init_count;
+	__u32 install_count;
+	__u32 free_count;
+};
+
+static const struct {
+	const char *path;
+	int type;
+} namespaces[] = {
+	{ "/proc/self/ns/uts", CLONE_NEWUTS },
+	{ "/proc/self/ns/ipc", CLONE_NEWIPC },
+	{ "/proc/self/ns/net", CLONE_NEWNET },
+};
+
+#define NR_NS ARRAY_SIZE(namespaces)
+
+static bool all_freed(int map_fd, const __u64 *ns_id)
+{
+	struct ns_stat stat;
+	int i;
+
+	for (i = 0; i < NR_NS; i++) {
+		if (bpf_map_lookup_elem(map_fd, &ns_id[i], &stat) || !stat.free_count)
+			return false;
+	}
+	return true;
+}
+
+void test_ns_lifecycle(void)
+{
+	int orig_fd[NR_NS] = { -1, -1, -1 }, new_fd[NR_NS] = { -1, -1, -1 };
+	struct test_ns_lifecycle *skel = NULL;
+	int pidfd, map_fd, err, i, tries;
+	struct ns_stat stat;
+	__u64 ns_id[NR_NS];
+	struct stat st;
+
+	for (i = 0; i < NR_NS; i++) {
+		orig_fd[i] = open(namespaces[i].path, O_RDONLY | O_CLOEXEC);
+		if (!ASSERT_OK_FD(orig_fd[i], "open orig ns"))
+			goto close_fds;
+	}
+
+	pidfd = sys_pidfd_open(getpid(), 0);
+	if (!ASSERT_OK_FD(pidfd, "pidfd_open"))
+		goto close_fds;
+	err = fstat(pidfd, &st);
+	close(pidfd);
+	if (!ASSERT_OK(err, "fstat pidfd"))
+		goto close_fds;
+
+	skel = test_ns_lifecycle__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "skel open_and_load"))
+		goto close_fds;
+
+	err = test_ns_lifecycle__attach(skel);
+	if (!ASSERT_OK(err, "skel attach"))
+		goto destroy;
+
+	map_fd = bpf_map__fd(skel->maps.ns_lifecycle);
+	skel->bss->monitored_pidfd_ino = st.st_ino;
+
+	err = unshare(CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWNET);
+	if (!ASSERT_OK(err, "unshare"))
+		goto disarm;
+
+	for (i = 0; i < NR_NS; i++) {
+		new_fd[i] = open(namespaces[i].path, O_RDONLY | O_CLOEXEC);
+		if (!ASSERT_OK_FD(new_fd[i], "open new ns"))
+			goto restore;
+		if (!ASSERT_OK(ioctl(new_fd[i], NS_GET_ID, &ns_id[i]), "NS_GET_ID"))
+			goto restore;
+	}
+
+	/* Leave the new namespaces and install them again. */
+	for (i = 0; i < NR_NS; i++) {
+		if (!ASSERT_OK(setns(orig_fd[i], namespaces[i].type), "setns orig"))
+			goto restore;
+		if (!ASSERT_OK(setns(new_fd[i], namespaces[i].type), "setns new"))
+			goto restore;
+		if (!ASSERT_OK(setns(orig_fd[i], namespaces[i].type), "setns orig again"))
+			goto restore;
+	}
+
+	skel->bss->monitored_pidfd_ino = 0;
+
+	/* Drop the last references. Network namespaces are freed asynchronously. */
+	for (i = 0; i < NR_NS; i++) {
+		close(new_fd[i]);
+		new_fd[i] = -1;
+	}
+
+	for (tries = 0; tries < 1000 && !all_freed(map_fd, ns_id); tries++) {
+		kern_sync_rcu();
+		usleep(10000);
+	}
+
+	for (i = 0; i < NR_NS; i++) {
+		/* namespace_init must already have seen the id. */
+		if (!ASSERT_OK(bpf_map_lookup_elem(map_fd, &ns_id[i], &stat), "lookup ns_stat"))
+			continue;
+
+		ASSERT_EQ(stat.ns_type, namespaces[i].type, "ns_type");
+		ASSERT_EQ(stat.init_count, 1, "init_count");
+		ASSERT_EQ(stat.install_count, 1, "install_count");
+		ASSERT_EQ(stat.free_count, 1, "free_count");
+	}
+	goto destroy;
+
+restore:
+	for (i = 0; i < NR_NS; i++)
+		setns(orig_fd[i], namespaces[i].type);
+disarm:
+	skel->bss->monitored_pidfd_ino = 0;
+destroy:
+	test_ns_lifecycle__destroy(skel);
+close_fds:
+	for (i = 0; i < NR_NS; i++) {
+		if (new_fd[i] >= 0)
+			close(new_fd[i]);
+		if (orig_fd[i] >= 0)
+			close(orig_fd[i]);
+	}
+}
diff --git a/tools/testing/selftests/bpf/progs/test_ns_lifecycle.c b/tools/testing/selftests/bpf/progs/test_ns_lifecycle.c
new file mode 100644
index 000000000000..88c5d40ba27a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_ns_lifecycle.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+struct ns_stat {
+	__u32 ns_type;
+	__u32 init_count;
+	__u32 install_count;
+	__u32 free_count;
+};
+
+/* Unlike inode numbers namespace ids are never recycled. */
+struct {
+	__uint(type, BPF_MAP_TYPE_HASH);
+	__uint(max_entries, 16);
+	__type(key, __u64);
+	__type(value, struct ns_stat);
+} ns_lifecycle SEC(".maps");
+
+__u64 monitored_pidfd_ino = 0;
+
+char _license[] SEC("license") = "GPL";
+
+static bool is_monitored(void)
+{
+	struct task_struct *task;
+
+	if (!monitored_pidfd_ino)
+		return false;
+
+	task = bpf_get_current_task_btf();
+	return task->thread_pid->ino == monitored_pidfd_ino;
+}
+
+SEC("lsm/namespace_init")
+int BPF_PROG(ns_init, struct ns_common *ns, int ret)
+{
+	struct ns_stat new = {}, *stat;
+	__u64 ns_id;
+
+	if (ret)
+		return ret;
+
+	if (!is_monitored())
+		return 0;
+
+	ns_id = ns->ns_id;
+	new.ns_type = ns->ns_type;
+	bpf_map_update_elem(&ns_lifecycle, &ns_id, &new, BPF_NOEXIST);
+	stat = bpf_map_lookup_elem(&ns_lifecycle, &ns_id);
+	if (stat)
+		__sync_fetch_and_add(&stat->init_count, 1);
+	return 0;
+}
+
+SEC("lsm/namespace_install")
+int BPF_PROG(ns_install, const struct nsset *nsset, struct ns_common *ns, int ret)
+{
+	struct ns_stat *stat;
+	__u64 ns_id;
+
+	if (ret)
+		return ret;
+
+	if (!is_monitored())
+		return 0;
+
+	ns_id = ns->ns_id;
+	stat = bpf_map_lookup_elem(&ns_lifecycle, &ns_id);
+	if (stat)
+		__sync_fetch_and_add(&stat->install_count, 1);
+	return 0;
+}
+
+SEC("lsm/namespace_free")
+int BPF_PROG(ns_free, struct ns_common *ns)
+{
+	struct ns_stat *stat;
+	__u64 ns_id;
+
+	ns_id = ns->ns_id;
+	stat = bpf_map_lookup_elem(&ns_lifecycle, &ns_id);
+	if (stat)
+		__sync_fetch_and_add(&stat->free_count, 1);
+	return 0;
+}

-- 
2.53.0


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

* Re: [PATCH 0/8] ns: fixes for the namespace LSM hooks
  2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
                   ` (7 preceding siblings ...)
  2026-10-26  9:57 ` [PATCH 8/8] selftests/bpf: add a lifecycle test for the namespace LSM hooks Christian Brauner
@ 2026-10-26 20:31 ` Paul Moore
  2026-10-27  9:24   ` Christian Brauner
  2026-10-26 23:31 ` Alexei Starovoitov
  9 siblings, 1 reply; 16+ messages in thread
From: Paul Moore @ 2026-10-26 20:31 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-fsdevel, bpf, linux-security-module, Alexei Starovoitov,
	Daniel Borkmann, netdev, stable

On Mon, Oct 26, 2026 at 5:57 AM Christian Brauner <brauner@kernel.org> wrote:
>
> I tried to switch systemd-nsresourced over to the namespace_free LSM
> hook that went in this cycle. I explicitly developed it to work nicely
> with that. But the version that is merged is broken for that case.
>
> The namespace_free hook is never called. security_namespace_free() bails
> if ns->ns_security is NULL and that's only allocated if an LSM requests
> a namespace blob. Nobody does. Landlock doesn't care as it only uses
> namespace_init and namespace_install but a bpf program attached to
> namespace_free never runs. And it can't work around that either. We
> could make the bpf LSM request a blob but that would just be a way to
> make the hook fire. So call it unconditionally like we do for
> superblocks, files and bpf maps.
>
> The bpf LSM also marks the arguments of namespace_init and
> namespace_free as trusted. Curiously that patch was also dropped.
>
> namespace_init is called before the namespace is set up and if anything
> fails afterwards the namespace is freed without looking at its reference
> count. namespace_free is called on the way out. There's no kfunc that
> takes a namespace yet but once there is one a program could take a
> reference to a namespace that's about to be freed. So add both hooks to
> untrusted_lsm_hooks.
>
> The namespace_init hook also always sees a namespace id of zero because
> the id is only allocated after the hook has run. The id is the only
> identifier of a namespace that is never recycled. So it's what a bpf LSM
> wants to use to track a namespace from namespace_init until
> namespace_free.
>
> The inode number is recycled way too quickly for that. So allocate the
> id in __ns_common_init() before the hook is called. Then ns_tree_add()
> doesn't need to allocate ids anymore and ns_tree_add_raw() can go.
> Landlock keeps logging zero for denied namespace creation because that's
> what it documents.
>
> I'd want to get the first three patches into v7.4 since the hooks are
> new this cycle. The systemd change I have for this uses the
> namespace_free hook whenever the kernel has it. On a kernel with the
> hook but without the first fix it would never learn about dead user
> namespaces.

From what I'm reading the LSM framework patches don't meet the typical
LSM criteria for stable/RC fixes: there is no serious bug/panic, and
there is no functional regression from a previous release.  It also
appears that the existing Landlock namespace code still works as
expected.  This looks like one of those unfortunate circumstances
where a new use case wasn't tested thoroughly enough before the merge
window, so it can wait for the next one.  We've already identified
other LSM/namespace issues that are targeting Linux v7.5, the
security_namespace_free() change is one of those as far as I'm
concerned.

> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> ---
> Christian Brauner (8):
>       lsm: always call the namespace_free hook
>       bpf: don't mark namespace_init and namespace_free arguments as trusted
>       selftests/bpf: add verifier tests for the namespace LSM hooks
>       landlock: pass the namespace id to log to check_ns_type()
>       ns: allocate the namespace id before the namespace_init hook
>       nstree: drop ns_tree_add_raw()
>       lsm: fix the security_namespace_init() kernel-doc
>       selftests/bpf: add a lifecycle test for the namespace LSM hooks
>
>  fs/namespace.c                                     |   6 +-
>  include/linux/ns_common.h                          |  16 ++-
>  include/linux/nstree.h                             |  29 +----
>  ipc/namespace.c                                    |   3 +-
>  kernel/bpf/bpf_lsm.c                               |   2 +
>  kernel/nscommon.c                                  |   7 +-
>  kernel/nstree.c                                    |   2 +-
>  net/core/net_namespace.c                           |   4 +-
>  security/landlock/ns.c                             |  11 +-
>  security/security.c                                |   9 +-
>  .../selftests/bpf/prog_tests/ns_lifecycle.c        | 145 +++++++++++++++++++++
>  tools/testing/selftests/bpf/prog_tests/verifier.c  |   2 +
>  .../selftests/bpf/progs/test_ns_lifecycle.c        |  88 +++++++++++++
>  .../selftests/bpf/progs/verifier_lsm_namespace.c   |  38 ++++++
>  .../testing/selftests/bpf/test_kmods/bpf_testmod.c |   5 +
>  .../selftests/bpf/test_kmods/bpf_testmod_kfunc.h   |   1 +
>  16 files changed, 314 insertions(+), 54 deletions(-)
> ---
> base-commit: 21a7f44fd0765fcc5194130b28767786eba1d1e7
> change-id: 20261026-work-security-namespace_free-7aaf89b5b00e

-- 
paul-moore.com

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

* Re: [PATCH 0/8] ns: fixes for the namespace LSM hooks
  2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
                   ` (8 preceding siblings ...)
  2026-10-26 20:31 ` [PATCH 0/8] ns: fixes " Paul Moore
@ 2026-10-26 23:31 ` Alexei Starovoitov
  9 siblings, 0 replies; 16+ messages in thread
From: Alexei Starovoitov @ 2026-10-26 23:31 UTC (permalink / raw)
  To: Christian Brauner, linux-fsdevel, bpf
  Cc: linux-security-module, Daniel Borkmann, Paul Moore, netdev,
	stable

On Mon, Oct 26, 2026 at 02:57 AM Christian Brauner <brauner@kernel.org> wrote:
> I'd want to get the first three patches into v7.4 since the hooks are
> new this cycle. The systemd change I have for this uses the
> namespace_free hook whenever the kernel has it. On a kernel with the
> hook but without the first fix it would never learn about dead user
> namespaces.

Why not the whole series?
Otherwise 7.4 will be the only release where namespace_init sees
ns_id == 0 and bpf progs that key by it will have to deal with that.
Also the test for patch 1 is in patch 8 and it needs patch 5.

For bpf bits:
Acked-by: Alexei Starovoitov <ast@kernel.org>

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

* Re: [PATCH 0/8] ns: fixes for the namespace LSM hooks
  2026-10-26 20:31 ` [PATCH 0/8] ns: fixes " Paul Moore
@ 2026-10-27  9:24   ` Christian Brauner
  2026-10-27 14:36     ` Daniel Borkmann
  2026-10-27 22:37     ` Paul Moore
  0 siblings, 2 replies; 16+ messages in thread
From: Christian Brauner @ 2026-10-27  9:24 UTC (permalink / raw)
  To: Paul Moore, Linus Torvalds
  Cc: Christian Brauner, linux-fsdevel, bpf, linux-security-module,
	Alexei Starovoitov, Daniel Borkmann, Mickaël Salaün,
	Günther Noack, KP Singh, Matt Bobrowski, netdev, stable

On Mon, Oct 26, 2026 at 04:31:25PM -0400, Paul Moore wrote:
> From what I'm reading the LSM framework patches don't meet the typical
> LSM criteria for stable/RC fixes: there is no serious bug/panic, and
> there is no functional regression from a previous release.
> This looks like one of those unfortunate circumstances where a new use
> case wasn't tested thoroughly enough before the merge window, so it
> can wait for the next one.

My original patchset had a selftest that excercised the BPF LSM usability
extensively. The hooks are new this cycle and broken for the main use-case I
wrote the patchset for.

If we don't fix them now they need to be backported next cycle which I'm sure
the stable developers will enjoy.

Curiously, your pull request told Linus "I expect that BPF LSM developers will
also be making use of the namespace hooks, if they haven't already". So it
seems very strange to claim this as a new use-case.

>From my perspective the timeline looks like this - I'm sure yours differs:

  I wrote an implementation for the BPF LSM to use a set of new hooks in a
  subsystem I maintain months ago.

  You rejected the hooks because BPF LSM programs cannot serve as justification
  to introduce new hooks. Only in-tree LSM (selinux, apparmor, landlock, etc) can.

  I cannot fulfill this requirement because I neither have the expertise nor
  the use-case for any in-tree LSMs.

  Landlock wants to make use of this by pure chance. So the maintainer offers
  to take over the patchset. He does.

End result after more than two kernel releases: The patchset that ends up
merged into upstream doesn't work with the original motivating use-case because
all the BPF LSM bits got dropped. So far, no problem. That may happen as long
as we can the fix it rapidly.

Then I take the time to send this series of fixes to actually restore the
original use-case so we don't release a kernel with a new api that doesn't work
for the main use-case I developed this for.

Once again, I get told no. This time because the functionality isn't broken the
right way to justify a merge-window follow-up. Instead, we'll release
half-working functionality.

I'm really on the fringes of this endless debacle but this seems to be another
iteration of "BPF people don't get along with LSM people" therefore they make
each other's lifes and that of innocent bystanders that just want to get work
done difficult.

I really don't understand why it's preferable to release a kernel where the
original use-case that I was forced to explicitly abandon doesn't work and
there's no reliable or cost-efficient way for userspace to detect whether the
hook works correctly.

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

* Re: [PATCH 0/8] ns: fixes for the namespace LSM hooks
  2026-10-27  9:24   ` Christian Brauner
@ 2026-10-27 14:36     ` Daniel Borkmann
  2026-10-27 22:37     ` Paul Moore
  1 sibling, 0 replies; 16+ messages in thread
From: Daniel Borkmann @ 2026-10-27 14:36 UTC (permalink / raw)
  To: Christian Brauner, Paul Moore, Linus Torvalds
  Cc: linux-fsdevel, bpf, linux-security-module, Alexei Starovoitov,
	Mickaël Salaün, Günther Noack, KP Singh,
	Matt Bobrowski, netdev, stable

On 10/27/26 10:24 AM, Christian Brauner wrote:
> On Mon, Oct 26, 2026 at 04:31:25PM -0400, Paul Moore wrote:
>>  From what I'm reading the LSM framework patches don't meet the typical
>> LSM criteria for stable/RC fixes: there is no serious bug/panic, and
>> there is no functional regression from a previous release.
>> This looks like one of those unfortunate circumstances where a new use
>> case wasn't tested thoroughly enough before the merge window, so it
>> can wait for the next one.
> 
> My original patchset had a selftest that excercised the BPF LSM usability
> extensively. The hooks are new this cycle and broken for the main use-case I
> wrote the patchset for.
> 
> If we don't fix them now they need to be backported next cycle which I'm sure
> the stable developers will enjoy.
> 
> Curiously, your pull request told Linus "I expect that BPF LSM developers will
> also be making use of the namespace hooks, if they haven't already". So it
> seems very strange to claim this as a new use-case.
> 
>>From my perspective the timeline looks like this - I'm sure yours differs:
> 
>    I wrote an implementation for the BPF LSM to use a set of new hooks in a
>    subsystem I maintain months ago.
> 
>    You rejected the hooks because BPF LSM programs cannot serve as justification
>    to introduce new hooks. Only in-tree LSM (selinux, apparmor, landlock, etc) can.
> 
>    I cannot fulfill this requirement because I neither have the expertise nor
>    the use-case for any in-tree LSMs.
> 
>    Landlock wants to make use of this by pure chance. So the maintainer offers
>    to take over the patchset. He does.
> 
> End result after more than two kernel releases: The patchset that ends up
> merged into upstream doesn't work with the original motivating use-case because
> all the BPF LSM bits got dropped. So far, no problem. That may happen as long
> as we can the fix it rapidly.
> 
> Then I take the time to send this series of fixes to actually restore the
> original use-case so we don't release a kernel with a new api that doesn't work
> for the main use-case I developed this for.
> 
> Once again, I get told no. This time because the functionality isn't broken the
> right way to justify a merge-window follow-up. Instead, we'll release
> half-working functionality.
> 
> I'm really on the fringes of this endless debacle but this seems to be another
> iteration of "BPF people don't get along with LSM people" therefore they make
> each other's lifes and that of innocent bystanders that just want to get work
> done difficult.
> 
> I really don't understand why it's preferable to release a kernel where the
> original use-case that I was forced to explicitly abandon doesn't work and
> there's no reliable or cost-efficient way for userspace to detect whether the
> hook works correctly.

:(

+1, strong preference to ship the 8 patches as PR given merge win is open as well
right now and to avoid painful probing complexity in user space. Patches look
reasonable to me, and I gave them a spin locally as well as we need these too.
Feel free to also add for bpf side:

Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Tested-by: Daniel Borkmann <daniel@iogearbox.net>

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

* Re: [PATCH 0/8] ns: fixes for the namespace LSM hooks
  2026-10-27  9:24   ` Christian Brauner
  2026-10-27 14:36     ` Daniel Borkmann
@ 2026-10-27 22:37     ` Paul Moore
  2026-10-28 10:42       ` Christian Brauner
  1 sibling, 1 reply; 16+ messages in thread
From: Paul Moore @ 2026-10-27 22:37 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Linus Torvalds, linux-fsdevel, bpf, linux-security-module,
	Alexei Starovoitov, Daniel Borkmann, Mickaël Salaün,
	Günther Noack, KP Singh, Matt Bobrowski, netdev, stable

On Tue, Oct 27, 2026 at 5:24 AM Christian Brauner <brauner@kernel.org> wrote:
> On Mon, Oct 26, 2026 at 04:31:25PM -0400, Paul Moore wrote:
> > From what I'm reading the LSM framework patches don't meet the typical
> > LSM criteria for stable/RC fixes: there is no serious bug/panic, and
> > there is no functional regression from a previous release.
> > This looks like one of those unfortunate circumstances where a new use
> > case wasn't tested thoroughly enough before the merge window, so it
> > can wait for the next one.
>
> My original patchset had a selftest that excercised the BPF LSM usability
> extensively. The hooks are new this cycle and broken for the main use-case I
> wrote the patchset for.

The hooks were merged into the LSM tree on August 31st, and due to
timing I'm guessing they appeared in linux-next the next day,
September 1st.  Considering the merge window opened on October
18th/19th, that's conservatively a month and a half during which
testing could have uncovered this issue and the behavior of the hook
could have been changed to accommodate your use-case.  I know the
Landlock folks were testing the hooks for their usage, but I've gone
back through my inbox and I don't see any reports of BPF LSM failures
using these new hooks in v7.4 until you posted this patchset during
the merge window.

> Curiously, your pull request told Linus "I expect that BPF LSM developers will
> also be making use of the namespace hooks, if they haven't already". So it
> seems very strange to claim this as a new use-case.

The LSM namespace hooks are new to Linux v7.4, they did not exist in
Linus' tree prior to this merge window which is why I consider both
the BPF LSM and Landlock usage of these hooks to be "new".

> >From my perspective the timeline looks like this - I'm sure yours differs:
>
>   I wrote an implementation for the BPF LSM to use a set of new hooks in a
>   subsystem I maintain months ago.
>
>   You rejected the hooks because BPF LSM programs cannot serve as justification
>   to introduce new hooks. Only in-tree LSM (selinux, apparmor, landlock, etc) can.
>
>   I cannot fulfill this requirement because I neither have the expertise nor
>   the use-case for any in-tree LSMs.
>
>   Landlock wants to make use of this by pure chance. So the maintainer offers
>   to take over the patchset. He does.
>
> End result after more than two kernel releases: The patchset that ends up
> merged into upstream doesn't work with the original motivating use-case because
> all the BPF LSM bits got dropped. So far, no problem. That may happen as long
> as we can the fix it rapidly.

As mentioned previously, I'm pretty consistent about what goes up to
Linus once the merge window opens: there is the initial pull-request
with the changes from the last development cycle and then after that
I'm pretty strict about only serious bugs/panics and regressions.  In
this case there is no existing LSM support for namespace controls in
any of the tagged releases from Linus so there are no regressions, and
by all accounts there are no panics related to these new hooks as-is.

Holding this change for Linux v7.5 will give you and others a chance
to fully test the new LSM hooks from a BPF LSM perspective, something
that evidently didn't happen during the Linux v7.4 development cycle.
Disabling the BPF LSM support for the new hooks in Linux v7.4 should
be relatively easy, much simpler than this patchset, and eliminate any
claimed harm.

Also, as the ongoing friction between the LSM and BPF developers was
brought up, I want to point out that SELinux support for the new LSM
namespace hooks was delayed to Linux v7.5 due to issues uncovered with
these new hooks during the Linux v7.4 development cycle (well before
the merge window opened).

> Then I take the time to send this series of fixes to actually restore the
> original use-case so we don't release a kernel with a new api that doesn't work
> for the main use-case I developed this for.
>
> Once again, I get told no. This time because the functionality isn't broken the
> right way to justify a merge-window follow-up. Instead, we'll release
> half-working functionality.

I told you "next merge window", which is far from "no".  Considering
no one tested the BPF LSM support for these new hooks during the v7.4
development cycle, that seems pretty reasonable to me.

> I'm really on the fringes of this endless debacle but this seems to be another
> iteration of "BPF people don't get along with LSM people" therefore they make
> each other's lifes and that of innocent bystanders that just want to get work
> done difficult.

First, please read what I wrote above about SELinux being in a similar
situation.  If a LSM which identified an issue with the hooks well
before the merge window is being pushed to Linux v7.5, I'm not going
to make an exception and rush a functional change for a LSM that
didn't identify an issue until *after* the merge window opened when it
could have been easily identified with testing during the Linux v7.4
dev cycle.

I'm not going to argue that there isn't ongoing friction between the
BPF and LSM devs, and I'll admit to finding it odd that you seem to
keep inserting yourself into that friction point, but my approach here
is the same as what I would have taken with any other LSM (and have in
this particular case).

-- 
paul-moore.com

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

* Re: [PATCH 0/8] ns: fixes for the namespace LSM hooks
  2026-10-27 22:37     ` Paul Moore
@ 2026-10-28 10:42       ` Christian Brauner
  2026-10-28 15:59         ` Paul Moore
  0 siblings, 1 reply; 16+ messages in thread
From: Christian Brauner @ 2026-10-28 10:42 UTC (permalink / raw)
  To: Paul Moore
  Cc: Christian Brauner, Linus Torvalds, linux-fsdevel, bpf,
	linux-security-module, Alexei Starovoitov, Daniel Borkmann,
	Mickaël Salaün, Günther Noack, KP Singh,
	Matt Bobrowski, Stephen Smalley, netdev, stable

On Tue, Oct 27, 2026 at 06:37:43PM -0400, Paul Moore wrote:
> The hooks were merged into the LSM tree on August 31st, and due to
> timing I'm guessing they appeared in linux-next the next day,
> September 1st.  Considering the merge window opened on October
> 18th/19th, that's conservatively a month and a half during which
> testing could have uncovered this issue and the behavior of the hook
> could have been changed to accommodate your use-case.

That's what the rc cycle is for. I'm not asking for changes to behavior
of the hooks to accommodate anything. I'm requesting to make them do
what they were written to do while I was wiring up a consumer.

> I know the
> Landlock folks were testing the hooks for their usage, but I've gone
> back through my inbox and I don't see any reports of BPF LSM failures
> using these new hooks in v7.4 until you posted this patchset during
> the merge window.

Because I sent fixes instead of a bug report. With Fixes: tags, with
reproducers in the commit messages and with tests, before rc1 even
exists. If that's too late to fix a hook that is new this cycle then
nothing can ever be fixed before it's released. Which makes no sense. We
do this all the time.

> The LSM namespace hooks are new to Linux v7.4, they did not exist in
> Linus' tree prior to this merge window which is why I consider both
> the BPF LSM and Landlock usage of these hooks to be "new".

Yes, they're new. That's the argument for fixing them now. Not against
it. No released kernel has these hooks. Once v7.4 is out every one of
these fixes is a behavior change in a released kernel, needs a stable
backport that by your own rules doesn't qualify.

And on top of that nonsense, userspace has to probe for it forever.

> As mentioned previously, I'm pretty consistent about what goes up to
> Linus once the merge window opens: there is the initial pull-request
> with the changes from the last development cycle and then after that
> I'm pretty strict about only serious bugs/panics and regressions.

Your README says "serious problems", not panics. A hook that is
exported to bpf as an attach point and never called is a serious
problem for an API.

And it's not how you've handled this before: lsm_get_self_attr() was new
in v6.8 and got fixed for apparmor and selinux at v6.8-rc7 (cf. [2]).
v7.1-rc5 took Stephen's cred_guard_mutex change for lsm_set_self_attr()
without a crash see (cf. [3]).

> Holding this change for Linux v7.5 will give you and others a chance
> to fully test the new LSM hooks from a BPF LSM perspective, something
> that evidently didn't happen during the Linux v7.4 development cycle.

The series comes with a verifier test and a lifecycle test for exactly
that. Daniel ran it and needs it too. Alexei acked it and asked for the
whole series. There's nothing left that waiting for v7.5 would test.

Nothing in it changes anything for Landlock either.

> Disabling the BPF LSM support for the new hooks in Linux v7.4 should
> be relatively easy, much simpler than this patchset, and eliminate any
> claimed harm.

No. That's a change to kernel/bpf/bpf_lsm.c which you don't maintain
and the people who do have acked the fixes and asked for the whole
series in v7.4.

You're proposing to rip working functionality out of someone else's code
so you don't have to take a three line deletion. namespace_init and
namespace_install work fine for bpf.

It's also not simpler. The LSM part of this series is deleting three
lines I wrote in security.c. The rest is my code and bpf's code with
acks. Your version needs a revert in v7.5 so it's two cross-tree changes
instead of one.

> Also, as the ongoing friction between the LSM and BPF developers was
> brought up, I want to point out that SELinux support for the new LSM
> namespace hooks was delayed to Linux v7.5 due to issues uncovered with
> these new hooks during the Linux v7.4 development cycle (well before
> the merge window opened).

That's not what happened. Stephen posted the fix on September 16, v3 on
September 18. I reviewed it on September 25 and offered to carry it in
the namespace tree for v7.4.

On September 29 you said you'd rather take it through lsm/dev. It's not
in lsm/dev. It's not in v7.4. A reviewed fix that was ready three weeks
before the merge window didn't get applied. That's the same decision
you're making now and it's not a precedent for it.

> I told you "next merge window", which is far from "no".

For v7.4 it is "no". That's the release these hooks go out in. It means
v7.4 ships with a namespace_free hook that never fires and v7.4.y stays
that way because by your own rules the fix doesn't qualify for stable.

> I'll admit to finding it odd that you seem to keep inserting yourself
> into that friction point

A large portion of code where new security functionality is added or
modified all of the time is maintained by me and others. There's
multiple series each cycle that somehow are related to either bpf or the
security layer. What do you expect to happen? That people sit on the
sidelines and write encouraging haikus from time to time while this
friction is grinding any progress to a halt?

Given that I have Acks from both Alexei and Daniel and there's literally
a trivial deletion in kernel/security.c and everything else is in
namespace code, I will send this as part of the first round of vfs fixes
if I have to.

You're objection is noted and I'll mention it to Linus and then he can
decide.

[1]: https://patch.msgid.link/20260220-work-bpf-namespace-v1-0-866207db7b83@kernel.org
[2]: https://git.kernel.org/torvalds/c/cf1182944c7c
[3]: https://git.kernel.org/torvalds/c/4a9b16541ad3

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

* Re: [PATCH 0/8] ns: fixes for the namespace LSM hooks
  2026-10-28 10:42       ` Christian Brauner
@ 2026-10-28 15:59         ` Paul Moore
  0 siblings, 0 replies; 16+ messages in thread
From: Paul Moore @ 2026-10-28 15:59 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Linus Torvalds, linux-fsdevel, bpf, linux-security-module,
	Alexei Starovoitov, Daniel Borkmann, Mickaël Salaün,
	Günther Noack, KP Singh, Matt Bobrowski, Stephen Smalley,
	netdev, stable

On Wed, Oct 28, 2026 at 6:45 AM Christian Brauner <brauner@kernel.org> wrote:
> On Tue, Oct 27, 2026 at 06:37:43PM -0400, Paul Moore wrote:
> > The hooks were merged into the LSM tree on August 31st, and due to
> > timing I'm guessing they appeared in linux-next the next day,
> > September 1st.  Considering the merge window opened on October
> > 18th/19th, that's conservatively a month and a half during which
> > testing could have uncovered this issue and the behavior of the hook
> > could have been changed to accommodate your use-case.
>
> That's what the rc cycle is for.

I believe the understanding is that basic functional testing occurs
before the RC cycle.  Considering the NULL check causing problems for
your use case was present in your first RFC posting back in February,
it's unlikely that you, or anyone in the BPF LSM space, tested this
prior to the merge window.

> I'm not asking for changes to behavior
> of the hooks to accommodate anything.

The LSM changes in this patchset do change the behavior, that's the
point.  The security_namespace_free() hook does not call into the
individual LSM callbacks if no LSM blobs have been requested; what you
are proposing now is that we change the hook so that the callback is
always called, regardless of the LSM blob state.

> > I know the
> > Landlock folks were testing the hooks for their usage, but I've gone
> > back through my inbox and I don't see any reports of BPF LSM failures
> > using these new hooks in v7.4 until you posted this patchset during
> > the merge window.
>
> Because I sent fixes instead of a bug report.

... which you sent *after* the merge window opened.  Once again, I see
no indication that the use case you are concerned about was ever
tested prior to the merge window.

> > The LSM namespace hooks are new to Linux v7.4, they did not exist in
> > Linus' tree prior to this merge window which is why I consider both
> > the BPF LSM and Landlock usage of these hooks to be "new".
>
> Yes, they're new. That's the argument for fixing them now. Not against
> it. No released kernel has these hooks. Once v7.4 is out every one of
> these fixes is a behavior change in a released kernel, needs a stable
> backport that by your own rules doesn't qualify.
>
> And on top of that nonsense, userspace has to probe for it forever.

It's a one line change in kernel/bpf/bpf_lsm.c to add the
security_namespace_free() to the existing list of disabled BPF LSM
callbacks.  No need for a patchset spanning 16 files and over 350
lines of changes; the BPF LSM callback can be enabled in Linux v7.5
after it has been properly tested.

It is probably also worth mentioning again to casual observers that
the LSM hooks, and their associated callbacks, are intentionally not a
stable API.  LSMs are advised to implement abstractions before
exposing the hook/callback API directly to user-visible interfaces
such as configuration policy, policy as code, etc.

> > As mentioned previously, I'm pretty consistent about what goes up to
> > Linus once the merge window opens: there is the initial pull-request
> > with the changes from the last development cycle and then after that
> > I'm pretty strict about only serious bugs/panics and regressions.
>
> Your README says "serious problems", not panics.

Are we really going to argue about how one defines "serious problems"
and "panics"?  You'll notice that I've used "serious problems" and
"panic" somewhat loosely in this email thread because the distinction
doesn't really apply to our discussion.  The issue you've brought up
is that a use case that was never tested prior to the merge window
doesn't work like you want it to work.  There is no kernel panic nor
any functional regression from previous kernel releases (user visible
or otherwise).  We'll change how the security_namespace_free() LSM
hook works for the next release which should give you plenty of time
to test that it works properly.

> A hook that is
> exported to bpf as an attach point and never called is a serious
> problem for an API.

I'll repeat myself again, a little louder for those in the back: the
LSM hooks and callbacks are not a stable API, if an individual LSM
needs a stable API to expose to userspace it needs to provide its own
abstraction layer.

> And it's not how you've handled this before: lsm_get_self_attr() was new
> in v6.8 and got fixed for apparmor and selinux at v6.8-rc7 (cf. [2]).
> v7.1-rc5 took Stephen's cred_guard_mutex change for lsm_set_self_attr()
> without a crash see (cf. [3]).

There is an important difference between those issues and the one
you've identified with this patchset: lsm_get_self_attr() and
lsm_set_self_attr() are syscalls while security_namespace_free() is a
LSM hook.  Linux syscalls are subject to the Linux kernel stable API,
LSM hooks are not.  Linux syscalls are directly accessible from
userspace, LSM hooks are not.  We could dive into other minutiae about
the differences between those issues, but the distinction between
syscalls and LSM hooks is really the only one that should matter here.

> > Disabling the BPF LSM support for the new hooks in Linux v7.4 should
> > be relatively easy, much simpler than this patchset, and eliminate any
> > claimed harm.
>
> No. That's a change to kernel/bpf/bpf_lsm.c which you don't maintain
> and the people who do have acked the fixes and asked for the whole
> series in v7.4.

 :D

Instead you're asking someone else to accept your changes in a
subsystem you don't maintain and when they push back due to lack of
testing and existing conventions you decide the proper approach is to
bully them and force the issue with Linus?  I'm used to dealing with
hypocrisy but this is truly something else ...

> You're proposing to rip working functionality out of someone else's code
> so you don't have to take a three line deletion. namespace_init and
> namespace_install work fine for bpf.
>
> It's also not simpler. The LSM part of this series is deleting three
> lines I wrote in security.c. The rest is my code and bpf's code with
> acks. Your version needs a revert in v7.5 so it's two cross-tree changes
> instead of one.

Adding security_namespace_free() the the disabled BPF LSM hook list
should be a one line change in Linux v7.4 (maybe three if you need a
Kconfig conditional for something) and then a one (maybe three) line
change in Linux v7.5 to allow it again.  Two patches, each at most
three lines, all contained within the BPF subsystem.

> > Also, as the ongoing friction between the LSM and BPF developers was
> > brought up, I want to point out that SELinux support for the new LSM
> > namespace hooks was delayed to Linux v7.5 due to issues uncovered with
> > these new hooks during the Linux v7.4 development cycle (well before
> > the merge window opened).
>
> That's not what happened. Stephen posted the fix on September 16, v3 on
> September 18. I reviewed it on September 25 and offered to carry it in
> the namespace tree for v7.4.
>
> On September 29 you said you'd rather take it through lsm/dev. It's not
> in lsm/dev. It's not in v7.4. A reviewed fix that was ready three weeks
> before the merge window didn't get applied. That's the same decision
> you're making now and it's not a precedent for it.

If you look at that thread, you'll notice I ended my last mail with a
"Are we all okay with that for this patch?" because I wanted to make
sure everyone was okay with the patch, and the process before merging
it.  Stephen answered that he was okay with the approach and you never
responded; I held the patch back exactly because you never explicitly
okay'd the approach.  I suspect one could make a lot of interesting
observations both about how that patch was handled, and how that
handling is now viewed from different perspectives.

> > I told you "next merge window", which is far from "no".
>
> For v7.4 it is "no". That's the release these hooks go out in. It means
> v7.4 ships with a namespace_free hook that never fires and v7.4.y stays
> that way because by your own rules the fix doesn't qualify for stable.
>
> > I'll admit to finding it odd that you seem to keep inserting yourself
> > into that friction point
>
> A large portion of code where new security functionality is added or
> modified all of the time is maintained by me and others.

... and when it suits you, or the BPF developers, you override or
otherwise steal ownership from the LSM space.  We obviously see things
different here, but if you're trying to convince me that you are doing
the Right Thing by inserting yourself into some of these discussions,
e.g. the recent LSM kfunc discussion, you are wasting your time.  To
borrow a phrase from Alexei's last email on that subject, "I don't
feel we will converge here".

> You're objection is noted and I'll mention it to Linus and then he can
> decide.

Please CC the LSM list and me.  I would also encourage you to disclose
the lack of testing you have done prior to the merge window as well as
your insistence that the LSM hooks become a stable API; if Linus is
going to change any of these policies it would be good to know.

--
paul-moore.com

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

end of thread, other threads:[~2026-10-28 15:59 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-26  9:57 [PATCH 0/8] ns: fixes for the namespace LSM hooks Christian Brauner
2026-10-26  9:57 ` [PATCH 1/8] lsm: always call the namespace_free hook Christian Brauner
2026-10-26  9:57 ` [PATCH 2/8] bpf: don't mark namespace_init and namespace_free arguments as trusted Christian Brauner
2026-10-26  9:57 ` [PATCH 3/8] selftests/bpf: add verifier tests for the namespace LSM hooks Christian Brauner
2026-10-26  9:57 ` [PATCH 4/8] landlock: pass the namespace id to log to check_ns_type() Christian Brauner
2026-10-26  9:57 ` [PATCH 5/8] ns: allocate the namespace id before the namespace_init hook Christian Brauner
2026-10-26  9:57 ` [PATCH 6/8] nstree: drop ns_tree_add_raw() Christian Brauner
2026-10-26  9:57 ` [PATCH 7/8] lsm: fix the security_namespace_init() kernel-doc Christian Brauner
2026-10-26  9:57 ` [PATCH 8/8] selftests/bpf: add a lifecycle test for the namespace LSM hooks Christian Brauner
2026-10-26 20:31 ` [PATCH 0/8] ns: fixes " Paul Moore
2026-10-27  9:24   ` Christian Brauner
2026-10-27 14:36     ` Daniel Borkmann
2026-10-27 22:37     ` Paul Moore
2026-10-28 10:42       ` Christian Brauner
2026-10-28 15:59         ` Paul Moore
2026-10-26 23:31 ` Alexei Starovoitov

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