Linux Security Modules development
 help / color / mirror / Atom feed
* Re: [RFC PATCH 4/4] capability: unexport has_capability_noaudit
From: Christoph Hellwig @ 2026-06-29 12:31 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: cem, linux-fsdevel, jack, hch, serge, linux-security-module,
	linux-kernel, linux-xfs
In-Reply-To: <20260626152008.GW6078@frogsfrogsfrogs>

On Fri, Jun 26, 2026 at 08:20:08AM -0700, Darrick J. Wong wrote:
> Please update the kerneldoc for this function to mention that it only
> checks real capability, not effective capability.  I'd like to prevent
> someone else from making the same mistakes I did with these functions
> that sound the same in documentation but have very different behaviors.

Yes, that would be useful.  But make it a separate patch please, it's
not related to the unexporting in any way.


^ permalink raw reply

* Re: [RFC PATCH 1/4] capabily: Add new capable_noaudit
From: Serge E. Hallyn @ 2026-06-29 13:49 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: cem, linux-fsdevel, jack, djwong, serge, linux-security-module,
	linux-kernel, linux-xfs
In-Reply-To: <20260629122939.GA21958@lst.de>

On Mon, Jun 29, 2026 at 02:29:39PM +0200, Christoph Hellwig wrote:
> On Fri, Jun 26, 2026 at 01:45:20PM +0200, cem@kernel.org wrote:
> > +extern bool capable_noaudit(int cap);
> 
> No need for the extern.
> 
> Otherwise this does look nice an clean to me:
> 
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> 
> But if the security folks don't like we can live with the more
> verbose version of it I guess.

Honestly I'm ok either way.  If people misunderstand the shortcut,
and ove-ruse it, that's safer than the other way.  The one that
scare me more is ns_capable(&current_user_ns, X).  I need to do an
audit of the current users of that.

So I'm happy to put

Reviewed-by: Serge Hallyn <serge@hallyn.com>

on the set.

^ permalink raw reply

* Re: [PATCH v6] rust: aref: make `AlwaysRefCounted::inc_ref` an associated function
From: Gary Guo @ 2026-06-29 14:37 UTC (permalink / raw)
  To: Trevor Chan, gregkh, rafael, dakr, ojeda, a.hindborg, paul,
	aliceryhl, airlied, simona, viro, brauner, igor.korotin, vireshk,
	nm, sboyd, m.wilczynski, boqun, gary, axboe, daniel.almeida,
	shankari.ak0208, lyude, j, lossin, acourbot, markus.probst,
	driver-core, rust-for-linux, linux-kernel, linux-block,
	linux-security-module, dri-devel, linux-fsdevel, linux-mm,
	linux-pm, linux-pci, linux-pwm
  Cc: david.m.ertman, iweiny, leon, bjorn3_gh, tmgross, tamird, work,
	sergeh, matthew.brost, thomas.hellstrom, jack, ljs, liam,
	bhelgaas, kwilczynski, ptikhomirov
In-Reply-To: <20260628095132.47753-1-trev@trevrosa.dev>

On Sun Jun 28, 2026 at 10:51 AM BST, Trevor Chan wrote:
> `AlwaysRefCounted::inc_ref` is a function that shouldn't be called lightly.
> 
> To prevent accidentally calling it, change `inc_ref` to be an associated function.
> 
> Modify all `AlwaysRefCounted` implementors to work with this change.
> 
> Suggested-by: Benno Lossin <lossin@kernel.org>
> Link: https://github.com/Rust-for-Linux/linux/issues/1177
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Trevor Chan <trev@trevrosa.dev>

Reviewed-by: Gary Guo <gary@garyguo.net>

> ---
> Changes in v2:
>  - Don't word wrap the patch
> Changes in v3:
>  - Make argument name of `Empty::inc_ref` consistent with `Empty::dec_ref`
> Changes in v4:
>  - Rebase to new rust-next, change new implementors
>  - Reword explanation for change in `AlwaysRefCounted::inc_ref` doc comment
> Changes in v5:
>  - Change commit message to be imperative
> Changes in v6:
>  - Change all the implementors
> ---
>  rust/kernel/auxiliary.rs        |  4 ++--
>  rust/kernel/block/mq/request.rs |  4 ++--
>  rust/kernel/cred.rs             |  4 ++--
>  rust/kernel/device.rs           |  4 ++--
>  rust/kernel/device/property.rs  |  4 ++--
>  rust/kernel/drm/device.rs       |  4 ++--
>  rust/kernel/drm/gem/mod.rs      |  4 ++--
>  rust/kernel/drm/gpuvm/mod.rs    |  4 ++--
>  rust/kernel/drm/gpuvm/vm_bo.rs  |  4 ++--
>  rust/kernel/fs/file.rs          |  8 ++++----
>  rust/kernel/i2c.rs              |  8 ++++----
>  rust/kernel/mm.rs               |  8 ++++----
>  rust/kernel/mm/mmput_async.rs   |  4 ++--
>  rust/kernel/opp.rs              |  4 ++--
>  rust/kernel/pci.rs              |  4 ++--
>  rust/kernel/pid_namespace.rs    |  4 ++--
>  rust/kernel/platform.rs         |  4 ++--
>  rust/kernel/pwm.rs              |  2 +-
>  rust/kernel/sync/aref.rs        | 11 +++++++----
>  rust/kernel/task.rs             |  4 ++--
>  rust/kernel/usb.rs              |  8 ++++----
>  21 files changed, 54 insertions(+), 51 deletions(-)


^ permalink raw reply

* Re: [PATCH stable/linux-5.10.y 0/7] Backport Fix incorrect overlayfs mmap() and mprotect() LSM access controls
From: Amir Goldstein @ 2026-06-29 17:31 UTC (permalink / raw)
  To: Cai Xinchen
  Cc: viro, brauner, jack, miklos, paul, jmorris, serge,
	stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
	linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
	selinux, bpf, stable, lujialin4
In-Reply-To: <20260629070653.580879-1-caixinchen1@huawei.com>

On Mon, Jun 29, 2026 at 8:38 AM Cai Xinchen <caixinchen1@huawei.com> wrote:
>
> ackport the patch series
> "Fix incorrect overlayfs mmap() and mprotect() LSM access controls" [1]
> to 5.10 lts

Chai,

First of all, I don't think that stable maintainers are picking backports
to 5.10 that were not backported to 6.1 and 5.15.

Second, backporting backing_file as a dependency to LTS kernels is a pretty
intrusive change, so your description above is very much lacking.

Please do not backport backing_file to any of the LTS kernels without providing
detailed explanation to try and convince the vfs maintainers that you
verified this
bacport is safe for the LTS kernel, because honestly, this looks a bit
risky for me.

Thanks,
Amir.

^ permalink raw reply

* [PATCH] lsm: cleanup repeated lsm_blob_size_update() calls in lsm_prepare()
From: Matt Bobrowski @ 2026-06-29 21:01 UTC (permalink / raw)
  To: linux-security-module
  Cc: Paul Moore, James Morris, Serge E . Hallyn, Matt Bobrowski

Centralize the definition of LSM security blob fields using an X-macro
(LSM_BLOBS_LIST). This reduces repetitive boilerplate code across
struct lsm_blob_sizes, blob size registration in lsm_prepare(), and
debug log printing in security_init().

Signed-off-by: Matt Bobrowski <mattbobrowski@google.com>
---
 include/linux/lsm_hooks.h | 42 ++++++++++++++++------------
 security/lsm_init.c       | 59 ++++++++++-----------------------------
 2 files changed, 38 insertions(+), 63 deletions(-)

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index b4f8cad53ddb..0e73b22bdeea 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -98,28 +98,34 @@ struct security_hook_list {
 	const struct lsm_id *lsmid;
 } __randomize_layout;
 
+#define LSM_BLOBS_LIST(X) \
+	X(cred)          \
+	X(file)          \
+	X(backing_file)  \
+	X(ib)            \
+	X(inode)         \
+	X(sock)          \
+	X(superblock)    \
+	X(ipc)           \
+	X(key)           \
+	X(msg_msg)       \
+	X(perf_event)    \
+	X(task)          \
+	X(tun_dev)       \
+	X(xattr_count)   \
+	X(bdev)          \
+	X(bpf_map)       \
+	X(bpf_prog)      \
+	X(bpf_token)
+
 /*
  * Security blob size or offset data.
+ * Note: lbs_xattr_count is the number of xattr slots in new_xattrs array.
  */
 struct lsm_blob_sizes {
-	unsigned int lbs_cred;
-	unsigned int lbs_file;
-	unsigned int lbs_backing_file;
-	unsigned int lbs_ib;
-	unsigned int lbs_inode;
-	unsigned int lbs_sock;
-	unsigned int lbs_superblock;
-	unsigned int lbs_ipc;
-	unsigned int lbs_key;
-	unsigned int lbs_msg_msg;
-	unsigned int lbs_perf_event;
-	unsigned int lbs_task;
-	unsigned int lbs_xattr_count; /* num xattr slots in new_xattrs array */
-	unsigned int lbs_tun_dev;
-	unsigned int lbs_bdev;
-	unsigned int lbs_bpf_map;
-	unsigned int lbs_bpf_prog;
-	unsigned int lbs_bpf_token;
+#define LSM_BLOB_SIZE(name) unsigned int lbs_##name;
+	LSM_BLOBS_LIST(LSM_BLOB_SIZE);
+#undef LSM_BLOB_SIZE
 };
 
 /*
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 7c0fd17f1601..c256f1c33efa 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -282,40 +282,24 @@ static void __init lsm_blob_size_update(unsigned int *sz_req,
  * lsm_prepare - Prepare the LSM framework for a new LSM
  * @lsm: LSM definition
  */
-static void __init lsm_prepare(struct lsm_info *lsm)
+static void __init lsm_prepare(const struct lsm_info *lsm)
 {
 	struct lsm_blob_sizes *blobs = lsm->blobs;
 
 	if (!blobs)
 		return;
 
-	/* Register the LSM blob sizes. */
-	blobs = lsm->blobs;
-	lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred);
-	lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file);
-	lsm_blob_size_update(&blobs->lbs_backing_file,
-			     &blob_sizes.lbs_backing_file);
-	lsm_blob_size_update(&blobs->lbs_ib, &blob_sizes.lbs_ib);
-	/* inode blob gets an rcu_head in addition to LSM blobs. */
+	/* The inode blob (inode->i_security) gets an rcu_head in addition to
+	 * LSM blobs.
+	 */
 	if (blobs->lbs_inode && blob_sizes.lbs_inode == 0)
 		blob_sizes.lbs_inode = sizeof(struct rcu_head);
