From: "Mickaël Salaün" <mic@digikod.net>
To: Paul Moore <paul@paul-moore.com>
Cc: "Christian Brauner" <brauner@kernel.org>,
"Günther Noack" <gnoack@google.com>,
"Serge E . Hallyn" <serge@hallyn.com>,
"Daniel Durning" <danieldurning.work@gmail.com>,
"Jonathan Corbet" <corbet@lwn.net>,
"Justin Suess" <utilityemal77@gmail.com>,
"Lennart Poettering" <lennart@poettering.net>,
"Mikhail Ivanov" <ivanov.mikhail1@huawei-partners.com>,
"Nicolas Bouchinet" <nicolas.bouchinet@oss.cyber.gouv.fr>,
"Shervin Oloumi" <enlightened@google.com>,
"Tingmao Wang" <m@maowtm.org>,
kernel-team@cloudflare.com, linux-fsdevel@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-security-module@vger.kernel.org
Subject: Re: [PATCH v2 1/9] security: add LSM blob and hooks for namespaces
Date: Thu, 9 Jul 2026 11:12:11 +0200 [thread overview]
Message-ID: <20260709.kaemaiTia6Li@digikod.net> (raw)
In-Reply-To: <3d4fe99cd1665a1be0b36636aeeaf4ab@paul-moore.com>
On Wed, Jul 08, 2026 at 11:22:17PM -0400, Paul Moore wrote:
> On May 27, 2026 =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> wrote:
> >
> > All namespace types now share the same ns_common infrastructure. Extend
> > this to include a security blob so LSMs can start managing namespaces
> > uniformly without having to add one-off hooks or security fields to
> > every individual namespace type.
> >
> > Add a ns_security pointer to ns_common and the corresponding lbs_ns blob
> > size to lsm_blob_sizes. Allocation and freeing hooks are called from the
> > common __ns_common_init() and __ns_common_free() paths so every
> > namespace type gets covered in one go. All information about the
> > namespace type and the appropriate casting helpers to get at the
> > containing namespace are available via ns_common making it
> > straightforward for LSMs to differentiate when they need to.
> >
> > A namespace_install hook is called from validate_ns() during setns(2)
> > giving LSMs a chance to enforce policy on namespace transitions. The
> > LSM check runs before ns->ops->install() so the security module can deny
> > the operation before any type-specific installation effects.
> >
> > Individual namespace types can still have their own specialized security
> > hooks when needed. This is just the common baseline that makes it easy
> > to track and manage namespaces from the security side without requiring
> > every namespace type to reinvent the wheel.
> >
> > Cc: Günther Noack <gnoack@google.com>
> > Cc: Paul Moore <paul@paul-moore.com>
> > Cc: Serge E. Hallyn <serge@hallyn.com>
> > Signed-off-by: Christian Brauner <brauner@kernel.org>
> > Link: https://lore.kernel.org/r/20260216-work-security-namespace-v1-1-075c28758e1f@kernel.org
> > Signed-off-by: Mickaël Salaün <mic@digikod.net>
> > ---
> > Changes since v1:
> > https://lore.kernel.org/r/20260312100444.2609563-2-mic@digikod.net
> > - Move security_namespace_install() before ns->ops->install() in
> > validate_ns() (suggested by Christian Brauner).
> > - Only call proc_free_inum() on security_namespace_alloc() failure
> > when inum was allocated by this function (suggested by Christian
> > Brauner).
> > - Fix anonymous mount namespace blob leak: move
> > security_namespace_free() into __ns_common_free() and make
> > proc_free_inum() conditional on dynamically allocated inums
> > via MNT_NS_INO_SPECIAL_MAX, so free_mnt_ns() can call
> > ns_common_free() unconditionally (suggested by Christian
> > Brauner). Also reported by Daniel Durning while working on
> > SELinux support for these hooks:
> > https://lore.kernel.org/r/20260318201747.4477-1-danieldurning.work@gmail.com
> > - Rename security_namespace_alloc() to security_namespace_init()
> > to match the caller-name convention and reflect that the hook
> > initialises LSM state attached to a constructed ns_common rather
> > than allocating the ns_common itself (suggested by Paul Moore).
> > - Refine the security_namespace_free() kdoc to clarify that
> > RCU-safe blob freeing is required only if an LSM exposes data
> > within the blob to concurrent RCU readers, and document that
> > the blob memory itself is released with kfree() after the
> > namespace_free hooks return (suggested by Paul Moore).
> > - Günther Noack's v1 Reviewed-by is not carried forward to v2:
> > the validate_ns() reordering and the anonymous-mount-namespace
> > blob-leak fix are semantic changes that were not part of his
> > review. Cc'd instead.
> > ---
> > fs/namespace.c | 3 +-
> > include/linux/lsm_hook_defs.h | 3 ++
> > include/linux/lsm_hooks.h | 1 +
> > include/linux/ns/ns_common_types.h | 3 ++
> > include/linux/security.h | 20 ++++++++
> > include/uapi/linux/nsfs.h | 1 +
> > kernel/nscommon.c | 17 ++++++-
> > kernel/nsproxy.c | 6 +++
> > security/lsm_init.c | 2 +
> > security/security.c | 77 ++++++++++++++++++++++++++++++
> > 10 files changed, 130 insertions(+), 3 deletions(-)
> >
> > diff --git a/fs/namespace.c b/fs/namespace.c
> > index fe919abd2f01..031ef3fafa48 100644
> > --- a/fs/namespace.c
> > +++ b/fs/namespace.c
> > @@ -4179,8 +4179,7 @@ static void dec_mnt_namespaces(struct ucounts *ucounts)
> >
> > static void free_mnt_ns(struct mnt_namespace *ns)
> > {
> > - if (!is_anon_ns(ns))
> > - ns_common_free(ns);
> > + ns_common_free(ns);
> > dec_mnt_namespaces(ns->ucounts);
> > mnt_ns_tree_remove(ns);
> > }
>
> ...
>
> > diff --git a/kernel/nscommon.c b/kernel/nscommon.c
> > index 3166c1fd844a..e72426bba29a 100644
> > --- a/kernel/nscommon.c
> > +++ b/kernel/nscommon.c
> > @@ -4,6 +4,7 @@
> > #include <linux/ns_common.h>
> > #include <linux/nstree.h>
> > #include <linux/proc_ns.h>
> > +#include <linux/security.h>
> > #include <linux/user_namespace.h>
> > #include <linux/vfsdebug.h>
> >
> > @@ -59,6 +60,9 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
> >
> > refcount_set(&ns->__ns_ref, 1);
> > ns->stashed = NULL;
> > +#ifdef CONFIG_SECURITY
> > + ns->ns_security = NULL;
> > +#endif
> > ns->ops = ops;
> > ns->ns_id = 0;
> > ns->ns_type = ns_type;
> > @@ -77,6 +81,14 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
> > ret = proc_alloc_inum(&ns->inum);
> > if (ret)
> > return ret;
> > +
> > + ret = security_namespace_init(ns);
> > + if (ret) {
> > + if (!inum)
> > + proc_free_inum(ns->inum);
> > + return ret;
> > + }
> > +
> > /*
> > * Tree ref starts at 0. It's incremented when namespace enters
> > * active use (installed in nsproxy) and decremented when all
> > @@ -91,7 +103,10 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
> >
> > void __ns_common_free(struct ns_common *ns)
> > {
> > - proc_free_inum(ns->inum);
> > + security_namespace_free(ns);
> > +
> > + if (ns->inum > MNT_NS_INO_SPECIAL_MAX)
> > + proc_free_inum(ns->inum);
>
> The ns->inum check in the if-conditional above isn't quite the same as
> the is_anon_ns() check it replaces in free_mnt_ns(). You touch on this
> a bit in the changelog, but that really should be explained in the
> commit description.
>
> ... or honestly, should that change be a separate patch?
I think it's fine, but it's Christian's patch, so I'll let him answer
and propose a new commit description.
>
> > }
> >
> > struct ns_common *__must_check ns_owner(struct ns_common *ns)
> > diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
> > index d9d3d5973bf5..0f1b208d8eef 100644
> > --- a/kernel/nsproxy.c
> > +++ b/kernel/nsproxy.c
> > @@ -385,6 +385,12 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset)
> >
> > static inline int validate_ns(struct nsset *nsset, struct ns_common *ns)
> > {
> > + int ret;
> > +
> > + ret = security_namespace_install(nsset, ns);
> > + if (ret)
> > + return ret;
> > +
> > return ns->ops->install(nsset, ns);
> > }
>
> In the previous revision to the patchset I asked about a
> security_namespace_switch() hook as we don't know if a namespace is
> actually attached to a process until we get to switch_task_namespaces().
> Perhaps that was answered, but I don't recall reading any mail about that
> and I'm not able to uncover any responses on lore.
From the cover letter:
no security_namespace_switch() post-hook is
added in this series: such a hook would only serve LSMs that maintain
per-task state derived from the active namespace set (SELinux-style
state tracking), and no current LSM (including this series) needs that.
Landlock enforces at namespace_install() and namespace_init(), before
the task-to-nsproxy switch. The hook is left for a separate LSM
infrastructure proposal once a concrete user emerges.
This follows the guidance of adding new hooks: there must be at least
one user.
>
> > diff --git a/security/security.c b/security/security.c
> > index 4e999f023651..21cc45d4bbd0 100644
> > --- a/security/security.c
> > +++ b/security/security.c
> > @@ -26,6 +26,7 @@
> > #include <linux/string.h>
> > #include <linux/xattr.h>
> > #include <linux/msg.h>
> > +#include <linux/ns_common.h>
> > #include <linux/overflow.h>
> > #include <linux/perf_event.h>
> > #include <linux/fs.h>
> > @@ -381,6 +382,19 @@ static int lsm_superblock_alloc(struct super_block *sb)
> > GFP_KERNEL);
> > }
> >
> > +/**
> > + * lsm_ns_alloc - allocate a composite namespace blob
> > + * @ns: the namespace that needs a blob
> > + *
> > + * Allocate the namespace blob for all the modules
> > + *
> > + * Returns 0, or -ENOMEM if memory can't be allocated.
> > + */
> > +static int lsm_ns_alloc(struct ns_common *ns)
> > +{
> > + return lsm_blob_alloc(&ns->ns_security, blob_sizes.lbs_ns, GFP_KERNEL);
> > +}
> > +
> > /**
> > * lsm_fill_user_ctx - Fill a user space lsm_ctx structure
> > * @uctx: a userspace LSM context to be filled
> > @@ -3358,6 +3372,69 @@ int security_create_user_ns(const struct cred *cred)
> > return call_int_hook(userns_create, 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).
> > + *
> > + * Return: Returns 0 if successful, otherwise < 0 error code.
> > + */
> > +int security_namespace_init(struct ns_common *ns)
> > +{
> > + int rc;
> > +
> > + rc = lsm_ns_alloc(ns);
> > + if (unlikely(rc))
> > + return rc;
> > +
> > + rc = call_int_hook(namespace_init, ns);
> > + if (unlikely(rc))
> > + security_namespace_free(ns);
> > +
> > + return rc;
> > +}
> > +
> > +/**
> > + * security_namespace_free() - Release LSM security data from a namespace
> > + * @ns: the namespace being freed
> > + *
> > + * Release security data attached to the namespace. Called before the
> > + * namespace structure is freed.
> > + *
> > + * Note: If an LSM exposes data within the security blob to concurrent
> > + * RCU readers, it must use RCU-safe freeing for that data. The blob
> > + * memory itself is released with kfree() after the namespace_free
> > + * hooks return.
>
> I appreciate that you revised the "Note:" based on my previous comments,
> but now it reads just like normal guidance on RCU and object lifetimes.
> Do we need this "Note:" at all?
Indeed, it's normal guidance, I'll remove it.
>
> > + */
> > +void security_namespace_free(struct ns_common *ns)
> > +{
> > + if (!ns->ns_security)
> > + return;
> > +
> > + call_void_hook(namespace_free, ns);
> > +
> > + kfree(ns->ns_security);
> > + ns->ns_security = NULL;
> > +}
> > +
> > +/**
> > + * security_namespace_install() - Check permission to install a namespace
> > + * @nsset: the target nsset being configured
> > + * @ns: the namespace being installed
> > + *
> > + * Check permission before allowing a namespace to be installed into the
> > + * process's set of namespaces via setns(2).
> > + *
> > + * Return: Returns 0 if permission is granted, otherwise < 0 error code.
> > + */
> > +int security_namespace_install(const struct nsset *nsset, struct ns_common *ns)
> > +{
> > + return call_int_hook(namespace_install, nsset, ns);
> > +}
> > +
> > /**
> > * security_ipc_permission() - Check if sysv ipc access is allowed
> > * @ipcp: ipc permission structure
> > --
> > 2.54.0
>
> --
> paul-moore.com
>
next prev parent reply other threads:[~2026-07-09 9:12 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-27 18:11 [PATCH v2 0/9] Landlock: Namespace and capability control Mickaël Salaün
2026-05-27 18:11 ` [PATCH v2 1/9] security: add LSM blob and hooks for namespaces Mickaël Salaün
2026-06-05 15:06 ` Mickaël Salaün
2026-06-05 18:08 ` Paul Moore
2026-07-09 3:22 ` Paul Moore
2026-07-09 9:12 ` Mickaël Salaün [this message]
2026-07-09 13:03 ` Paul Moore
2026-07-09 15:58 ` Mickaël Salaün
2026-07-10 6:55 ` Christian Brauner
2026-07-10 20:42 ` Paul Moore
2026-05-27 18:11 ` [PATCH v2 2/9] security: Add LSM_AUDIT_DATA_NS for namespace audit records Mickaël Salaün
2026-05-27 18:11 ` [PATCH v2 3/9] landlock: Wrap per-layer access masks in struct layer_config Mickaël Salaün
2026-05-27 18:11 ` [PATCH v2 4/9] landlock: Enforce namespace use restrictions Mickaël Salaün
2026-05-27 18:11 ` [PATCH v2 5/9] landlock: Enforce capability restrictions Mickaël Salaün
2026-05-27 18:11 ` [PATCH v2 6/9] selftests/landlock: Add namespace restriction tests Mickaël Salaün
2026-05-27 18:11 ` [PATCH v2 7/9] selftests/landlock: Add capability " Mickaël Salaün
2026-05-27 18:11 ` [PATCH v2 8/9] samples/landlock: Add capability and namespace restriction support Mickaël Salaün
2026-05-27 18:11 ` [PATCH v2 9/9] landlock: Add documentation for capability and namespace restrictions Mickaël Salaün
2026-06-01 9:37 ` Günther Noack
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260709.kaemaiTia6Li@digikod.net \
--to=mic@digikod.net \
--cc=brauner@kernel.org \
--cc=corbet@lwn.net \
--cc=danieldurning.work@gmail.com \
--cc=enlightened@google.com \
--cc=gnoack@google.com \
--cc=ivanov.mikhail1@huawei-partners.com \
--cc=kernel-team@cloudflare.com \
--cc=lennart@poettering.net \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=m@maowtm.org \
--cc=nicolas.bouchinet@oss.cyber.gouv.fr \
--cc=paul@paul-moore.com \
--cc=serge@hallyn.com \
--cc=utilityemal77@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox