From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp-8fad.mail.infomaniak.ch (smtp-8fad.mail.infomaniak.ch [83.166.143.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 76E2F3FF1C0 for ; Thu, 9 Jul 2026 09:12:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=83.166.143.173 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783588351; cv=none; b=r5Ge3StfRLYlFt7MK7TUC+cxL0O1iP0nbHdjZDhWrQxMSB8nVIGUmcIBhzcVlCKPR36D4yLjtscXI/jc0XR0Hf9WY+OpGKOleZOiluTt8c+RUrEifc8mBx5GziAKAso4lx+L+GZ33nctfKnIij7Q88GU1nOeJ8aufbZXxDIH9Qs= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783588351; c=relaxed/simple; bh=odxmvaaTBTHa/N/VxCEKfs89fFIzWDl9u1LmixE69ys=; h=Date:From:To:Cc:Subject:Message-ID:MIME-Version:Content-Type: Content-Disposition:In-Reply-To; b=tDThRiQyOP+FgGfjwCImZ9vk3tK5ypcy3/Nmp6bTOsq0Ri/POs0MISdVpS1xsvYY9TY7SQcEpJpzSQFvH77XSSxwXhmAYXHA69RIDX7WOUgKXXDX1evahG+ZVNB1s2DZHeoKaQIMXPTrpOzSQ7MAO165Xo7rKl2rCvQYpGmmQaM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=digikod.net; spf=pass smtp.mailfrom=digikod.net; dkim=pass (1024-bit key) header.d=digikod.net header.i=@digikod.net header.b=1Yy7t0Xn; arc=none smtp.client-ip=83.166.143.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=digikod.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=digikod.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=digikod.net header.i=@digikod.net header.b="1Yy7t0Xn" Received: from smtp-3-0000.mail.infomaniak.ch (smtp-3-0000.mail.infomaniak.ch [10.4.36.107]) by smtp-4-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4gwq3j0HzmzHWf; Thu, 9 Jul 2026 11:12:17 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=digikod.net; s=20191114; t=1783588336; bh=bpRixxfeLPO8/IIBrQSTv4rgan2tSVU3TO6cS36uoEg=; h=Date:From:To:Cc:Subject:In-Reply-To:From; b=1Yy7t0Xn/+yJPMrqTDIL8ItYpu4g/Fz6bA8/s00HP7+kTPy6foeWk11fl9JiGgyF5 BhfsvSG6sbJQga0rfcWiDd1vFVU0mq6VTvA/eenAbOsi61S0brw+fhuP17VlB9U1t7 manEyV9Fca7ACI9JBHnXiPfFtmIzlT6lxJT+88l0= Received: from unknown by smtp-3-0000.mail.infomaniak.ch (Postfix) with ESMTPA id 4gwq3g1q46zly2; Thu, 9 Jul 2026 11:12:15 +0200 (CEST) Date: Thu, 9 Jul 2026 11:12:11 +0200 From: =?utf-8?Q?Micka=C3=ABl_Sala=C3=BCn?= To: Paul Moore Cc: Christian Brauner , =?utf-8?Q?G=C3=BCnther?= Noack , "Serge E . Hallyn" , Daniel Durning , Jonathan Corbet , Justin Suess , Lennart Poettering , Mikhail Ivanov , Nicolas Bouchinet , Shervin Oloumi , Tingmao Wang , 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 Message-ID: <20260709.kaemaiTia6Li@digikod.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <3d4fe99cd1665a1be0b36636aeeaf4ab@paul-moore.com> X-Infomaniak-Routing: alpha 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?= 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 > > Cc: Paul Moore > > Cc: Serge E. Hallyn > > Signed-off-by: Christian Brauner > > Link: https://lore.kernel.org/r/20260216-work-security-namespace-v1-1-075c28758e1f@kernel.org > > Signed-off-by: Mickaël Salaün > > --- > > 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 > > #include > > #include > > +#include > > #include > > #include > > > > @@ -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 > > #include > > #include > > +#include > > #include > > #include > > #include > > @@ -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 >