-	lsm_blob_size_update(&blobs->lbs_inode, &blob_sizes.lbs_inode);
-	lsm_blob_size_update(&blobs->lbs_ipc, &blob_sizes.lbs_ipc);
-	lsm_blob_size_update(&blobs->lbs_key, &blob_sizes.lbs_key);
-	lsm_blob_size_update(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
-	lsm_blob_size_update(&blobs->lbs_perf_event,
-			     &blob_sizes.lbs_perf_event);
-	lsm_blob_size_update(&blobs->lbs_sock, &blob_sizes.lbs_sock);
-	lsm_blob_size_update(&blobs->lbs_superblock,
-			     &blob_sizes.lbs_superblock);
-	lsm_blob_size_update(&blobs->lbs_task, &blob_sizes.lbs_task);
-	lsm_blob_size_update(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
-	lsm_blob_size_update(&blobs->lbs_xattr_count,
-			     &blob_sizes.lbs_xattr_count);
-	lsm_blob_size_update(&blobs->lbs_bdev, &blob_sizes.lbs_bdev);
-	lsm_blob_size_update(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
-	lsm_blob_size_update(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
-	lsm_blob_size_update(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
+
+	/* Register the LSM blob sizes. */
+#define UPDATE_LSM_BLOB_SIZE(name) \
+	lsm_blob_size_update(&blobs->lbs_##name, &blob_sizes.lbs_##name);
+	LSM_BLOBS_LIST(UPDATE_LSM_BLOB_SIZE);
+#undef UPDATE_LSM_BLOB_SIZE
 }
 
 /**
@@ -441,25 +425,10 @@ int __init security_init(void)
 		lsm_prepare(*lsm);
 
 	if (lsm_debug) {
-		lsm_pr("blob(cred) size %d\n", blob_sizes.lbs_cred);
-		lsm_pr("blob(file) size %d\n", blob_sizes.lbs_file);
-		lsm_pr("blob(backing_file) size %d\n",
-		       blob_sizes.lbs_backing_file);
-		lsm_pr("blob(ib) size %d\n", blob_sizes.lbs_ib);
-		lsm_pr("blob(inode) size %d\n", blob_sizes.lbs_inode);
-		lsm_pr("blob(ipc) size %d\n", blob_sizes.lbs_ipc);
-		lsm_pr("blob(key) size %d\n", blob_sizes.lbs_key);
-		lsm_pr("blob(msg_msg)_size %d\n", blob_sizes.lbs_msg_msg);
-		lsm_pr("blob(sock) size %d\n", blob_sizes.lbs_sock);
-		lsm_pr("blob(superblock) size %d\n", blob_sizes.lbs_superblock);
-		lsm_pr("blob(perf_event) size %d\n", blob_sizes.lbs_perf_event);
-		lsm_pr("blob(task) size %d\n", blob_sizes.lbs_task);
-		lsm_pr("blob(tun_dev) size %d\n", blob_sizes.lbs_tun_dev);
-		lsm_pr("blob(xattr) count %d\n", blob_sizes.lbs_xattr_count);
-		lsm_pr("blob(bdev) size %d\n", blob_sizes.lbs_bdev);
-		lsm_pr("blob(bpf_map) size %d\n", blob_sizes.lbs_bpf_map);
-		lsm_pr("blob(bpf_prog) size %d\n", blob_sizes.lbs_bpf_prog);
-		lsm_pr("blob(bpf_token) size %d\n", blob_sizes.lbs_bpf_token);
+#define PRINT_LSM_BLOB_SIZE(name) \
+	lsm_pr("blob(" #name ") size %d\n", blob_sizes.lbs_##name);
+		LSM_BLOBS_LIST(PRINT_LSM_BLOB_SIZE);
+#undef PRINT_LSM_BLOB_SIZE
 	}
 
 	if (blob_sizes.lbs_file)
-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related

* Re: [PATCH stable/linux-5.10.y 0/7] Backport Fix incorrect overlayfs mmap() and mprotect() LSM access controls
From: Cai Xinchen @ 2026-06-30  3:06 UTC (permalink / raw)
  To: Amir Goldstein
  Cc: viro, brauner, jack, miklos, paul, jmorris, serge,
	stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
	linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
	selinux, bpf, stable, lujialin4
In-Reply-To: <CAOQ4uxjcD0-PHqqmrpEvkLRgtKJGe8-n+6DQyBngjN2TorwU+g@mail.gmail.com>

Thank you for your reply. Regarding the two points of feedback:

First, 6.1 is still in the process of being adapted.

Second, this patch set is primarily intended to fix CVE-2026-46054, but 
it seems that for lower versions to implement SELinux checks for overlay 
mmap/mprotect checks, some dependencies are unavoidable. In such cases, 
should we add more tests to reduce the risk and integrate the changes, 
or should we simply not fix this issue? If more tests are needed, are 
there any recommended test suites?

On 6/30/2026 1:31 AM, Amir Goldstein wrote:
> On Mon, Jun 29, 2026 at 8:38 AM Cai Xinchen <caixinchen1@huawei.com> wrote:
>> ackport the patch series
>> "Fix incorrect overlayfs mmap() and mprotect() LSM access controls" [1]
>> to 5.10 lts
> Chai,
>
> First of all, I don't think that stable maintainers are picking backports
> to 5.10 that were not backported to 6.1 and 5.15.
>
> Second, backporting backing_file as a dependency to LTS kernels is a pretty
> intrusive change, so your description above is very much lacking.
>
> Please do not backport backing_file to any of the LTS kernels without providing
> detailed explanation to try and convince the vfs maintainers that you
> verified this
> bacport is safe for the LTS kernel, because honestly, this looks a bit
> risky for me.
>
> Thanks,
> Amir.

^ permalink raw reply

* Re: [PATCH stable/linux-5.10.y 0/7] Backport Fix incorrect overlayfs mmap() and mprotect() LSM access controls
From: Amir Goldstein @ 2026-06-30 11:01 UTC (permalink / raw)
  To: Cai Xinchen
  Cc: viro, brauner, jack, miklos, paul, jmorris, serge,
	stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
	linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
	selinux, bpf, stable, lujialin4
In-Reply-To: <f4c8f5fe-30c3-4e7f-8512-7a2befdd1ed3@huawei.com>

On Tue, Jun 30, 2026 at 5:06 AM Cai Xinchen <caixinchen1@huawei.com> wrote:
>
> Thank you for your reply. Regarding the two points of feedback:
>
> First, 6.1 is still in the process of being adapted.

So do not propose for 5.10 please.

>
> Second, this patch set is primarily intended to fix CVE-2026-46054, but
> it seems that for lower versions to implement SELinux checks for overlay
> mmap/mprotect checks, some dependencies are unavoidable. In such cases,
> should we add more tests to reduce the risk and integrate the changes,
> or should we simply not fix this issue? If more tests are needed, are
> there any recommended test suites?

I have concerns.
The burdn of proof is on you.

Thanks,
Amir.

^ permalink raw reply

* Re: [PATCH] lsm: cleanup repeated lsm_blob_size_update() calls in lsm_prepare()
From: Casey Schaufler @ 2026-06-30 16:26 UTC (permalink / raw)
  To: Matt Bobrowski, linux-security-module
  Cc: Paul Moore, James Morris, Serge E . Hallyn, Casey Schaufler
In-Reply-To: <20260629210150.832576-1-mattbobrowski@google.com>

On 6/29/2026 2:01 PM, Matt Bobrowski wrote:
> Centralize the definition of LSM security blob fields using an X-macro
> (LSM_BLOBS_LIST). This reduces repetitive boilerplate code across
> struct lsm_blob_sizes, blob size registration in lsm_prepare(), and
> debug log printing in security_init().
>
> Signed-off-by: Matt Bobrowski <mattbobrowski@google.com>
> ---
>  include/linux/lsm_hooks.h | 42 ++++++++++++++++------------
>  security/lsm_init.c       | 59 ++++++++++-----------------------------
>  2 files changed, 38 insertions(+), 63 deletions(-)
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index b4f8cad53ddb..0e73b22bdeea 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -98,28 +98,34 @@ struct security_hook_list {
>  	const struct lsm_id *lsmid;
>  } __randomize_layout;
>  
> +#define LSM_BLOBS_LIST(X) \
> +	X(cred)          \
> +	X(file)          \
> +	X(backing_file)  \
> +	X(ib)            \
> +	X(inode)         \
> +	X(sock)          \
> +	X(superblock)    \
> +	X(ipc)           \
> +	X(key)           \
> +	X(msg_msg)       \
> +	X(perf_event)    \
> +	X(task)          \
> +	X(tun_dev)       \
> +	X(xattr_count)   \
> +	X(bdev)          \
> +	X(bpf_map)       \
> +	X(bpf_prog)      \
> +	X(bpf_token)
> +
>  /*
>   * Security blob size or offset data.
> + * Note: lbs_xattr_count is the number of xattr slots in new_xattrs array.
>   */
>  struct lsm_blob_sizes {
> -	unsigned int lbs_cred;
> -	unsigned int lbs_file;
> -	unsigned int lbs_backing_file;
> -	unsigned int lbs_ib;
> -	unsigned int lbs_inode;
> -	unsigned int lbs_sock;
> -	unsigned int lbs_superblock;
> -	unsigned int lbs_ipc;
> -	unsigned int lbs_key;
> -	unsigned int lbs_msg_msg;
> -	unsigned int lbs_perf_event;
> -	unsigned int lbs_task;
> -	unsigned int lbs_xattr_count; /* num xattr slots in new_xattrs array */
> -	unsigned int lbs_tun_dev;
> -	unsigned int lbs_bdev;
> -	unsigned int lbs_bpf_map;
> -	unsigned int lbs_bpf_prog;
> -	unsigned int lbs_bpf_token;
> +#define LSM_BLOB_SIZE(name) unsigned int lbs_##name;
> +	LSM_BLOBS_LIST(LSM_BLOB_SIZE);
> +#undef LSM_BLOB_SIZE
>  };
>  
>  /*
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> index 7c0fd17f1601..c256f1c33efa 100644
> --- a/security/lsm_init.c
> +++ b/security/lsm_init.c
> @@ -282,40 +282,24 @@ static void __init lsm_blob_size_update(unsigned int *sz_req,
>   * lsm_prepare - Prepare the LSM framework for a new LSM
>   * @lsm: LSM definition
>   */
> -static void __init lsm_prepare(struct lsm_info *lsm)
> +static void __init lsm_prepare(const struct lsm_info *lsm)

This is an unrelated change which should be in a separate patch.

>  {
>  	struct lsm_blob_sizes *blobs = lsm->blobs;
>  
>  	if (!blobs)
>  		return;
>  
> -	/* Register the LSM blob sizes. */
> -	blobs = lsm->blobs;
> -	lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred);
> -	lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file);
> -	lsm_blob_size_update(&blobs->lbs_backing_file,
> -			     &blob_sizes.lbs_backing_file);
> -	lsm_blob_size_update(&blobs->lbs_ib, &blob_sizes.lbs_ib);
> -	/* inode blob gets an rcu_head in addition to LSM blobs. */
> +	/* The inode blob (inode->i_security) gets an rcu_head in addition to
> +	 * LSM blobs.
> +	 */

Please retain the comment style of the file. Either
	/* ... */
	/*
	 * ...
	 */
but not
	/* ...
	 */

>  	if (blobs->lbs_inode && blob_sizes.lbs_inode == 0)
>  		blob_sizes.lbs_inode = sizeof(struct rcu_head);

This is where I dislike the macro approach. While this particular
exception is easily handled, others may not be. A blob whose size
isn't represented as an unsigned int (e.g a bool) would not use
lsm_blob_size_update(). While all of the existing blob sizes are
unsigned ints, I have considered bools on more than one occasion.

> -	lsm_blob_size_update(&blobs->lbs_inode, &blob_sizes.lbs_inode);
> -	lsm_blob_size_update(&blobs->lbs_ipc, &blob_sizes.lbs_ipc);
> -	lsm_blob_size_update(&blobs->lbs_key, &blob_sizes.lbs_key);
> -	lsm_blob_size_update(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> -	lsm_blob_size_update(&blobs->lbs_perf_event,
> -			     &blob_sizes.lbs_perf_event);
> -	lsm_blob_size_update(&blobs->lbs_sock, &blob_sizes.lbs_sock);
> -	lsm_blob_size_update(&blobs->lbs_superblock,
> -			     &blob_sizes.lbs_superblock);
> -	lsm_blob_size_update(&blobs->lbs_task, &blob_sizes.lbs_task);
> -	lsm_blob_size_update(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> -	lsm_blob_size_update(&blobs->lbs_xattr_count,
> -			     &blob_sizes.lbs_xattr_count);
> -	lsm_blob_size_update(&blobs->lbs_bdev, &blob_sizes.lbs_bdev);
> -	lsm_blob_size_update(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
> -	lsm_blob_size_update(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
> -	lsm_blob_size_update(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
> +
> +	/* Register the LSM blob sizes. */
> +#define UPDATE_LSM_BLOB_SIZE(name) \
> +	lsm_blob_size_update(&blobs->lbs_##name, &blob_sizes.lbs_##name);
> +	LSM_BLOBS_LIST(UPDATE_LSM_BLOB_SIZE);
> +#undef UPDATE_LSM_BLOB_SIZE
>  }
>  
>  /**
> @@ -441,25 +425,10 @@ int __init security_init(void)
>  		lsm_prepare(*lsm);
>  
>  	if (lsm_debug) {
> -		lsm_pr("blob(cred) size %d\n", blob_sizes.lbs_cred);
> -		lsm_pr("blob(file) size %d\n", blob_sizes.lbs_file);
> -		lsm_pr("blob(backing_file) size %d\n",
> -		       blob_sizes.lbs_backing_file);
> -		lsm_pr("blob(ib) size %d\n", blob_sizes.lbs_ib);
> -		lsm_pr("blob(inode) size %d\n", blob_sizes.lbs_inode);
> -		lsm_pr("blob(ipc) size %d\n", blob_sizes.lbs_ipc);
> -		lsm_pr("blob(key) size %d\n", blob_sizes.lbs_key);
> -		lsm_pr("blob(msg_msg)_size %d\n", blob_sizes.lbs_msg_msg);
> -		lsm_pr("blob(sock) size %d\n", blob_sizes.lbs_sock);
> -		lsm_pr("blob(superblock) size %d\n", blob_sizes.lbs_superblock);
> -		lsm_pr("blob(perf_event) size %d\n", blob_sizes.lbs_perf_event);
> -		lsm_pr("blob(task) size %d\n", blob_sizes.lbs_task);
> -		lsm_pr("blob(tun_dev) size %d\n", blob_sizes.lbs_tun_dev);
> -		lsm_pr("blob(xattr) count %d\n", blob_sizes.lbs_xattr_count);
> -		lsm_pr("blob(bdev) size %d\n", blob_sizes.lbs_bdev);
> -		lsm_pr("blob(bpf_map) size %d\n", blob_sizes.lbs_bpf_map);
> -		lsm_pr("blob(bpf_prog) size %d\n", blob_sizes.lbs_bpf_prog);
> -		lsm_pr("blob(bpf_token) size %d\n", blob_sizes.lbs_bpf_token);
> +#define PRINT_LSM_BLOB_SIZE(name) \
> +	lsm_pr("blob(" #name ") size %d\n", blob_sizes.lbs_##name);
> +		LSM_BLOBS_LIST(PRINT_LSM_BLOB_SIZE);
> +#undef PRINT_LSM_BLOB_SIZE
>  	}
>  
>  	if (blob_sizes.lbs_file)

^ permalink raw reply

* [PATCH v4 bpf-next 0/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: David Windsor @ 2026-06-30 18:39 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
	Emil Tsalapatis, Matt Bobrowski, Paul Moore, James Morris,
	Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
	Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
	Shuah Khan
  Cc: bpf, linux-security-module, linux-fsdevel, linux-integrity,
	selinux, linux-kselftest, linux-kernel, David Windsor

Many in-kernel LSMs (SELinux, Smack, IMA) store security labels in
extended attributes. For these LSMs, atomic labeling during inode
creation is critical: if the inode becomes accessible before its xattr
is set, it is briefly unlabeled, which can disrupt LSMs making policy
decisions based on file labels.

Existing LSMs solve this by setting xattrs directly in the
inode_init_security hook, which runs before the inode becomes
accessible. BPF LSM programs currently lack this capability because
the hook uses an output parameter (xattr_count) that BPF programs
cannot write to, and existing kfuncs like bpf_set_dentry_xattr
require a dentry that isn't available until after the inode is
accessible.

This series introduces the bpf_init_inode_xattr() kfunc, which takes
the combined inode_init_security xattr context argument to access
xattrs and xattr_count, and internally writes to xattr_count via
lsm_get_xattr_slot().

v4:
  - introduce struct lsm_xattrs in separate patch (Alexei, Paul)
  - rename struct xattr_ctx to struct lsm_xattrs (Paul)
  - make lsm_xattrs.xattr_count unsigned int (Paul)
  - drop new_xattrs/xattr_count locals in
    security_inode_init_security() (Paul)
  - fold __bpf_init_inode_xattr() into bpf_init_inode_xattr() (Paul)
  - drop bpf_fs_kfuncs_filter() attach-point check; rely on verifier
    type enforcement (Alexei)
  - drop attach-time cap; enforce slot budget in the kfunc (Alexei)
  - allocate the combined xattr with GFP_NOFS (sashiko-bot)
  - replace init_inode_xattr_attach_cap selftest with runtime
    init_inode_xattr_slot_limit

v3:
  - rename struct lsm_xattr_ctx to struct xattr_ctx (Paul)
  - increase BPF_LSM_INODE_INIT_XATTRS to 4 (Song)
  - enforce per-hook attachment cap at attach time to prevent
    runtime rejection (Paul)
  - add init_inode_xattr_attach_cap selftest

v2:
  - pass the xattr state as a combined context object and drop the
    verifier fixup path (Kumar)
  - restrict bpf_init_inode_xattr labels to bpf.* namespace (Matt)
  - cap bpf_init_inode_xattr() at BPF_LSM_INODE_INIT_XATTRS slots per
    invocation (AI)

David Windsor (3):
  security: pass inode_init_security xattrs via struct lsm_xattrs
  bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
  selftests/bpf: add tests for bpf_init_inode_xattr kfunc

 fs/bpf_fs_kfuncs.c                            |  79 +++++++++++
 include/linux/bpf_lsm.h                       |   3 +
 include/linux/evm.h                           |   9 +-
 include/linux/lsm_hook_defs.h                 |   4 +-
 include/linux/lsm_hooks.h                     |  16 +--
 include/linux/security.h                      |   5 +
 kernel/bpf/bpf_lsm.c                          |   1 +
 security/bpf/hooks.c                          |   1 +
 security/integrity/evm/evm_main.c             |   8 +-
 security/security.c                           |  24 ++--
 security/selinux/hooks.c                      |   4 +-
 security/smack/smack_lsm.c                    |  27 ++--
 tools/testing/selftests/bpf/bpf_kfuncs.h      |   5 +
 .../selftests/bpf/prog_tests/lsm_kfuncs.c     | 129 ++++++++++++++++++
 .../bpf/progs/test_init_inode_xattr.c         |  31 +++++
 15 files changed, 299 insertions(+), 47 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_kfuncs.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_init_inode_xattr.c


base-commit: e771677c937da5808f7b6c1f0e4a97ec1a84f8a8
-- 
2.53.0


^ permalink raw reply

* [PATCH v4 bpf-next 1/3] security: pass inode_init_security xattrs via struct lsm_xattrs
From: David Windsor @ 2026-06-30 18:39 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
	Emil Tsalapatis, Matt Bobrowski, Paul Moore, James Morris,
	Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
	Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
	Shuah Khan
  Cc: bpf, linux-security-module, linux-fsdevel, linux-integrity,
	selinux, linux-kselftest, linux-kernel, David Windsor
In-Reply-To: <20260630183956.281293-1-dwindsor@gmail.com>

inode_init_security receives the LSM xattr array and its count as
separate parameters. For better compatibility with the bpf verifier,
update inode_init_security and its callers to consolidate these
parameters into a single context object: struct lsm_xattrs.

Signed-off-by: David Windsor <dwindsor@gmail.com>
---
 include/linux/evm.h               |  9 +++++----
 include/linux/lsm_hook_defs.h     |  4 ++--
 include/linux/lsm_hooks.h         | 16 +++++++---------
 include/linux/security.h          |  5 +++++
 security/integrity/evm/evm_main.c |  8 +++++---
 security/security.c               | 24 ++++++++++++------------
 security/selinux/hooks.c          |  4 ++--
 security/smack/smack_lsm.c        | 27 ++++++++++++---------------
 8 files changed, 50 insertions(+), 47 deletions(-)

diff --git a/include/linux/evm.h b/include/linux/evm.h
index 913f4573b203..528f360f3308 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -12,6 +12,8 @@
 #include <linux/integrity.h>
 #include <linux/xattr.h>
 
+struct lsm_xattrs;
+
 #ifdef CONFIG_EVM
 extern int evm_set_key(void *key, size_t keylen);
 extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
@@ -21,8 +23,8 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
 int evm_fix_hmac(struct dentry *dentry, const char *xattr_name,
 		 const char *xattr_value, size_t xattr_value_len);
 int evm_inode_init_security(struct inode *inode, struct inode *dir,
-			    const struct qstr *qstr, struct xattr *xattrs,
-			    int *xattr_count);
+			    const struct qstr *qstr,
+			    struct lsm_xattrs *xattrs);
 extern bool evm_revalidate_status(const char *xattr_name);
 extern int evm_protected_xattr_if_enabled(const char *req_xattr_name);
 extern int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
@@ -63,8 +65,7 @@ static inline int evm_fix_hmac(struct dentry *dentry, const char *xattr_name,
 
 static inline int evm_inode_init_security(struct inode *inode, struct inode *dir,
 					  const struct qstr *qstr,
-					  struct xattr *xattrs,
-					  int *xattr_count)
+					  struct lsm_xattrs *xattrs)
 {
 	return 0;
 }
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 65c9609ec207..5b2de7865ce8 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -116,8 +116,8 @@ LSM_HOOK(int, 0, inode_alloc_security, struct inode *inode)
 LSM_HOOK(void, LSM_RET_VOID, inode_free_security, struct inode *inode)
 LSM_HOOK(void, LSM_RET_VOID, inode_free_security_rcu, void *inode_security)
 LSM_HOOK(int, -EOPNOTSUPP, inode_init_security, struct inode *inode,
-	 struct inode *dir, const struct qstr *qstr, struct xattr *xattrs,
-	 int *xattr_count)
+	 struct inode *dir, const struct qstr *qstr,
+	 struct lsm_xattrs *xattrs)
 LSM_HOOK(int, 0, inode_init_security_anon, struct inode *inode,
 	 const struct qstr *name, const struct inode *context_inode)
 LSM_HOOK(int, 0, inode_create, struct inode *dir, struct dentry *dentry,
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index b4f8cad53ddb..7afe06a8d4c6 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -200,20 +200,18 @@ extern struct lsm_static_calls_table static_calls_table __ro_after_init;
 
 /**
  * lsm_get_xattr_slot - Return the next available slot and increment the index
- * @xattrs: array storing LSM-provided xattrs
- * @xattr_count: number of already stored xattrs (updated)
+ * @ctx: xattr state shared by inode_init_security hooks
  *
- * Retrieve the first available slot in the @xattrs array to fill with an xattr,
- * and increment @xattr_count.
+ * Retrieve the first available slot in the @ctx->xattrs array to fill with an
+ * xattr, and increment @ctx->xattr_count.
  *
- * Return: The slot to fill in @xattrs if non-NULL, NULL otherwise.
+ * Return: The slot to fill in @ctx->xattrs if non-NULL, NULL otherwise.
  */
-static inline struct xattr *lsm_get_xattr_slot(struct xattr *xattrs,
-					       int *xattr_count)
+static inline struct xattr *lsm_get_xattr_slot(struct lsm_xattrs *ctx)
 {
-	if (unlikely(!xattrs))
+	if (unlikely(!ctx || !ctx->xattrs))
 		return NULL;
-	return &xattrs[(*xattr_count)++];
+	return &ctx->xattrs[ctx->xattr_count++];
 }
 
 #endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/include/linux/security.h b/include/linux/security.h
index 153e9043058f..0be590c40689 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -68,6 +68,11 @@ struct watch;
 struct watch_notification;
 struct lsm_ctx;
 
+struct lsm_xattrs {
+	struct xattr *xattrs;
+	unsigned int xattr_count;
+};
+
 /* Default (no) options for the capable function */
 #define CAP_OPT_NONE 0x0
 /* If capable should audit the security request */
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index b59e3f121b8a..b7158fc63543 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -1062,14 +1062,16 @@ static int evm_inode_copy_up_xattr(struct dentry *src, const char *name)
  * evm_inode_init_security - initializes security.evm HMAC value
  */
 int evm_inode_init_security(struct inode *inode, struct inode *dir,
-			    const struct qstr *qstr, struct xattr *xattrs,
-			    int *xattr_count)
+			    const struct qstr *qstr,
+			    struct lsm_xattrs *lsm_xattrs)
 {
 	struct evm_xattr *xattr_data;
 	struct xattr *xattr, *evm_xattr;
+	struct xattr *xattrs;
 	bool evm_protected_xattrs = false;
 	int rc;
 
+	xattrs = lsm_xattrs ? lsm_xattrs->xattrs : NULL;
 	if (!(evm_initialized & EVM_INIT_HMAC) || !xattrs)
 		return 0;
 
@@ -1087,7 +1089,7 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir,
 	if (!evm_protected_xattrs)
 		return 0;
 
-	evm_xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+	evm_xattr = lsm_get_xattr_slot(lsm_xattrs);
 	/*
 	 * Array terminator (xattr name = NULL) must be the first non-filled
 	 * xattr slot.
diff --git a/security/security.c b/security/security.c
index 71aea8fdf014..2ad7f09c1a61 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1333,8 +1333,8 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
 				 const initxattrs initxattrs, void *fs_data)
 {
 	struct lsm_static_call *scall;
-	struct xattr *new_xattrs = NULL;
-	int ret = -EOPNOTSUPP, xattr_count = 0;
+	struct lsm_xattrs xattrs = {};
+	int ret = -EOPNOTSUPP;
 
 	if (unlikely(IS_PRIVATE(inode)))
 		return 0;
@@ -1344,15 +1344,15 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
 
 	if (initxattrs) {
 		/* Allocate +1 as terminator. */
-		new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1,
-				     sizeof(*new_xattrs), GFP_NOFS);
-		if (!new_xattrs)
+		xattrs.xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1,
+					sizeof(*xattrs.xattrs), GFP_NOFS);
+		if (!xattrs.xattrs)
 			return -ENOMEM;
 	}
 
 	lsm_for_each_hook(scall, inode_init_security) {
-		ret = scall->hl->hook.inode_init_security(inode, dir, qstr, new_xattrs,
-						  &xattr_count);
+		ret = scall->hl->hook.inode_init_security(inode, dir, qstr,
+							  &xattrs);
 		if (ret && ret != -EOPNOTSUPP)
 			goto out;
 		/*
@@ -1364,14 +1364,14 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
 	}
 
 	/* If initxattrs() is NULL, xattr_count is zero, skip the call. */
-	if (!xattr_count)
+	if (!xattrs.xattr_count)
 		goto out;
 
-	ret = initxattrs(inode, new_xattrs, fs_data);
+	ret = initxattrs(inode, xattrs.xattrs, fs_data);
 out:
-	for (; xattr_count > 0; xattr_count--)
-		kfree(new_xattrs[xattr_count - 1].value);
-	kfree(new_xattrs);
+	for (; xattrs.xattr_count > 0; xattrs.xattr_count--)
+		kfree(xattrs.xattrs[xattrs.xattr_count - 1].value);
+	kfree(xattrs.xattrs);
 	return (ret == -EOPNOTSUPP) ? 0 : ret;
 }
 EXPORT_SYMBOL(security_inode_init_security);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1a713d96206f..6bba6b212e17 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2962,7 +2962,7 @@ static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
 
 static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
 				       const struct qstr *qstr,
-				       struct xattr *xattrs, int *xattr_count)
+				       struct lsm_xattrs *xattrs)
 {
 	const struct cred_security_struct *crsec = selinux_cred(current_cred());
 	struct superblock_security_struct *sbsec;
@@ -2992,7 +2992,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
 	    !(sbsec->flags & SBLABEL_MNT))
 		return -EOPNOTSUPP;
 
-	xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+	xattr = lsm_get_xattr_slot(xattrs);
 	if (xattr) {
 		rc = security_sid_to_context_force(newsid,
 						   &context, &clen);
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index ff115068c5c0..4501078430ca 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -981,10 +981,10 @@ smk_rule_transmutes(struct smack_known *subject,
 }
 
 static int
-xattr_dupval(struct xattr *xattrs, int *xattr_count,
+xattr_dupval(struct lsm_xattrs *xattrs,
 	     const char *name, const void *value, unsigned int vallen)
 {
-	struct xattr * const xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+	struct xattr * const xattr = lsm_get_xattr_slot(xattrs);
 
 	if (!xattr)
 		return 0;
@@ -1003,14 +1003,13 @@ xattr_dupval(struct xattr *xattrs, int *xattr_count,
  * @inode: the newly created inode
  * @dir: containing directory object
  * @qstr: unused
- * @xattrs: where to put the attributes
- * @xattr_count: current number of LSM-provided xattrs (updated)
+ * @xattrs: where to put attributes and update count
  *
  * Returns 0 if it all works out, -ENOMEM if there's no memory
  */
 static int smack_inode_init_security(struct inode *inode, struct inode *dir,
 				     const struct qstr *qstr,
-				     struct xattr *xattrs, int *xattr_count)
+				     struct lsm_xattrs *xattrs)
 {
 	struct task_smack *tsp = smack_cred(current_cred());
 	struct inode_smack * const issp = smack_inode(inode);
@@ -1057,21 +1056,19 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
 		if (S_ISDIR(inode->i_mode)) {
 			transflag = SMK_INODE_TRANSMUTE;
 
-			if (xattr_dupval(xattrs, xattr_count,
-				XATTR_SMACK_TRANSMUTE,
-				TRANS_TRUE,
-				TRANS_TRUE_SIZE
-			))
+			if (xattr_dupval(xattrs,
+					 XATTR_SMACK_TRANSMUTE,
+					 TRANS_TRUE,
+					 TRANS_TRUE_SIZE))
 				rc = -ENOMEM;
 		}
 	}
 
 	if (rc == 0)
-		if (xattr_dupval(xattrs, xattr_count,
-			    XATTR_SMACK_SUFFIX,
-			    issp->smk_inode->smk_known,
-		     strlen(issp->smk_inode->smk_known)
-		))
+		if (xattr_dupval(xattrs,
+				 XATTR_SMACK_SUFFIX,
+				 issp->smk_inode->smk_known,
+				 strlen(issp->smk_inode->smk_known)))
 			rc = -ENOMEM;
 instant_inode:
 	issp->smk_flags |= (SMK_INODE_INSTANT | transflag);
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: David Windsor @ 2026-06-30 18:39 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
	Emil Tsalapatis, Matt Bobrowski, Paul Moore, James Morris,
	Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
	Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
	Shuah Khan
  Cc: bpf, linux-security-module, linux-fsdevel, linux-integrity,
	selinux, linux-kselftest, linux-kernel, David Windsor
In-Reply-To: <20260630183956.281293-1-dwindsor@gmail.com>

Add bpf_init_inode_xattr() kfunc for BPF LSM programs to atomically set
xattrs via the inode_init_security hook using lsm_get_xattr_slot(). The
hook now passes its xattr state as a single struct lsm_xattrs object,
which the kfunc takes directly.

The kfunc is only usable from lsm/inode_init_security programs: no other
hook exposes a struct lsm_xattrs argument, so the verifier rejects calls
from elsewhere. Restrict the xattr names that may be set via this kfunc
to the bpf.* namespace.

BPF reserves BPF_LSM_INODE_INIT_XATTRS slots via lbs_xattr_count, and the
kfunc enforces that BPF never consumes more slots than it reserved,
returning -ENOSPC once the budget is exhausted.

A previous attempt [1] required a kmalloc string output protocol for
the xattr name. Since commit 6bcdfd2cac55 ("security: Allow all LSMs to
provide xattrs for inode_init_security hook") [2], the xattr name is no
longer allocated; it is a static constant.

Link: https://kernsec.org/pipermail/linux-security-module-archive/2022-October/034878.html [1]
Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6bcdfd2cac55 [2]
Signed-off-by: David Windsor <dwindsor@gmail.com>
---
 fs/bpf_fs_kfuncs.c      | 79 +++++++++++++++++++++++++++++++++++++++++
 include/linux/bpf_lsm.h |  3 ++
 kernel/bpf/bpf_lsm.c    |  1 +
 security/bpf/hooks.c    |  1 +
 4 files changed, 84 insertions(+)

diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
index 768aca2dc0f0..c4023c82f21e 100644
--- a/fs/bpf_fs_kfuncs.c
+++ b/fs/bpf_fs_kfuncs.c
@@ -10,6 +10,7 @@
 #include <linux/fsnotify.h>
 #include <linux/file.h>
 #include <linux/kernfs.h>
+#include <linux/lsm_hooks.h>
 #include <linux/mm.h>
 #include <linux/xattr.h>
 
@@ -374,6 +375,83 @@ __bpf_kfunc struct inode *bpf_real_inode(struct dentry *dentry)
 	return d_real_inode(dentry);
 }
 
+static int bpf_xattrs_used(const struct lsm_xattrs *ctx)
+{
+	const size_t prefix_len = sizeof(XATTR_BPF_LSM_SUFFIX) - 1;
+	unsigned int i, n = 0;
+
+	for (i = 0; i < ctx->xattr_count; i++) {
+		const char *name = ctx->xattrs[i].name;
+
+		if (name && !strncmp(name, XATTR_BPF_LSM_SUFFIX, prefix_len))
+			n++;
+	}
+	return n;
+}
+
+/**
+ * bpf_init_inode_xattr - set an xattr on a new inode from inode_init_security
+ * @xattrs: inode_init_security xattr state from the hook context
+ * @name__str: xattr name (e.g., "bpf.file_label")
+ * @value_p: dynptr containing the xattr value
+ *
+ * Only callable from lsm/inode_init_security programs.
+ *
+ * Return: 0 on success, negative error on failure.
+ */
+__bpf_kfunc int bpf_init_inode_xattr(struct lsm_xattrs *xattrs,
+				     const char *name__str,
+				     const struct bpf_dynptr *value_p)
+{
+	struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p;
+	size_t name_len;
+	void *xattr_value;
+	struct xattr *xattr;
+	const void *value;
+	u32 value_len;
+
+	if (!xattrs || !xattrs->xattrs || !name__str)
+		return -EINVAL;
+	if (bpf_xattrs_used(xattrs) >= BPF_LSM_INODE_INIT_XATTRS)
+		return -ENOSPC;
+
+	name_len = strlen(name__str);
+	if (name_len == 0 || name_len > XATTR_NAME_MAX)
+		return -EINVAL;
+	if (strncmp(name__str, XATTR_BPF_LSM_SUFFIX,
+		    sizeof(XATTR_BPF_LSM_SUFFIX) - 1))
+		return -EPERM;
+
+	value_len = __bpf_dynptr_size(value_ptr);
+	if (value_len == 0 || value_len > XATTR_SIZE_MAX)
+		return -EINVAL;
+
+	value = __bpf_dynptr_data(value_ptr, value_len);
+	if (!value)
+		return -EINVAL;
+
+	/* Combine xattr value + name into one allocation. */
+	xattr_value = kmalloc(value_len + name_len + 1, GFP_NOFS);
+	if (!xattr_value)
+		return -ENOMEM;
+
+	memcpy(xattr_value, value, value_len);
+	memcpy(xattr_value + value_len, name__str, name_len);
+	((char *)xattr_value)[value_len + name_len] = '\0';
+
+	xattr = lsm_get_xattr_slot(xattrs);
+	if (!xattr) {
+		kfree(xattr_value);
+		return -ENOSPC;
+	}
+
+	xattr->value = xattr_value;
+	xattr->name = (const char *)xattr_value + value_len;
+	xattr->value_len = value_len;
+
+	return 0;
+}
+
 __bpf_kfunc_end_defs();
 
 BTF_KFUNCS_START(bpf_fs_kfunc_set_ids)
@@ -385,6 +463,7 @@ BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE)
 BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE)
 BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE)
 BTF_ID_FLAGS(func, bpf_real_inode, KF_SLEEPABLE | KF_RET_NULL)
+BTF_ID_FLAGS(func, bpf_init_inode_xattr, KF_SLEEPABLE)
 BTF_KFUNCS_END(bpf_fs_kfunc_set_ids)
 
 static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id)
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index 143775a27a2a..b655c708818e 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -19,6 +19,9 @@
 #include <linux/lsm_hook_defs.h>
 #undef LSM_HOOK
 
+/* max bpf xattrs per inode */
+#define BPF_LSM_INODE_INIT_XATTRS 4
+
 struct bpf_storage_blob {
 	struct bpf_local_storage __rcu *storage;
 };
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 564071a92d7d..1c3f84a92420 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -315,6 +315,7 @@ BTF_ID(func, bpf_lsm_inode_create)
 BTF_ID(func, bpf_lsm_inode_free_security)
 BTF_ID(func, bpf_lsm_inode_getattr)
 BTF_ID(func, bpf_lsm_inode_getxattr)
+BTF_ID(func, bpf_lsm_inode_init_security)
 BTF_ID(func, bpf_lsm_inode_mknod)
 BTF_ID(func, bpf_lsm_inode_need_killpriv)
 BTF_ID(func, bpf_lsm_inode_post_setxattr)
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
index 40efde233f3a..d7c44c5c0e30 100644
--- a/security/bpf/hooks.c
+++ b/security/bpf/hooks.c
@@ -30,6 +30,7 @@ static int __init bpf_lsm_init(void)
 
 struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = {
 	.lbs_inode = sizeof(struct bpf_storage_blob),
+	.lbs_xattr_count = BPF_LSM_INODE_INIT_XATTRS,
 };
 
 DEFINE_LSM(bpf) = {
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 bpf-next 3/3] selftests/bpf: add tests for bpf_init_inode_xattr kfunc
From: David Windsor @ 2026-06-30 18:39 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
	Emil Tsalapatis, Matt Bobrowski, Paul Moore, James Morris,
	Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
	Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
	Shuah Khan
  Cc: bpf, linux-security-module, linux-fsdevel, linux-integrity,
	selinux, linux-kselftest, linux-kernel, David Windsor
In-Reply-To: <20260630183956.281293-1-dwindsor@gmail.com>

Test bpf atomic inode xattr labeling in inode_init_security.

Signed-off-by: David Windsor <dwindsor@gmail.com>
---
 tools/testing/selftests/bpf/bpf_kfuncs.h      |   5 +
 .../selftests/bpf/prog_tests/fs_kfuncs.c      | 117 ++++++++++++++++++
 .../bpf/progs/test_init_inode_xattr.c         |  31 +++++
 3 files changed, 153 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/test_init_inode_xattr.c

diff --git a/tools/testing/selftests/bpf/bpf_kfuncs.h b/tools/testing/selftests/bpf/bpf_kfuncs.h
index ae71e9b69051..2639f9f94195 100644
--- a/tools/testing/selftests/bpf/bpf_kfuncs.h
+++ b/tools/testing/selftests/bpf/bpf_kfuncs.h
@@ -92,4 +92,9 @@ extern int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__str,
 				const struct bpf_dynptr *value_p, int flags) __ksym __weak;
 extern int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str) __ksym __weak;
 
+struct lsm_xattrs;
+extern int bpf_init_inode_xattr(struct lsm_xattrs *xattrs,
+				const char *name__str,
+				const struct bpf_dynptr *value_p) __ksym __weak;
+
 #endif
diff --git a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
index 43a26ec69a8e..8b2e0d433aea 100644
--- a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
+++ b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
@@ -10,6 +10,7 @@
 #include "test_get_xattr.skel.h"
 #include "test_set_remove_xattr.skel.h"
 #include "test_fsverity.skel.h"
+#include "test_init_inode_xattr.skel.h"
 
 static const char testfile[] = "/tmp/test_progs_fs_kfuncs";
 
@@ -268,6 +269,116 @@ static void test_fsverity(void)
 	remove(testfile);
 }
 
+static void test_init_inode_xattr(void)
+{
+	struct test_init_inode_xattr *skel = NULL;
+	int fd = -1, err;
+	char value_out[64];
+	const char *testfile_new = "/tmp/test_progs_fs_kfuncs_new";
+
+	skel = test_init_inode_xattr__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "test_init_inode_xattr__open_and_load"))
+		return;
+
+	skel->bss->monitored_pid = getpid();
+	err = test_init_inode_xattr__attach(skel);
+	if (!ASSERT_OK(err, "test_init_inode_xattr__attach"))
+		goto out;
+
+	/* Trigger inode_init_security */
+	fd = open(testfile_new, O_CREAT | O_RDWR, 0644);
+	if (!ASSERT_GE(fd, 0, "create_file"))
+		goto out;
+
+	ASSERT_EQ(skel->data->init_result, 0, "init_result");
+
+	/* initxattrs prepends "security." to the name. */
+	err = getxattr(testfile_new, "security.bpf.test_label", value_out,
+		       sizeof(value_out));
+	if (err < 0 && errno == ENODATA) {
+		printf("%s:SKIP:filesystem did not apply LSM xattrs\n",
+		       __func__);
+		test__skip();
+		goto out;
+	}
+	if (!ASSERT_GE(err, 0, "getxattr"))
+		goto out;
+
+	ASSERT_EQ(err, (int)sizeof(skel->data->xattr_value), "xattr_size");
+	ASSERT_EQ(strncmp(value_out, "unconfined_u:object_r:user_home_t:s0",
+			  sizeof("unconfined_u:object_r:user_home_t:s0")), 0,
+		  "xattr_value");
+
+out:
+	close(fd);
+	test_init_inode_xattr__destroy(skel);
+	remove(testfile_new);
+}
+
+/* Keep in sync with BPF_LSM_INODE_INIT_XATTRS in include/linux/bpf_lsm.h. */
+#define INIT_INODE_XATTR_MAX 4
+
+/*
+ * Programs may attach to inode_init_security without an attach-time limit, but
+ * the kfunc only lets BPF claim INIT_INODE_XATTR_MAX xattr slots per inode.
+ * Calls beyond that budget are rejected at runtime with -ENOSPC.
+ */
+static void test_init_inode_xattr_slot_limit(void)
+{
+	struct test_init_inode_xattr *skel[INIT_INODE_XATTR_MAX + 1] = {};
+	struct bpf_link *link[INIT_INODE_XATTR_MAX + 1] = {};
+	const char *testfile_slot = "/tmp/test_progs_fs_kfuncs_slot";
+	int ok = 0, nospc = 0, other = 0;
+	int i, fd = -1;
+
+	/* All programs attach successfully; there is no attach-time cap. */
+	for (i = 0; i <= INIT_INODE_XATTR_MAX; i++) {
+		skel[i] = test_init_inode_xattr__open_and_load();
+		if (!ASSERT_OK_PTR(skel[i], "open_and_load"))
+			goto out;
+
+		skel[i]->bss->monitored_pid = getpid();
+
+		link[i] = bpf_program__attach_lsm(skel[i]->progs.test_init_inode_xattr);
+		if (!ASSERT_OK_PTR(link[i], "attach"))
+			goto out;
+	}
+
+	/* Trigger inode_init_security once with all programs attached. */
+	fd = open(testfile_slot, O_CREAT | O_RDWR, 0644);
+	if (!ASSERT_GE(fd, 0, "create_file"))
+		goto out;
+
+	/*
+	 * Exactly INIT_INODE_XATTR_MAX programs claim a slot; the program past
+	 * the budget gets -ENOSPC. The order in which programs run is not
+	 * guaranteed, so count results instead of indexing.
+	 */
+	for (i = 0; i <= INIT_INODE_XATTR_MAX; i++) {
+		int res = skel[i]->data->init_result;
+
+		if (res == 0)
+			ok++;
+		else if (res == -ENOSPC)
+			nospc++;
+		else
+			other++;
+	}
+
+	ASSERT_EQ(ok, INIT_INODE_XATTR_MAX, "slots_within_budget");
+	ASSERT_EQ(nospc, 1, "slot_over_budget");
+	ASSERT_EQ(other, 0, "unexpected_result");
+
+out:
+	if (fd >= 0)
+		close(fd);
+	for (i = 0; i <= INIT_INODE_XATTR_MAX; i++) {
+		bpf_link__destroy(link[i]);
+		test_init_inode_xattr__destroy(skel[i]);
+	}
+	remove(testfile_slot);
+}
+
 void test_fs_kfuncs(void)
 {
 	/* Matches xattr_names in progs/test_get_xattr.c */
@@ -288,4 +399,10 @@ void test_fs_kfuncs(void)
 
 	if (test__start_subtest("fsverity"))
 		test_fsverity();
+
+	if (test__start_subtest("init_inode_xattr"))
+		test_init_inode_xattr();
+
+	if (test__start_subtest("init_inode_xattr_slot_limit"))
+		test_init_inode_xattr_slot_limit();
 }
diff --git a/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c b/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c
new file mode 100644
index 000000000000..cb378db957aa
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Cisco Systems, Inc. */
+
+#include "vmlinux.h"
+#include <bpf/bpf_tracing.h>
+#include "bpf_kfuncs.h"
+
+char _license[] SEC("license") = "GPL";
+
+__u32 monitored_pid;
+int init_result = -1;
+
+static const char xattr_name[] = "bpf.test_label";
+char xattr_value[] = "unconfined_u:object_r:user_home_t:s0";
+
+SEC("lsm.s/inode_init_security")
+int BPF_PROG(test_init_inode_xattr, struct inode *inode, struct inode *dir,
+	     const struct qstr *qstr, struct lsm_xattrs *xattrs)
+{
+	struct bpf_dynptr value_ptr;
+	__u32 pid;
+
+	pid = bpf_get_current_pid_tgid() >> 32;
+	if (pid != monitored_pid)
+		return 0;
+
+	bpf_dynptr_from_mem(xattr_value, sizeof(xattr_value), 0, &value_ptr);
+	init_result = bpf_init_inode_xattr(xattrs, xattr_name, &value_ptr);
+
+	return 0;
+}
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: David Windsor @ 2026-06-30 18:46 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
	Emil Tsalapatis, Matt Bobrowski, Paul Moore, James Morris,
	Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
	Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
	Shuah Khan
  Cc: bpf, linux-security-module, linux-fsdevel, linux-integrity,
	selinux, linux-kselftest, linux-kernel
In-Reply-To: <20260630183956.281293-3-dwindsor@gmail.com>

On Thu, Jun 25, 2026 at 7:23 AM Christian Brauner <brauner@kernel.org> wrote:
<snip>
>
> We expose a bunch of VFS heavy operations for various security modules
> and this is really not different. For xattrs we have it all centralized
> in the VFS and in general all VFS related bpf kfuncs should continue
> living there and be registered there. Anything that's just bpf infra
> specific can go to security/bpf/kfuncs.c instead. But anyway, it's a bpf
> specific helper so it's the bpf maintainer's call.

After Alexei's requested changes removing the attach-time checks,
there's really not much left to go in an LSM-specific kfuncs file. The
bpf infra plumbing for registering the kfunc and bpf_xattrs_used()
seem to be the only LSM-specific bits aside from the kfunc.

I am willing to put this code anywhere. I've tried to CC all involved
in all 3 patches, even though there's some split in concerns.

^ permalink raw reply

* Re: [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: Paul Moore @ 2026-06-30 19:20 UTC (permalink / raw)
  To: David Windsor
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
	Emil Tsalapatis, Matt Bobrowski, James Morris, Serge E . Hallyn,
	Casey Schaufler, Stephen Smalley, Ondrej Mosnacek, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Alexander Viro,
	Christian Brauner, Jan Kara, Shuah Khan, bpf,
	linux-security-module, linux-fsdevel, linux-integrity, selinux,
	linux-kselftest, linux-kernel
In-Reply-To: <20260630183956.281293-3-dwindsor@gmail.com>

On Tue, Jun 30, 2026 at 2:40 PM David Windsor <dwindsor@gmail.com> wrote:
>
> Add bpf_init_inode_xattr() kfunc for BPF LSM programs to atomically set
> xattrs via the inode_init_security hook using lsm_get_xattr_slot(). The
> hook now passes its xattr state as a single struct lsm_xattrs object,
> which the kfunc takes directly.
>
> The kfunc is only usable from lsm/inode_init_security programs: no other
> hook exposes a struct lsm_xattrs argument, so the verifier rejects calls
> from elsewhere. Restrict the xattr names that may be set via this kfunc
> to the bpf.* namespace.
>
> BPF reserves BPF_LSM_INODE_INIT_XATTRS slots via lbs_xattr_count, and the
> kfunc enforces that BPF never consumes more slots than it reserved,
> returning -ENOSPC once the budget is exhausted.
>
> A previous attempt [1] required a kmalloc string output protocol for
> the xattr name. Since commit 6bcdfd2cac55 ("security: Allow all LSMs to
> provide xattrs for inode_init_security hook") [2], the xattr name is no
> longer allocated; it is a static constant.
>
> Link: https://kernsec.org/pipermail/linux-security-module-archive/2022-October/034878.html [1]
> Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6bcdfd2cac55 [2]
> Signed-off-by: David Windsor <dwindsor@gmail.com>
> ---
>  fs/bpf_fs_kfuncs.c      | 79 +++++++++++++++++++++++++++++++++++++++++
>  include/linux/bpf_lsm.h |  3 ++
>  kernel/bpf/bpf_lsm.c    |  1 +
>  security/bpf/hooks.c    |  1 +
>  4 files changed, 84 insertions(+)
>
> diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
> index 768aca2dc0f0..c4023c82f21e 100644
> --- a/fs/bpf_fs_kfuncs.c
> +++ b/fs/bpf_fs_kfuncs.c
> @@ -10,6 +10,7 @@
>  #include <linux/fsnotify.h>
>  #include <linux/file.h>
>  #include <linux/kernfs.h>
> +#include <linux/lsm_hooks.h>
>  #include <linux/mm.h>
>  #include <linux/xattr.h>
>
> @@ -374,6 +375,83 @@ __bpf_kfunc struct inode *bpf_real_inode(struct dentry *dentry)
>         return d_real_inode(dentry);
>  }
>
> +static int bpf_xattrs_used(const struct lsm_xattrs *ctx)
> +{
> +       const size_t prefix_len = sizeof(XATTR_BPF_LSM_SUFFIX) - 1;
> +       unsigned int i, n = 0;
> +
> +       for (i = 0; i < ctx->xattr_count; i++) {
> +               const char *name = ctx->xattrs[i].name;
> +
> +               if (name && !strncmp(name, XATTR_BPF_LSM_SUFFIX, prefix_len))
> +                       n++;
> +       }
> +       return n;
> +}
> +
> +/**
> + * bpf_init_inode_xattr - set an xattr on a new inode from inode_init_security
> + * @xattrs: inode_init_security xattr state from the hook context
> + * @name__str: xattr name (e.g., "bpf.file_label")
> + * @value_p: dynptr containing the xattr value
> + *
> + * Only callable from lsm/inode_init_security programs.
> + *
> + * Return: 0 on success, negative error on failure.
> + */
> +__bpf_kfunc int bpf_init_inode_xattr(struct lsm_xattrs *xattrs,
> +                                    const char *name__str,
> +                                    const struct bpf_dynptr *value_p)
> +{
> +       struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p;
> +       size_t name_len;
> +       void *xattr_value;
> +       struct xattr *xattr;
> +       const void *value;
> +       u32 value_len;
> +
> +       if (!xattrs || !xattrs->xattrs || !name__str)
> +               return -EINVAL;
> +       if (bpf_xattrs_used(xattrs) >= BPF_LSM_INODE_INIT_XATTRS)
> +               return -ENOSPC;
> +
> +       name_len = strlen(name__str);
> +       if (name_len == 0 || name_len > XATTR_NAME_MAX)
> +               return -EINVAL;
> +       if (strncmp(name__str, XATTR_BPF_LSM_SUFFIX,
> +                   sizeof(XATTR_BPF_LSM_SUFFIX) - 1))
> +               return -EPERM;
> +
> +       value_len = __bpf_dynptr_size(value_ptr);
> +       if (value_len == 0 || value_len > XATTR_SIZE_MAX)
> +               return -EINVAL;
> +
> +       value = __bpf_dynptr_data(value_ptr, value_len);
> +       if (!value)
> +               return -EINVAL;
> +
> +       /* Combine xattr value + name into one allocation. */
> +       xattr_value = kmalloc(value_len + name_len + 1, GFP_NOFS);
> +       if (!xattr_value)
> +               return -ENOMEM;
> +
> +       memcpy(xattr_value, value, value_len);
> +       memcpy(xattr_value + value_len, name__str, name_len);
> +       ((char *)xattr_value)[value_len + name_len] = '\0';
> +
> +       xattr = lsm_get_xattr_slot(xattrs);
> +       if (!xattr) {
> +               kfree(xattr_value);
> +               return -ENOSPC;
> +       }
> +
> +       xattr->value = xattr_value;
> +       xattr->name = (const char *)xattr_value + value_len;
> +       xattr->value_len = value_len;
> +
> +       return 0;
> +}

This is not a generic VFS function, it is a LSM specific function, it
belongs under security/, please move the code as discussed previously.

--
paul-moore.com

^ permalink raw reply

* Re: [PATCH v3 stable/linux-6.12.y 0/3] Backport Fix incorrect overlayfs mmap() and mprotect() LSM access controls
From: Sasha Levin @ 2026-06-30 22:23 UTC (permalink / raw)
  To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
	stephen.smalley.work, omosnace, gregkh, bboscaccy, caixinchen1
  Cc: Sasha Levin, linux-fsdevel, linux-kernel, linux-unionfs,
	linux-security-module, selinux, bpf, stable, lujialin4
In-Reply-To: <20260629070338.578858-1-caixinchen1@huawei.com>

> Backport the patch series
> "Fix incorrect overlayfs mmap() and mprotect() LSM access controls" [1]
> to 6.12 lts
>
> Amir Goldstein (1):
>   fs: constify file ptr in backing_file accessor helpers
>
> Paul Moore (2):
>   lsm: add backing_file LSM hooks
>   selinux: fix overlayfs mmap() and mprotect() access checks

All three patches are queued for 6.12, thanks.

--
Thanks,
Sasha

^ permalink raw reply

* Re: [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: Alexei Starovoitov @ 2026-07-01  6:09 UTC (permalink / raw)
  To: Paul Moore, David Windsor
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
	Emil Tsalapatis, Matt Bobrowski, James Morris, Serge E . Hallyn,
	Casey Schaufler, Stephen Smalley, Ondrej Mosnacek, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Alexander Viro,
	Christian Brauner, Jan Kara, Shuah Khan, bpf,
	linux-security-module, linux-fsdevel, linux-integrity, selinux,
	linux-kselftest, linux-kernel
In-Reply-To: <CAHC9VhSYNF=_Tfe-D99rHy70EXQVr1ES0arxT09Bidey4zOFdg@mail.gmail.com>

On Tue Jun 30, 2026 at 12:20 PM PDT, Paul Moore wrote:
>> +__bpf_kfunc int bpf_init_inode_xattr(struct lsm_xattrs *xattrs,
>> +                                    const char *name__str,
>> +                                    const struct bpf_dynptr *value_p)
>> +{
>> +       struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p;
>> +       size_t name_len;
>> +       void *xattr_value;
>> +       struct xattr *xattr;
>> +       const void *value;
>> +       u32 value_len;
>> +
>> +       if (!xattrs || !xattrs->xattrs || !name__str)
>> +               return -EINVAL;
>> +       if (bpf_xattrs_used(xattrs) >= BPF_LSM_INODE_INIT_XATTRS)
>> +               return -ENOSPC;
>> +
>> +       name_len = strlen(name__str);
>> +       if (name_len == 0 || name_len > XATTR_NAME_MAX)
>> +               return -EINVAL;
>> +       if (strncmp(name__str, XATTR_BPF_LSM_SUFFIX,
>> +                   sizeof(XATTR_BPF_LSM_SUFFIX) - 1))
>> +               return -EPERM;
>> +
>> +       value_len = __bpf_dynptr_size(value_ptr);
>> +       if (value_len == 0 || value_len > XATTR_SIZE_MAX)
>> +               return -EINVAL;
>> +
>> +       value = __bpf_dynptr_data(value_ptr, value_len);
>> +       if (!value)
>> +               return -EINVAL;
>> +
>> +       /* Combine xattr value + name into one allocation. */
>> +       xattr_value = kmalloc(value_len + name_len + 1, GFP_NOFS);
>> +       if (!xattr_value)
>> +               return -ENOMEM;
>> +
>> +       memcpy(xattr_value, value, value_len);
>> +       memcpy(xattr_value + value_len, name__str, name_len);
>> +       ((char *)xattr_value)[value_len + name_len] = '\0';
>> +
>> +       xattr = lsm_get_xattr_slot(xattrs);
>> +       if (!xattr) {
>> +               kfree(xattr_value);
>> +               return -ENOSPC;
>> +       }
>> +
>> +       xattr->value = xattr_value;
>> +       xattr->name = (const char *)xattr_value + value_len;
>> +       xattr->value_len = value_len;
>> +
>> +       return 0;
>> +}
>
> This is not a generic VFS function, it is a LSM specific function, it
> belongs under security/, please move the code as discussed previously.

Paul,
Not quite. It's all about xattrs.
Having "struct lsm_xattrs" in the arguments doesn't make it lsm related.
You needs to study existing kfuncs and tracepoints.
A bunch of them have "*lsm*" in the arguments.

All,
CI found issues, so this set needs another respin.
After that it's hopefully good to go.

David,
you're on the right track. The patchset is getting close.
Thank you for working on it.


^ permalink raw reply

* Re: [RFC PATCH 06/20] bpf: lsm: Add Landlock kfuncs
From: Mickaël Salaün @ 2026-07-01 10:59 UTC (permalink / raw)
  To: Justin Suess
  Cc: ast, daniel, andrii, kpsingh, paul, viro, brauner, kees, gnoack,
	jack, jmorris, serge, song, yonghong.song, martin.lau, m, eddyz87,
	john.fastabend, sdf, skhan, bpf, linux-security-module,
	linux-kernel, linux-fsdevel, Frederick Lawler
In-Reply-To: <20260407200157.3874806-7-utilityemal77@gmail.com>

On Tue, Apr 07, 2026 at 04:01:28PM -0400, Justin Suess wrote:
> Create 2 kfuncs exposing control over Landlock functionality to BPF
> callers. Export an opaque struct bpf_landlock_ruleset preventing callers
> from accessing unstable internal Landlock fields.
> 
> 1) bpf_landlock_put_ruleset releases a reference on a bpf_landlock_ruleset.
> This is properly passed to the verifier with the KF_RELEASE annotation.
> 
> 2) bpf_landlock_restrict_binprm alters the pre-committed credentials in the
> linux_binprm struct, ensuring the program will start with the specified
> landlock ruleset. Normal domain inheritance, for existing and future
> domains apply as normal.
> 
> To enable proper reference counting and destruction, a destructor is
> registered for the bpf_landlock_ruleset.
> 
> Additionally, both kfuncs are restricted to LSM programs attached to
> bprm_creds_for_exec or bprm_creds_from_file, and only sleepable varients
> of these hooks. Landlock may block because a ruleset is protected by a
> lock, so both of the above kfuncs may sleep and are KF_SLEEPABLE.
> 
> If RESTRICT_FLAGS_NO_NEW_PRIVS is set, and the task doesn't have
> CAP_SYS_ADMIN or is not already running with no_new_privs, we set the
> set_nnp_on_point_of_no_return to ensure that the next execution
> transition (but not the current one) will be subject to no_new_privs.
> 
> Running task_set_no_new_privs directly is unsafe in this path, as a
> failed execution will result in a lingering side effect of no_new_privs
> being set on the original thread.
> 
> Signed-off-by: Justin Suess <utilityemal77@gmail.com>
> ---
>  include/linux/bpf_lsm.h |  15 +++++
>  kernel/bpf/bpf_lsm.c    | 145 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 160 insertions(+)
> 
> diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
> index 643809cc78c3..1fc019c0db44 100644
> --- a/include/linux/bpf_lsm.h
> +++ b/include/linux/bpf_lsm.h
> @@ -31,6 +31,21 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
>  bool bpf_lsm_is_sleepable_hook(u32 btf_id);
>  bool bpf_lsm_is_trusted(const struct bpf_prog *prog);
>  
> +/*
> + * Opaque type for BPF landlock ruleset.  This is used to prevent BPF programs
> + * from directly accessing the landlock_ruleset structure, which is not designed
> + * for external use and may change in the future.
> + */
> +struct bpf_landlock_ruleset {};
> +BTF_ID_LIST_SINGLE(bpf_landlock_ruleset_btf_ids, struct, bpf_landlock_ruleset)
> +__bpf_kfunc void
> +bpf_landlock_put_ruleset(const struct bpf_landlock_ruleset *ruleset);
> +__bpf_kfunc int
> +bpf_landlock_restrict_binprm(struct linux_binprm *bprm,
> +			     const struct bpf_landlock_ruleset *ruleset,
> +			     u32 flags);
> +__bpf_kfunc void bpf_landlock_put_ruleset_dtor(void *ruleset);
> +
>  static inline struct bpf_storage_blob *bpf_inode(
>  	const struct inode *inode)
>  {
> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> index 0c4a0c8e6f70..5da9950aa555 100644
> --- a/kernel/bpf/bpf_lsm.c
> +++ b/kernel/bpf/bpf_lsm.c
> @@ -16,6 +16,7 @@
>  #include <linux/btf_ids.h>
>  #include <linux/ima.h>
>  #include <linux/bpf-cgroup.h>
> +#include <linux/landlock.h>
>  
>  /* For every LSM hook that allows attachment of BPF programs, declare a nop
>   * function where a BPF program can be attached. Notably, we qualify each with
> @@ -447,3 +448,147 @@ int bpf_lsm_get_retval_range(const struct bpf_prog *prog,
>  	}
>  	return 0;
>  }
> +
> +BTF_SET_START(bpf_landlock_kfunc_hooks)
> +BTF_ID(func, bpf_lsm_bprm_creds_for_exec)
> +BTF_ID(func, bpf_lsm_bprm_creds_from_file)
> +BTF_SET_END(bpf_landlock_kfunc_hooks)
> +
> +BTF_KFUNCS_START(bpf_landlock_kfunc_btf_ids)
> +BTF_ID_FLAGS(func, bpf_landlock_put_ruleset, KF_RELEASE | KF_SLEEPABLE)
> +BTF_ID_FLAGS(func, bpf_landlock_restrict_binprm, KF_SLEEPABLE)
> +BTF_KFUNCS_END(bpf_landlock_kfunc_btf_ids)
> +
> +BTF_ID_LIST(bpf_landlock_dtor_ids)
> +BTF_ID(struct, bpf_landlock_ruleset)
> +BTF_ID(func, bpf_landlock_put_ruleset_dtor)
> +
> +static int bpf_landlock_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id)
> +{
> +	if (!btf_id_set8_contains(&bpf_landlock_kfunc_btf_ids, kfunc_id))
> +		return 0;
> +
> +	/* BPF_LSM_CGROUP programs run under classic RCU and cannot sleep. */
> +	if (prog->expected_attach_type == BPF_LSM_CGROUP)
> +		return -EACCES;
> +
> +	if (!btf_id_set_contains(&bpf_landlock_kfunc_hooks,
> +				 prog->aux->attach_btf_id))
> +		return -EACCES;
> +
> +	return 0;
> +}
> +
> +static const struct btf_kfunc_id_set bpf_landlock_kfunc_set = {
> +	.owner = THIS_MODULE,
> +	.set = &bpf_landlock_kfunc_btf_ids,
> +	.filter = bpf_landlock_kfunc_filter,
> +};
> +
> +static int __init bpf_landlock_kfunc_init(void)
> +{
> +	const struct btf_id_dtor_kfunc bpf_landlock_dtors[] = {
> +		{
> +			.btf_id = bpf_landlock_dtor_ids[0],
> +			.kfunc_btf_id = bpf_landlock_dtor_ids[1],
> +		},
> +	};
> +	int ret;
> +
> +	ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM,
> +					&bpf_landlock_kfunc_set);
> +	if (ret)
> +		return ret;
> +
> +	return register_btf_id_dtor_kfuncs(bpf_landlock_dtors,
> +					   ARRAY_SIZE(bpf_landlock_dtors),
> +					   THIS_MODULE);
> +}
> +
> +late_initcall(bpf_landlock_kfunc_init);
> +
> +__bpf_kfunc_start_defs();
> +
> +#if IS_ENABLED(CONFIG_SECURITY_LANDLOCK)
> +
> +/**
> + * bpf_landlock_put_ruleset - put a Landlock ruleset
> + * @ruleset: Landlock ruleset to put
> + */
> +__bpf_kfunc void
> +bpf_landlock_put_ruleset(const struct bpf_landlock_ruleset *ruleset)
> +{
> +	landlock_put_ruleset((struct landlock_ruleset *)ruleset);
> +}
> +
> +/**
> + * bpf_landlock_restrict_binprm - enforce a Landlock ruleset on exec credentials
> + * @bprm: execution context providing the prepared credentials to restrict
> + * @ruleset: Landlock ruleset to enforce, may be NULL only with
> + *	LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF
> + * @flags: landlock_restrict_self() flags
> + *
> + * When @flags contains LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS, the request is
> + * staged through @bprm and committed only after exec reaches point-of-no-return.
> + * This guarantees that the resulting task cannot gain more privileges through
> + * later exec transitions, including when called from bprm_creds_from_file.
> + * The current execution is unaffected, and may escalate as usual until the next
> + * exec.
> + */
> +__bpf_kfunc int
> +bpf_landlock_restrict_binprm(struct linux_binprm *bprm,
> +			     const struct bpf_landlock_ruleset *ruleset,
> +			     u32 flags)
> +{
> +	int err = landlock_restrict_cred_precheck(flags, false);

Landlock domain creation allocates memory with GFP_KERNEL_ACCOUNT, but
when called by eBPF, this should not be the case.

> +
> +	if (err)
> +		return err;
> +
> +	err = landlock_restrict_cred(bprm->cred,
> +				     (struct landlock_ruleset *)ruleset,
> +				     flags);
> +
> +	if (err)
> +		return err;
> +	/*
> +	 * Stage no_new_privs through @bprm so exec can honor it without
> +	 * mutating the current task before point-of-no-return.
> +	 */
> +	if ((flags & LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS)
> +	    && !task_no_new_privs(current)
> +	    && !ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN))
> +		bprm->set_nnp_on_point_of_no_return = 1;
> +
> +	return err;
> +}
> +
> +/* We define stubs for these to allow ebpf programs using landlock kfuncs to load
> + * even when CONFIG_SECURITY_LANDLOCK is not enabled.
> + */
> +#else /* IS_ENABLED(CONFIG_SECURITY_LANDLOCK) */
> +
> +__bpf_kfunc void
> +bpf_landlock_put_ruleset(const struct bpf_landlock_ruleset *ruleset)
> +{
> +}
> +
> +__bpf_kfunc int
> +bpf_landlock_restrict_binprm(struct linux_binprm *bprm,
> +			     const struct bpf_landlock_ruleset *ruleset,
> +			     u32 flags)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +#endif /* IS_ENABLED(CONFIG_SECURITY_LANDLOCK) */
> +
> +/* Destructor does nothing when Landlock is not enabled */
> +__bpf_kfunc void bpf_landlock_put_ruleset_dtor(void *ruleset)
> +{
> +	bpf_landlock_put_ruleset(ruleset);
> +}
> +
> +CFI_NOSEAL(bpf_landlock_put_ruleset_dtor);
> +
> +__bpf_kfunc_end_defs();
> -- 
> 2.53.0
> 
> 

^ permalink raw reply

* linux-next: apparmor: ld.lld: error: undefined symbol: decompress_zstd
From: Tetsuo Handa @ 2026-07-01 11:46 UTC (permalink / raw)
  To: Maxime Bélair, John Johansen; +Cc: linux-security-module

linux-next is failing to build, for decompress_zstd() is guarded by
CONFIG_SECURITY_APPARMOR_EXPORT_BINARY=y.

https://syzkaller.appspot.com/bug?id=71887a15323ee1c7728a72bf3bd6356263f4a10f

Please fix as soon as possible, for I'm asking syzbot to test my debug patches
using linux-next.


^ permalink raw reply

* Re: [RFC PATCH 06/20] bpf: lsm: Add Landlock kfuncs
From: Paul Moore @ 2026-07-01 12:12 UTC (permalink / raw)
  To: Mickaël Salaün, Justin Suess
  Cc: ast, daniel, andrii, kpsingh, viro, brauner, kees, gnoack, jack,
	jmorris, serge, song, yonghong.song, martin.lau, m, eddyz87,
	john.fastabend, sdf, skhan, bpf, linux-security-module,
	linux-kernel, linux-fsdevel, Frederick Lawler
In-Reply-To: <20260701.ze4eph1eKo7a@digikod.net>

On Wed, Jul 1, 2026 at 6:59 AM Mickaël Salaün <mic@digikod.net> wrote:
> On Tue, Apr 07, 2026 at 04:01:28PM -0400, Justin Suess wrote:
> > Create 2 kfuncs exposing control over Landlock functionality to BPF
> > callers. Export an opaque struct bpf_landlock_ruleset preventing callers
> > from accessing unstable internal Landlock fields.

Generally speaking we don't want to provide APIs, either in-kernel or
at the userspace/kernel boundary, that are specific to a single LSM,
see the LSM syscalls or the security_current_getlsmprop_subj()
function as examples.

Yes, Landlock does have its own syscalls, but those are
"grandfathered" and not something I want to see emulated across other
LSMs.  If a BPF program wants to interact with a LSM, it should go
through a LSM framework API.

There have been some initial efforts to develop a LSM wide policy API
for userspace, and while it was put on hold to sort out some namespace
issues, we could move forward with an in-kernel API now.  We don't
have strict API stability guarantees for LSM hooks/APIs so we have
some more freedom to do something now, even if it isn't perfect, and
refine it at a later date.

-- 
paul-moore.com

^ permalink raw reply

* [PATCH 0/6] selftests: fix multiple spelling errors across submodules
From: Wang Yan @ 2026-07-01 12:35 UTC (permalink / raw)
  To: Shuah Khan, Mickaël Salaün, Günther Noack,
	linux-security-module
  Cc: linux-kselftest, linux-kernel, Wang Yan

This series fixes trivial spelling typos in comments across multiple
selftests subdirectories. All changes are pure comment fixes, no
functional or logical change to any test logic.

Changes are split by subsystem for easier review and merging.

Please review.

Wang Yan (6):
  selftests/arm64: fix spelling errors in comments
  selftests/filesystems: fix spelling error in statmount test comment
  selftests/ftrace: fix spelling error in poll test comment
  selftests/kvm/x86: fix spelling error in xapic_ipi_test comment
  selftests/landlock: fix spelling error in fs_test comment
  selftests/powerpc/tm: fix spelling errors in comments

 tools/testing/selftests/arm64/gcs/libc-gcs.c                   | 2 +-
 tools/testing/selftests/arm64/pauth/pac.c                      | 2 +-
 tools/testing/selftests/filesystems/statmount/statmount_test.c | 2 +-
 tools/testing/selftests/ftrace/poll.c                          | 2 +-
 tools/testing/selftests/kvm/x86/xapic_ipi_test.c               | 2 +-
 tools/testing/selftests/landlock/fs_test.c                     | 2 +-
 tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c        | 2 +-
 tools/testing/selftests/powerpc/tm/tm-signal-stack.c           | 2 +-
 tools/testing/selftests/powerpc/tm/tm-tar.c                    | 2 +-
 9 files changed, 9 insertions(+), 9 deletions(-)

-- 
2.25.1


^ permalink raw reply

* [PATCH 5/6] selftests/landlock: fix spelling error in fs_test comment
From: Wang Yan @ 2026-07-01 12:35 UTC (permalink / raw)
  To: Shuah Khan, Mickaël Salaün, Günther Noack,
	linux-security-module, linux-kselftest, linux-kernel
  Cc: Wang Yan
In-Reply-To: <20260701123520.271580-1-wangyan01@kylinos.cn>

Fix typo "allowes" -> "allows" in Landlock filesystem test comment.

Signed-off-by: Wang Yan <wangyan01@kylinos.cn>
---
 tools/testing/selftests/landlock/fs_test.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index 86e08aa6e0a7..e672089e9329 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -6927,7 +6927,7 @@ TEST_F_FORK(layout2_overlay, same_content_different_file)
 		ASSERT_EQ(0, test_open(path_entry, O_RDWR));
 	}
 
-	/* Only allowes access to the merge hierarchy. */
+	/* Only allows access to the merge hierarchy. */
 	enforce_fs(_metadata, ACCESS_RW, layer5_merge_only);
 
 	/* Checks new accesses on lower layer. */
-- 
2.25.1


^ permalink raw reply related

* Re: [RFC PATCH 06/20] bpf: lsm: Add Landlock kfuncs
From: Justin Suess @ 2026-07-01 12:52 UTC (permalink / raw)
  To: Paul Moore
  Cc: Mickaël Salaün, ast, daniel, andrii, kpsingh, viro,
	brauner, kees, gnoack, jack, jmorris, serge, song, yonghong.song,
	martin.lau, m, eddyz87, john.fastabend, sdf, skhan, bpf,
	linux-security-module, linux-kernel, linux-fsdevel,
	Frederick Lawler
In-Reply-To: <CAHC9VhRfqSoM89WC6TdQHapUVQe8a18rW2c-F7+tu6EjM9EdCA@mail.gmail.com>

On Wed, Jul 01, 2026 at 08:12:34AM -0400, Paul Moore wrote:
> On Wed, Jul 1, 2026 at 6:59 AM Mickaël Salaün <mic@digikod.net> wrote:
> > On Tue, Apr 07, 2026 at 04:01:28PM -0400, Justin Suess wrote:
> > > Create 2 kfuncs exposing control over Landlock functionality to BPF
> > > callers. Export an opaque struct bpf_landlock_ruleset preventing callers
> > > from accessing unstable internal Landlock fields.
> 
> Generally speaking we don't want to provide APIs, either in-kernel or
> at the userspace/kernel boundary, that are specific to a single LSM,
> see the LSM syscalls or the security_current_getlsmprop_subj()
> function as examples.
>

I would raise bpf_ima_file_hash, bpf_ima_inode_hash, as examples of
clear precedence for this. (BPF calling into specific LSM)

Is this also discouraged now?

These IMA BPF functions are also helpers, which are more "permanent"
than the kfuncs like this patch proposes.

Kfuncs are explicitly marked as not being an ABI, and are more
flexible for later changes / deprecation etc. [1]

That was partially why I proposed this as a kfunc, and not a helper.

[1] : https://docs.ebpf.io/linux/concepts/kfuncs/

> Yes, Landlock does have its own syscalls, but those are
> "grandfathered" and not something I want to see emulated across other
> LSMs.  If a BPF program wants to interact with a LSM, it should go
> through a LSM framework API.
>

LSM framework API can mean a lot of things. I assume you are meaning
like a pseudo-filesystem mounted interface that controls LSM? 
Correct me if I'm wrong.

I'm a little unsure how this would work with the BPF model. Generally,
BPF relies on type checking and reference checking. Creating a weakly
typed securityfs / sysfs like interface would be very awkward for BPF to
use. Especially if it requires reading / writing files, or parsing
strings, it would be very hairy. It's the same problem with reading any
file from kernel space, it's almost always inadvisable.

Pseudo-fs is fantastic for userspace, (read and write is as simple as
echo and cat) but not fantastic when you are writing BPF programs.

But maybe this is a false diochotomy, I see no reason why the LSM
framework API couldn't have a strongly typed interface into BPF via
helpers / kfuncs. In that case, wouldn't these kfuncs be exactly that
LSM framework API? And there be some translation layer exposing them to
userspace using BTF type information -> pseudo fs? :)

BTF is not just for BPF!

Justin

> There have been some initial efforts to develop a LSM wide policy API
> for userspace, and while it was put on hold to sort out some namespace
> issues, we could move forward with an in-kernel API now.  We don't
> have strict API stability guarantees for LSM hooks/APIs so we have
> some more freedom to do something now, even if it isn't perfect, and
> refine it at a later date.
> 
> -- 
> paul-moore.com

^ permalink raw reply

* Re: [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: Paul Moore @ 2026-07-01 12:55 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: David Windsor, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Jiri Olsa,
	Kumar Kartikeya Dwivedi, Emil Tsalapatis, Matt Bobrowski,
	James Morris, Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
	Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
	Shuah Khan, bpf, linux-security-module, linux-fsdevel,
	linux-integrity, selinux, linux-kselftest, linux-kernel
In-Reply-To: <DJN0EPROS056.3RY0R6W1XZHNZ@gmail.com>

On Wed, Jul 1, 2026 at 2:09 AM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
> On Tue Jun 30, 2026 at 12:20 PM PDT, Paul Moore wrote:
> >> +__bpf_kfunc int bpf_init_inode_xattr(struct lsm_xattrs *xattrs,
> >> +                                    const char *name__str,
> >> +                                    const struct bpf_dynptr *value_p)
> >> +{
> >> +       struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p;
> >> +       size_t name_len;
> >> +       void *xattr_value;
> >> +       struct xattr *xattr;
> >> +       const void *value;
> >> +       u32 value_len;
> >> +
> >> +       if (!xattrs || !xattrs->xattrs || !name__str)
> >> +               return -EINVAL;
> >> +       if (bpf_xattrs_used(xattrs) >= BPF_LSM_INODE_INIT_XATTRS)
> >> +               return -ENOSPC;
> >> +
> >> +       name_len = strlen(name__str);
> >> +       if (name_len == 0 || name_len > XATTR_NAME_MAX)
> >> +               return -EINVAL;
> >> +       if (strncmp(name__str, XATTR_BPF_LSM_SUFFIX,
> >> +                   sizeof(XATTR_BPF_LSM_SUFFIX) - 1))
> >> +               return -EPERM;
> >> +
> >> +       value_len = __bpf_dynptr_size(value_ptr);
> >> +       if (value_len == 0 || value_len > XATTR_SIZE_MAX)
> >> +               return -EINVAL;
> >> +
> >> +       value = __bpf_dynptr_data(value_ptr, value_len);
> >> +       if (!value)
> >> +               return -EINVAL;
> >> +
> >> +       /* Combine xattr value + name into one allocation. */
> >> +       xattr_value = kmalloc(value_len + name_len + 1, GFP_NOFS);
> >> +       if (!xattr_value)
> >> +               return -ENOMEM;
> >> +
> >> +       memcpy(xattr_value, value, value_len);
> >> +       memcpy(xattr_value + value_len, name__str, name_len);
> >> +       ((char *)xattr_value)[value_len + name_len] = '\0';
> >> +
> >> +       xattr = lsm_get_xattr_slot(xattrs);
> >> +       if (!xattr) {
> >> +               kfree(xattr_value);
> >> +               return -ENOSPC;
> >> +       }
> >> +
> >> +       xattr->value = xattr_value;
> >> +       xattr->name = (const char *)xattr_value + value_len;
> >> +       xattr->value_len = value_len;
> >> +
> >> +       return 0;
> >> +}
> >
> > This is not a generic VFS function, it is a LSM specific function, it
> > belongs under security/, please move the code as discussed previously.
>
> Paul,
> Not quite. It's all about xattrs.
> Having "struct lsm_xattrs" in the arguments doesn't make it lsm related.
> You needs to study existing kfuncs and tracepoints.
> A bunch of them have "*lsm*" in the arguments.

Alexei,

I'm sorry you don't understand the basics of the LSM concept, but
please look at evm_inode_init_security(), xattr_dupval(), and
selinux_inode_init_security() for some background.  There should not
be any usage of lsm_get_xattr_slot() or BPF_LSM_INODE_INIT_XATTRS
outside of security/; you argued a similar idea to justify your NACK
of Hornet, I'm simply applying the same logic here.  We also have the
very recent security issue caused by the BPF subsystem which failed to
acknowledge that the admin disabled the BPF LSM and then walked all
over kernel memory when it shouldn't.  Moving LSM internals outside of
the LSM creates an environment where flaws like this can go
undetected.

As I said previously, if you absolutely insist on the kfunc being in
the VFS kfunc file, the LSM specific bits need to be abstracted out
into an LSM function.

  kfunc bpf_init_inode_xattr(...)
  {
    /* sanity check params */
    if (!xattrs ...)
      return -EINVAL;

   /* get value/len from bpf dynptr */

   /* hook will check for LSM specific xattr count/limits, allocate,
copy value*/
   rc = security_lsmxattr_add(xattrs, LSM_ID_BPF, value, value_len);
   if (rc)
     return rc;
  }

David, if you like I can provide you a patch that implements the
security_lsmxattr_add() hook above if you aren't comfortable writing
that, but if you want to give it a shot that's all the better :)

-- 
paul-moore.com

^ permalink raw reply

* Re: [RFC PATCH 06/20] bpf: lsm: Add Landlock kfuncs
From: Paul Moore @ 2026-07-01 13:28 UTC (permalink / raw)
  To: Justin Suess
  Cc: Mickaël Salaün, ast, daniel, andrii, kpsingh, viro,
	brauner, kees, gnoack, jack, jmorris, serge, song, yonghong.song,
	martin.lau, m, eddyz87, john.fastabend, sdf, skhan, bpf,
	linux-security-module, linux-kernel, linux-fsdevel,
	Frederick Lawler
In-Reply-To: <akUH13qbsEnJLgQu@zenbox>

On Wed, Jul 1, 2026 at 8:52 AM Justin Suess <utilityemal77@gmail.com> wrote:
> On Wed, Jul 01, 2026 at 08:12:34AM -0400, Paul Moore wrote:
> > On Wed, Jul 1, 2026 at 6:59 AM Mickaël Salaün <mic@digikod.net> wrote:
> > > On Tue, Apr 07, 2026 at 04:01:28PM -0400, Justin Suess wrote:
> > > > Create 2 kfuncs exposing control over Landlock functionality to BPF
> > > > callers. Export an opaque struct bpf_landlock_ruleset preventing callers
> > > > from accessing unstable internal Landlock fields.
> >
> > Generally speaking we don't want to provide APIs, either in-kernel or
> > at the userspace/kernel boundary, that are specific to a single LSM,
> > see the LSM syscalls or the security_current_getlsmprop_subj()
> > function as examples.
>
> I would raise bpf_ima_file_hash, bpf_ima_inode_hash, as examples of
> clear precedence for this. (BPF calling into specific LSM)

The BPF IMA helpers were merged back in the v5.18 timeframe when IMA
was still standalone, it wasn't until v6.9 that IMA and EVM became
proper LSMs.

> Kfuncs are explicitly marked as not being an ABI, and are more
> flexible for later changes / deprecation etc. [1]

The issue isn't so much the kfunc itself, it is what the kfunc
*calls*.  From what I saw in the proposed patch, the kfunc calls
directly into Landlock instead of passing through the LSM framework,
e.g. a function wrapper in security/security.c.

> LSM framework API can mean a lot of things. I assume you are meaning
> like a pseudo-filesystem mounted interface that controls LSM?
> Correct me if I'm wrong.

My apologies, I should have been more clear.  When I speak about the
"LSM framework", I'm talking about the abstraction layer that provides
the interface that the kernel and userspace uses to talk to individual
LSMs.  The LSM framework is analogous to the VFS layer/framework in
that it provides a single API for a variety of underlying subsystems.
While not 100% correct, you can think of it the LSM framework as being
the functions/hooks defined in security/security.c.

Does that help?

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH 0/6] selftests: fix multiple spelling errors across submodules
From: Sean Christopherson @ 2026-07-01 14:18 UTC (permalink / raw)
  To: Wang Yan
  Cc: Shuah Khan, Mickaël Salaün, Günther Noack,
	linux-security-module, linux-kselftest, linux-kernel
In-Reply-To: <20260701123520.271580-1-wangyan01@kylinos.cn>

On Wed, Jul 01, 2026, Wang Yan wrote:
> This series fixes trivial spelling typos in comments across multiple
> selftests subdirectories. All changes are pure comment fixes, no
> functional or logical change to any test logic.
> 
> Changes are split by subsystem for easier review and merging.

In the future, please send individual patches.  Splitting the patches is nice,
but then bundling them in a single series that affects different maintainer
domains reintroduces the friction you're trying to avoid, especially since most
of us only got the one patch.  E.g. I only received patch 4/6, which left me
wondering "what's in the other 5 patches?".

> Wang Yan (6):
>   selftests/arm64: fix spelling errors in comments
>   selftests/filesystems: fix spelling error in statmount test comment
>   selftests/ftrace: fix spelling error in poll test comment
>   selftests/kvm/x86: fix spelling error in xapic_ipi_test comment
>   selftests/landlock: fix spelling error in fs_test comment
>   selftests/powerpc/tm: fix spelling errors in comments
> 
>  tools/testing/selftests/arm64/gcs/libc-gcs.c                   | 2 +-
>  tools/testing/selftests/arm64/pauth/pac.c                      | 2 +-
>  tools/testing/selftests/filesystems/statmount/statmount_test.c | 2 +-
>  tools/testing/selftests/ftrace/poll.c                          | 2 +-
>  tools/testing/selftests/kvm/x86/xapic_ipi_test.c               | 2 +-
>  tools/testing/selftests/landlock/fs_test.c                     | 2 +-
>  tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c        | 2 +-
>  tools/testing/selftests/powerpc/tm/tm-signal-stack.c           | 2 +-
>  tools/testing/selftests/powerpc/tm/tm-tar.c                    | 2 +-
>  9 files changed, 9 insertions(+), 9 deletions(-)
> 
> -- 
> 2.25.1
> 

^ permalink raw reply


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