Linux Security Modules development
 help / color / mirror / Atom feed
* [PATCH v3 04/34] lsm: introduce looping macros for the initialization code
From: Paul Moore @ 2025-08-14 22:50 UTC (permalink / raw)
  To: linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
	Mickaël Salaün, Günther Noack, Kees Cook,
	Micah Morton, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
	Xiu Jianfeng
In-Reply-To: <20250814225159.275901-36-paul@paul-moore.com>

There are three common for loop patterns in the LSM initialization code
to loop through the ordered LSM list and the registered "early" LSMs.
This patch implements these loop patterns as macros to help simplify the
code and reduce the change for errors.

Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
 security/lsm_init.c | 42 +++++++++++++++++++++++++++---------------
 1 file changed, 27 insertions(+), 15 deletions(-)

diff --git a/security/lsm_init.c b/security/lsm_init.c
index 6f40ab1d2f54..18828a65c364 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -32,6 +32,15 @@ static __initdata bool debug;
 			pr_info(__VA_ARGS__);				\
 	} while (0)
 
+#define lsm_order_for_each(iter)					\
+	for ((iter) = ordered_lsms; *(iter); (iter)++)
+#define lsm_for_each_raw(iter)						\
+	for ((iter) = __start_lsm_info;					\
+	     (iter) < __end_lsm_info; (iter)++)
+#define lsm_early_for_each_raw(iter)					\
+	for ((iter) = __start_early_lsm_info;				\
+	     (iter) < __end_early_lsm_info; (iter)++)
+
 static int lsm_append(const char *new, char **result);
 
 /* Save user chosen LSM */
@@ -96,9 +105,10 @@ static bool __init exists_ordered_lsm(struct lsm_info *lsm)
 {
 	struct lsm_info **check;
 
-	for (check = ordered_lsms; *check; check++)
+	lsm_order_for_each(check) {
 		if (*check == lsm)
 			return true;
+	}
 
 	return false;
 }
@@ -209,7 +219,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
 	char *sep, *name, *next;
 
 	/* LSM_ORDER_FIRST is always first. */
-	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+	lsm_for_each_raw(lsm) {
 		if (lsm->order == LSM_ORDER_FIRST)
 			append_ordered_lsm(lsm, "  first");
 	}
@@ -224,8 +234,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
 		 * if the selected one was separately disabled: disable
 		 * all non-matching Legacy Major LSMs.
 		 */
-		for (major = __start_lsm_info; major < __end_lsm_info;
-		     major++) {
+		lsm_for_each_raw(major) {
 			if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
 			    strcmp(major->name, chosen_major_lsm) != 0) {
 				set_enabled(major, false);
@@ -241,7 +250,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
 	while ((name = strsep(&next, ",")) != NULL) {
 		bool found = false;
 
-		for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+		lsm_for_each_raw(lsm) {
 			if (strcmp(lsm->name, name) == 0) {
 				if (lsm->order == LSM_ORDER_MUTABLE)
 					append_ordered_lsm(lsm, origin);
@@ -256,7 +265,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
 
 	/* Process "security=", if given. */
 	if (chosen_major_lsm) {
-		for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+		lsm_for_each_raw(lsm) {
 			if (exists_ordered_lsm(lsm))
 				continue;
 			if (strcmp(lsm->name, chosen_major_lsm) == 0)
@@ -265,13 +274,13 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
 	}
 
 	/* LSM_ORDER_LAST is always last. */
-	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+	lsm_for_each_raw(lsm) {
 		if (lsm->order == LSM_ORDER_LAST)
 			append_ordered_lsm(lsm, "   last");
 	}
 
 	/* Disable all LSMs not in the ordered list. */
-	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+	lsm_for_each_raw(lsm) {
 		if (exists_ordered_lsm(lsm))
 			continue;
 		set_enabled(lsm, false);
@@ -290,13 +299,14 @@ static void __init report_lsm_order(void)
 	pr_info("initializing lsm=");
 
 	/* Report each enabled LSM name, comma separated. */
-	for (early = __start_early_lsm_info;
-	     early < __end_early_lsm_info; early++)
+	lsm_early_for_each_raw(early) {
 		if (is_enabled(early))
 			pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
-	for (lsm = ordered_lsms; *lsm; lsm++)
+	}
+	lsm_order_for_each(lsm) {
 		if (is_enabled(*lsm))
 			pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
+	}
 
 	pr_cont("\n");
 }
@@ -343,8 +353,9 @@ static void __init ordered_lsm_init(void)
 	} else
 		ordered_lsm_parse(builtin_lsm_order, "builtin");
 
-	for (lsm = ordered_lsms; *lsm; lsm++)
+	lsm_order_for_each(lsm) {
 		lsm_prepare(*lsm);
+	}
 
 	report_lsm_order();
 
@@ -382,8 +393,9 @@ static void __init ordered_lsm_init(void)
 
 	lsm_early_cred((struct cred *) current->cred);
 	lsm_early_task(current);
-	for (lsm = ordered_lsms; *lsm; lsm++)
+	lsm_order_for_each(lsm) {
 		initialize_lsm(*lsm);
+	}
 }
 
 static bool match_last_lsm(const char *list, const char *lsm)
@@ -485,7 +497,7 @@ int __init early_security_init(void)
 {
 	struct lsm_info *lsm;
 
-	for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
+	lsm_early_for_each_raw(lsm) {
 		if (!lsm->enabled)
 			lsm->enabled = &lsm_enabled_true;
 		lsm_prepare(lsm);
@@ -512,7 +524,7 @@ int __init security_init(void)
 	 * Append the names of the early LSM modules now that kmalloc() is
 	 * available
 	 */
-	for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
+	lsm_early_for_each_raw(lsm) {
 		init_debug("  early started: %s (%s)\n", lsm->name,
 			   is_enabled(lsm) ? "enabled" : "disabled");
 		if (lsm->enabled)
-- 
2.50.1


^ permalink raw reply related

* [PATCH v3 03/34] lsm: consolidate lsm_allowed() and prepare_lsm() into lsm_prepare()
From: Paul Moore @ 2025-08-14 22:50 UTC (permalink / raw)
  To: linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
	Mickaël Salaün, Günther Noack, Kees Cook,
	Micah Morton, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
	Xiu Jianfeng
In-Reply-To: <20250814225159.275901-36-paul@paul-moore.com>

Simplify and consolidate the lsm_allowed() and prepare_lsm() functions
into a new function, lsm_prepare().

Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
 security/lsm_init.c | 109 +++++++++++++++++++-------------------------
 1 file changed, 46 insertions(+), 63 deletions(-)

diff --git a/security/lsm_init.c b/security/lsm_init.c
index 124213b906af..6f40ab1d2f54 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -123,22 +123,6 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
 		   is_enabled(lsm) ? "enabled" : "disabled");
 }
 
-/* Is an LSM allowed to be initialized? */
-static bool __init lsm_allowed(struct lsm_info *lsm)
-{
-	/* Skip if the LSM is disabled. */
-	if (!is_enabled(lsm))
-		return false;
-
-	/* Not allowed if another exclusive LSM already initialized. */
-	if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
-		init_debug("exclusive disabled: %s\n", lsm->name);
-		return false;
-	}
-
-	return true;
-}
-
 static void __init lsm_set_blob_size(int *need, int *lbs)
 {
 	int offset;
@@ -151,54 +135,53 @@ static void __init lsm_set_blob_size(int *need, int *lbs)
 	*need = offset;
 }
 
-static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
+/**
+ * lsm_prepare - Prepare the LSM framework for a new LSM
+ * @lsm: LSM definition
+ */
+static void __init lsm_prepare(struct lsm_info *lsm)
 {
-	if (!needed)
+	struct lsm_blob_sizes *blobs;
+
+	if (!is_enabled(lsm)) {
+		set_enabled(lsm, false);
+		return;
+	} else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
+		init_debug("exclusive disabled: %s\n", lsm->name);
+		set_enabled(lsm, false);
 		return;
-
-	lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
-	lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
-	lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
-	/*
-	 * The inode blob gets an rcu_head in addition to
-	 * what the modules might need.
-	 */
-	if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
-		blob_sizes.lbs_inode = sizeof(struct rcu_head);
-	lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
-	lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
-	lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
-	lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
-	lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
-	lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
-	lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
-	lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
-	lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
-	lsm_set_blob_size(&needed->lbs_xattr_count,
-			  &blob_sizes.lbs_xattr_count);
-	lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
-	lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
-	lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
-	lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
-}
-
-/* Prepare LSM for initialization. */
-static void __init prepare_lsm(struct lsm_info *lsm)
-{
-	int enabled = lsm_allowed(lsm);
-
-	/* Record enablement (to handle any following exclusive LSMs). */
-	set_enabled(lsm, enabled);
-
-	/* If enabled, do pre-initialization work. */
-	if (enabled) {
-		if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
-			exclusive = lsm;
-			init_debug("exclusive chosen:   %s\n", lsm->name);
-		}
-
-		lsm_set_blob_sizes(lsm->blobs);
 	}
+
+	/* Mark the LSM as enabled. */
+	set_enabled(lsm, true);
+	if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
+		init_debug("exclusive chosen:   %s\n", lsm->name);
+		exclusive = lsm;
+	}
+
+	/* Register the LSM blob sizes. */
+	blobs = lsm->blobs;
+	lsm_set_blob_size(&blobs->lbs_cred, &blob_sizes.lbs_cred);
+	lsm_set_blob_size(&blobs->lbs_file, &blob_sizes.lbs_file);
+	lsm_set_blob_size(&blobs->lbs_ib, &blob_sizes.lbs_ib);
+	/* inode blob 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_set_blob_size(&blobs->lbs_inode, &blob_sizes.lbs_inode);
+	lsm_set_blob_size(&blobs->lbs_ipc, &blob_sizes.lbs_ipc);
+	lsm_set_blob_size(&blobs->lbs_key, &blob_sizes.lbs_key);
+	lsm_set_blob_size(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
+	lsm_set_blob_size(&blobs->lbs_perf_event, &blob_sizes.lbs_perf_event);
+	lsm_set_blob_size(&blobs->lbs_sock, &blob_sizes.lbs_sock);
+	lsm_set_blob_size(&blobs->lbs_superblock, &blob_sizes.lbs_superblock);
+	lsm_set_blob_size(&blobs->lbs_task, &blob_sizes.lbs_task);
+	lsm_set_blob_size(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
+	lsm_set_blob_size(&blobs->lbs_xattr_count,
+			  &blob_sizes.lbs_xattr_count);
+	lsm_set_blob_size(&blobs->lbs_bdev, &blob_sizes.lbs_bdev);
+	lsm_set_blob_size(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
+	lsm_set_blob_size(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
+	lsm_set_blob_size(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
 }
 
 /* Initialize a given LSM, if it is enabled. */
@@ -361,7 +344,7 @@ static void __init ordered_lsm_init(void)
 		ordered_lsm_parse(builtin_lsm_order, "builtin");
 
 	for (lsm = ordered_lsms; *lsm; lsm++)
-		prepare_lsm(*lsm);
+		lsm_prepare(*lsm);
 
 	report_lsm_order();
 
@@ -505,7 +488,7 @@ int __init early_security_init(void)
 	for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
 		if (!lsm->enabled)
 			lsm->enabled = &lsm_enabled_true;
-		prepare_lsm(lsm);
+		lsm_prepare(lsm);
 		initialize_lsm(lsm);
 	}
 
-- 
2.50.1


^ permalink raw reply related

* [PATCH v3 02/34] lsm: split the init code out into lsm_init.c
From: Paul Moore @ 2025-08-14 22:50 UTC (permalink / raw)
  To: linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
	Mickaël Salaün, Günther Noack, Kees Cook,
	Micah Morton, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
	Xiu Jianfeng
In-Reply-To: <20250814225159.275901-36-paul@paul-moore.com>

Continue to pull code out of security/security.c to help improve
readability by pulling all of the LSM framework initialization
code out into a new file.

No code changes.

Reviewed-by: Kees Cook <kees@kernel.org>
Reviewed-by: John Johansen <john.johansen@canonical.com>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
 include/linux/lsm_hooks.h |   3 +-
 security/Makefile         |   2 +-
 security/lsm.h            |  22 ++
 security/lsm_init.c       | 543 ++++++++++++++++++++++++++++++++++
 security/security.c       | 597 +++-----------------------------------
 5 files changed, 601 insertions(+), 566 deletions(-)
 create mode 100644 security/lsm.h
 create mode 100644 security/lsm_init.c

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 79ec5a2bdcca..0112926ed923 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -170,11 +170,10 @@ struct lsm_info {
 		__used __section(".early_lsm_info.init")		\
 		__aligned(sizeof(unsigned long))
 
+
 /* DO NOT tamper with these variables outside of the LSM framework */
 extern char *lsm_names;
 extern struct lsm_static_calls_table static_calls_table __ro_after_init;
-extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
-extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
 
 /**
  * lsm_get_xattr_slot - Return the next available slot and increment the index
diff --git a/security/Makefile b/security/Makefile
index 14d87847bce8..4601230ba442 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) 			+= lsm_syscalls.o
 obj-$(CONFIG_MMU)			+= min_addr.o
 
 # Object file lists
-obj-$(CONFIG_SECURITY)			+= security.o lsm_notifier.o
+obj-$(CONFIG_SECURITY)			+= security.o lsm_notifier.o lsm_init.o
 obj-$(CONFIG_SECURITYFS)		+= inode.o
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/
diff --git a/security/lsm.h b/security/lsm.h
new file mode 100644
index 000000000000..0e1731bad4a7
--- /dev/null
+++ b/security/lsm.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LSM functions
+ */
+
+#ifndef _LSM_H_
+#define _LSM_H_
+
+#include <linux/lsm_hooks.h>
+
+/* LSM blob configuration */
+extern struct lsm_blob_sizes blob_sizes;
+
+/* LSM blob caches */
+extern struct kmem_cache *lsm_file_cache;
+extern struct kmem_cache *lsm_inode_cache;
+
+/* LSM blob allocators */
+int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
+int lsm_task_alloc(struct task_struct *task);
+
+#endif /* _LSM_H_ */
diff --git a/security/lsm_init.c b/security/lsm_init.c
new file mode 100644
index 000000000000..124213b906af
--- /dev/null
+++ b/security/lsm_init.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LSM initialization functions
+ */
+
+#define pr_fmt(fmt) "LSM: " fmt
+
+#include <linux/init.h>
+#include <linux/lsm_hooks.h>
+
+#include "lsm.h"
+
+char *lsm_names;
+
+/* Pointers to LSM sections defined in include/asm-generic/vmlinux.lds.h */
+extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
+extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
+
+/* Boot-time LSM user choice */
+static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
+static __initdata const char *chosen_lsm_order;
+static __initdata const char *chosen_major_lsm;
+
+/* Ordered list of LSMs to initialize. */
+static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
+static __initdata struct lsm_info *exclusive;
+
+static __initdata bool debug;
+#define init_debug(...)							\
+	do {								\
+		if (debug)						\
+			pr_info(__VA_ARGS__);				\
+	} while (0)
+
+static int lsm_append(const char *new, char **result);
+
+/* Save user chosen LSM */
+static int __init choose_major_lsm(char *str)
+{
+	chosen_major_lsm = str;
+	return 1;
+}
+__setup("security=", choose_major_lsm);
+
+/* Explicitly choose LSM initialization order. */
+static int __init choose_lsm_order(char *str)
+{
+	chosen_lsm_order = str;
+	return 1;
+}
+__setup("lsm=", choose_lsm_order);
+
+/* Enable LSM order debugging. */
+static int __init enable_debug(char *str)
+{
+	debug = true;
+	return 1;
+}
+__setup("lsm.debug", enable_debug);
+
+/* Mark an LSM's enabled flag. */
+static int lsm_enabled_true __initdata = 1;
+static int lsm_enabled_false __initdata = 0;
+static void __init set_enabled(struct lsm_info *lsm, bool enabled)
+{
+	/*
+	 * When an LSM hasn't configured an enable variable, we can use
+	 * a hard-coded location for storing the default enabled state.
+	 */
+	if (!lsm->enabled) {
+		if (enabled)
+			lsm->enabled = &lsm_enabled_true;
+		else
+			lsm->enabled = &lsm_enabled_false;
+	} else if (lsm->enabled == &lsm_enabled_true) {
+		if (!enabled)
+			lsm->enabled = &lsm_enabled_false;
+	} else if (lsm->enabled == &lsm_enabled_false) {
+		if (enabled)
+			lsm->enabled = &lsm_enabled_true;
+	} else {
+		*lsm->enabled = enabled;
+	}
+}
+
+static inline bool is_enabled(struct lsm_info *lsm)
+{
+	if (!lsm->enabled)
+		return false;
+
+	return *lsm->enabled;
+}
+
+/* Is an LSM already listed in the ordered LSMs list? */
+static bool __init exists_ordered_lsm(struct lsm_info *lsm)
+{
+	struct lsm_info **check;
+
+	for (check = ordered_lsms; *check; check++)
+		if (*check == lsm)
+			return true;
+
+	return false;
+}
+
+/* Append an LSM to the list of ordered LSMs to initialize. */
+static int last_lsm __initdata;
+static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
+{
+	/* Ignore duplicate selections. */
+	if (exists_ordered_lsm(lsm))
+		return;
+
+	if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
+		return;
+
+	/* Enable this LSM, if it is not already set. */
+	if (!lsm->enabled)
+		lsm->enabled = &lsm_enabled_true;
+	ordered_lsms[last_lsm++] = lsm;
+
+	init_debug("%s ordered: %s (%s)\n", from, lsm->name,
+		   is_enabled(lsm) ? "enabled" : "disabled");
+}
+
+/* Is an LSM allowed to be initialized? */
+static bool __init lsm_allowed(struct lsm_info *lsm)
+{
+	/* Skip if the LSM is disabled. */
+	if (!is_enabled(lsm))
+		return false;
+
+	/* Not allowed if another exclusive LSM already initialized. */
+	if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
+		init_debug("exclusive disabled: %s\n", lsm->name);
+		return false;
+	}
+
+	return true;
+}
+
+static void __init lsm_set_blob_size(int *need, int *lbs)
+{
+	int offset;
+
+	if (*need <= 0)
+		return;
+
+	offset = ALIGN(*lbs, sizeof(void *));
+	*lbs = offset + *need;
+	*need = offset;
+}
+
+static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
+{
+	if (!needed)
+		return;
+
+	lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
+	lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
+	lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
+	/*
+	 * The inode blob gets an rcu_head in addition to
+	 * what the modules might need.
+	 */
+	if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
+		blob_sizes.lbs_inode = sizeof(struct rcu_head);
+	lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
+	lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
+	lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
+	lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
+	lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
+	lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
+	lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
+	lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
+	lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
+	lsm_set_blob_size(&needed->lbs_xattr_count,
+			  &blob_sizes.lbs_xattr_count);
+	lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
+	lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
+	lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
+	lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
+}
+
+/* Prepare LSM for initialization. */
+static void __init prepare_lsm(struct lsm_info *lsm)
+{
+	int enabled = lsm_allowed(lsm);
+
+	/* Record enablement (to handle any following exclusive LSMs). */
+	set_enabled(lsm, enabled);
+
+	/* If enabled, do pre-initialization work. */
+	if (enabled) {
+		if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
+			exclusive = lsm;
+			init_debug("exclusive chosen:   %s\n", lsm->name);
+		}
+
+		lsm_set_blob_sizes(lsm->blobs);
+	}
+}
+
+/* Initialize a given LSM, if it is enabled. */
+static void __init initialize_lsm(struct lsm_info *lsm)
+{
+	if (is_enabled(lsm)) {
+		int ret;
+
+		init_debug("initializing %s\n", lsm->name);
+		ret = lsm->init();
+		WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
+	}
+}
+
+/*
+ * Current index to use while initializing the lsm id list.
+ */
+u32 lsm_active_cnt __ro_after_init;
+const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
+
+/* Populate ordered LSMs list from comma-separated LSM name list. */
+static void __init ordered_lsm_parse(const char *order, const char *origin)
+{
+	struct lsm_info *lsm;
+	char *sep, *name, *next;
+
+	/* LSM_ORDER_FIRST is always first. */
+	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+		if (lsm->order == LSM_ORDER_FIRST)
+			append_ordered_lsm(lsm, "  first");
+	}
+
+	/* Process "security=", if given. */
+	if (chosen_major_lsm) {
+		struct lsm_info *major;
+
+		/*
+		 * To match the original "security=" behavior, this
+		 * explicitly does NOT fallback to another Legacy Major
+		 * if the selected one was separately disabled: disable
+		 * all non-matching Legacy Major LSMs.
+		 */
+		for (major = __start_lsm_info; major < __end_lsm_info;
+		     major++) {
+			if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
+			    strcmp(major->name, chosen_major_lsm) != 0) {
+				set_enabled(major, false);
+				init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
+					   chosen_major_lsm, major->name);
+			}
+		}
+	}
+
+	sep = kstrdup(order, GFP_KERNEL);
+	next = sep;
+	/* Walk the list, looking for matching LSMs. */
+	while ((name = strsep(&next, ",")) != NULL) {
+		bool found = false;
+
+		for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+			if (strcmp(lsm->name, name) == 0) {
+				if (lsm->order == LSM_ORDER_MUTABLE)
+					append_ordered_lsm(lsm, origin);
+				found = true;
+			}
+		}
+
+		if (!found)
+			init_debug("%s ignored: %s (not built into kernel)\n",
+				   origin, name);
+	}
+
+	/* Process "security=", if given. */
+	if (chosen_major_lsm) {
+		for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+			if (exists_ordered_lsm(lsm))
+				continue;
+			if (strcmp(lsm->name, chosen_major_lsm) == 0)
+				append_ordered_lsm(lsm, "security=");
+		}
+	}
+
+	/* LSM_ORDER_LAST is always last. */
+	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+		if (lsm->order == LSM_ORDER_LAST)
+			append_ordered_lsm(lsm, "   last");
+	}
+
+	/* Disable all LSMs not in the ordered list. */
+	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+		if (exists_ordered_lsm(lsm))
+			continue;
+		set_enabled(lsm, false);
+		init_debug("%s skipped: %s (not in requested order)\n",
+			   origin, lsm->name);
+	}
+
+	kfree(sep);
+}
+
+static void __init report_lsm_order(void)
+{
+	struct lsm_info **lsm, *early;
+	int first = 0;
+
+	pr_info("initializing lsm=");
+
+	/* Report each enabled LSM name, comma separated. */
+	for (early = __start_early_lsm_info;
+	     early < __end_early_lsm_info; early++)
+		if (is_enabled(early))
+			pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
+	for (lsm = ordered_lsms; *lsm; lsm++)
+		if (is_enabled(*lsm))
+			pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
+
+	pr_cont("\n");
+}
+
+/**
+ * lsm_early_cred - during initialization allocate a composite cred blob
+ * @cred: the cred that needs a blob
+ *
+ * Allocate the cred blob for all the modules
+ */
+static void __init lsm_early_cred(struct cred *cred)
+{
+	int rc = lsm_cred_alloc(cred, GFP_KERNEL);
+
+	if (rc)
+		panic("%s: Early cred alloc failed.\n", __func__);
+}
+
+/**
+ * lsm_early_task - during initialization allocate a composite task blob
+ * @task: the task that needs a blob
+ *
+ * Allocate the task blob for all the modules
+ */
+static void __init lsm_early_task(struct task_struct *task)
+{
+	int rc = lsm_task_alloc(task);
+
+	if (rc)
+		panic("%s: Early task alloc failed.\n", __func__);
+}
+
+static void __init ordered_lsm_init(void)
+{
+	struct lsm_info **lsm;
+
+	if (chosen_lsm_order) {
+		if (chosen_major_lsm) {
+			pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
+				chosen_major_lsm, chosen_lsm_order);
+			chosen_major_lsm = NULL;
+		}
+		ordered_lsm_parse(chosen_lsm_order, "cmdline");
+	} else
+		ordered_lsm_parse(builtin_lsm_order, "builtin");
+
+	for (lsm = ordered_lsms; *lsm; lsm++)
+		prepare_lsm(*lsm);
+
+	report_lsm_order();
+
+	init_debug("cred blob size       = %d\n", blob_sizes.lbs_cred);
+	init_debug("file blob size       = %d\n", blob_sizes.lbs_file);
+	init_debug("ib blob size         = %d\n", blob_sizes.lbs_ib);
+	init_debug("inode blob size      = %d\n", blob_sizes.lbs_inode);
+	init_debug("ipc blob size        = %d\n", blob_sizes.lbs_ipc);
+#ifdef CONFIG_KEYS
+	init_debug("key blob size        = %d\n", blob_sizes.lbs_key);
+#endif /* CONFIG_KEYS */
+	init_debug("msg_msg blob size    = %d\n", blob_sizes.lbs_msg_msg);
+	init_debug("sock blob size       = %d\n", blob_sizes.lbs_sock);
+	init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
+	init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
+	init_debug("task blob size       = %d\n", blob_sizes.lbs_task);
+	init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
+	init_debug("xattr slots          = %d\n", blob_sizes.lbs_xattr_count);
+	init_debug("bdev blob size       = %d\n", blob_sizes.lbs_bdev);
+	init_debug("bpf map blob size    = %d\n", blob_sizes.lbs_bpf_map);
+	init_debug("bpf prog blob size   = %d\n", blob_sizes.lbs_bpf_prog);
+	init_debug("bpf token blob size  = %d\n", blob_sizes.lbs_bpf_token);
+
+	/*
+	 * Create any kmem_caches needed for blobs
+	 */
+	if (blob_sizes.lbs_file)
+		lsm_file_cache = kmem_cache_create("lsm_file_cache",
+						   blob_sizes.lbs_file, 0,
+						   SLAB_PANIC, NULL);
+	if (blob_sizes.lbs_inode)
+		lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
+						    blob_sizes.lbs_inode, 0,
+						    SLAB_PANIC, NULL);
+
+	lsm_early_cred((struct cred *) current->cred);
+	lsm_early_task(current);
+	for (lsm = ordered_lsms; *lsm; lsm++)
+		initialize_lsm(*lsm);
+}
+
+static bool match_last_lsm(const char *list, const char *lsm)
+{
+	const char *last;
+
+	if (WARN_ON(!list || !lsm))
+		return false;
+	last = strrchr(list, ',');
+	if (last)
+		/* Pass the comma, strcmp() will check for '\0' */
+		last++;
+	else
+		last = list;
+	return !strcmp(last, lsm);
+}
+
+static int lsm_append(const char *new, char **result)
+{
+	char *cp;
+
+	if (*result == NULL) {
+		*result = kstrdup(new, GFP_KERNEL);
+		if (*result == NULL)
+			return -ENOMEM;
+	} else {
+		/* Check if it is the last registered name */
+		if (match_last_lsm(*result, new))
+			return 0;
+		cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
+		if (cp == NULL)
+			return -ENOMEM;
+		kfree(*result);
+		*result = cp;
+	}
+	return 0;
+}
+
+static void __init lsm_static_call_init(struct security_hook_list *hl)
+{
+	struct lsm_static_call *scall = hl->scalls;
+	int i;
+
+	for (i = 0; i < MAX_LSM_COUNT; i++) {
+		/* Update the first static call that is not used yet */
+		if (!scall->hl) {
+			__static_call_update(scall->key, scall->trampoline,
+					     hl->hook.lsm_func_addr);
+			scall->hl = hl;
+			static_branch_enable(scall->active);
+			return;
+		}
+		scall++;
+	}
+	panic("%s - Ran out of static slots.\n", __func__);
+}
+
+/**
+ * security_add_hooks - Add a modules hooks to the hook lists.
+ * @hooks: the hooks to add
+ * @count: the number of hooks to add
+ * @lsmid: the identification information for the security module
+ *
+ * Each LSM has to register its hooks with the infrastructure.
+ */
+void __init security_add_hooks(struct security_hook_list *hooks, int count,
+			       const struct lsm_id *lsmid)
+{
+	int i;
+
+	/*
+	 * A security module may call security_add_hooks() more
+	 * than once during initialization, and LSM initialization
+	 * is serialized. Landlock is one such case.
+	 * Look at the previous entry, if there is one, for duplication.
+	 */
+	if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
+		if (lsm_active_cnt >= MAX_LSM_COUNT)
+			panic("%s Too many LSMs registered.\n", __func__);
+		lsm_idlist[lsm_active_cnt++] = lsmid;
+	}
+
+	for (i = 0; i < count; i++) {
+		hooks[i].lsmid = lsmid;
+		lsm_static_call_init(&hooks[i]);
+	}
+
+	/*
+	 * Don't try to append during early_security_init(), we'll come back
+	 * and fix this up afterwards.
+	 */
+	if (slab_is_available()) {
+		if (lsm_append(lsmid->name, &lsm_names) < 0)
+			panic("%s - Cannot get early memory.\n", __func__);
+	}
+}
+
+int __init early_security_init(void)
+{
+	struct lsm_info *lsm;
+
+	for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
+		if (!lsm->enabled)
+			lsm->enabled = &lsm_enabled_true;
+		prepare_lsm(lsm);
+		initialize_lsm(lsm);
+	}
+
+	return 0;
+}
+
+/**
+ * security_init - initializes the security framework
+ *
+ * This should be called early in the kernel initialization sequence.
+ */
+int __init security_init(void)
+{
+	struct lsm_info *lsm;
+
+	init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
+	init_debug("  CONFIG_LSM=%s\n", builtin_lsm_order);
+	init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");
+
+	/*
+	 * Append the names of the early LSM modules now that kmalloc() is
+	 * available
+	 */
+	for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
+		init_debug("  early started: %s (%s)\n", lsm->name,
+			   is_enabled(lsm) ? "enabled" : "disabled");
+		if (lsm->enabled)
+			lsm_append(lsm->name, &lsm_names);
+	}
+
+	/* Load LSMs in specified order. */
+	ordered_lsm_init();
+
+	return 0;
+}
diff --git a/security/security.c b/security/security.c
index 8cb049bebc57..ff6da6735e2a 100644
--- a/security/security.c
+++ b/security/security.c
@@ -32,24 +32,7 @@
 #include <net/flow.h>
 #include <net/sock.h>
 
-#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
-
-/*
- * Identifier for the LSM static calls.
- * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
- * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
- */
-#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
-
-/*
- * Call the macro M for each LSM hook MAX_LSM_COUNT times.
- */
-#define LSM_LOOP_UNROLL(M, ...) 		\
-do {						\
-	UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)	\
-} while (0)
-
-#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
+#include "lsm.h"
 
 /*
  * These are descriptions of the reasons that can be passed to the
@@ -90,21 +73,29 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
 	[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
 };
 
-static struct kmem_cache *lsm_file_cache;
-static struct kmem_cache *lsm_inode_cache;
+struct lsm_blob_sizes blob_sizes;
 
-char *lsm_names;
-static struct lsm_blob_sizes blob_sizes __ro_after_init;
+struct kmem_cache *lsm_file_cache;
+struct kmem_cache *lsm_inode_cache;
 
-/* Boot-time LSM user choice */
-static __initdata const char *chosen_lsm_order;
-static __initdata const char *chosen_major_lsm;
+#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
 
-static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
+/*
+ * Identifier for the LSM static calls.
+ * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
+ * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
+ */
+#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
 
-/* Ordered list of LSMs to initialize. */
-static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
-static __initdata struct lsm_info *exclusive;
+/*
+ * Call the macro M for each LSM hook MAX_LSM_COUNT times.
+ */
+#define LSM_LOOP_UNROLL(M, ...) 		\
+do {						\
+	UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)	\
+} while (0)
+
+#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
 
 #ifdef CONFIG_HAVE_STATIC_CALL
 #define LSM_HOOK_TRAMP(NAME, NUM) \
@@ -155,496 +146,25 @@ struct lsm_static_calls_table
 #undef INIT_LSM_STATIC_CALL
 	};
 
-static __initdata bool debug;
-#define init_debug(...)						\
-	do {							\
-		if (debug)					\
-			pr_info(__VA_ARGS__);			\
-	} while (0)
-
-static bool __init is_enabled(struct lsm_info *lsm)
-{
-	if (!lsm->enabled)
-		return false;
-
-	return *lsm->enabled;
-}
-
-/* Mark an LSM's enabled flag. */
-static int lsm_enabled_true __initdata = 1;
-static int lsm_enabled_false __initdata = 0;
-static void __init set_enabled(struct lsm_info *lsm, bool enabled)
-{
-	/*
-	 * When an LSM hasn't configured an enable variable, we can use
-	 * a hard-coded location for storing the default enabled state.
-	 */
-	if (!lsm->enabled) {
-		if (enabled)
-			lsm->enabled = &lsm_enabled_true;
-		else
-			lsm->enabled = &lsm_enabled_false;
-	} else if (lsm->enabled == &lsm_enabled_true) {
-		if (!enabled)
-			lsm->enabled = &lsm_enabled_false;
-	} else if (lsm->enabled == &lsm_enabled_false) {
-		if (enabled)
-			lsm->enabled = &lsm_enabled_true;
-	} else {
-		*lsm->enabled = enabled;
-	}
-}
-
-/* Is an LSM already listed in the ordered LSMs list? */
-static bool __init exists_ordered_lsm(struct lsm_info *lsm)
-{
-	struct lsm_info **check;
-
-	for (check = ordered_lsms; *check; check++)
-		if (*check == lsm)
-			return true;
-
-	return false;
-}
-
-/* Append an LSM to the list of ordered LSMs to initialize. */
-static int last_lsm __initdata;
-static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
-{
-	/* Ignore duplicate selections. */
-	if (exists_ordered_lsm(lsm))
-		return;
-
-	if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
-		return;
-
-	/* Enable this LSM, if it is not already set. */
-	if (!lsm->enabled)
-		lsm->enabled = &lsm_enabled_true;
-	ordered_lsms[last_lsm++] = lsm;
-
-	init_debug("%s ordered: %s (%s)\n", from, lsm->name,
-		   is_enabled(lsm) ? "enabled" : "disabled");
-}
-
-/* Is an LSM allowed to be initialized? */
-static bool __init lsm_allowed(struct lsm_info *lsm)
-{
-	/* Skip if the LSM is disabled. */
-	if (!is_enabled(lsm))
-		return false;
-
-	/* Not allowed if another exclusive LSM already initialized. */
-	if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
-		init_debug("exclusive disabled: %s\n", lsm->name);
-		return false;
-	}
-
-	return true;
-}
-
-static void __init lsm_set_blob_size(int *need, int *lbs)
-{
-	int offset;
-
-	if (*need <= 0)
-		return;
-
-	offset = ALIGN(*lbs, sizeof(void *));
-	*lbs = offset + *need;
-	*need = offset;
-}
-
-static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
-{
-	if (!needed)
-		return;
-
-	lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
-	lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
-	lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
-	/*
-	 * The inode blob gets an rcu_head in addition to
-	 * what the modules might need.
-	 */
-	if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
-		blob_sizes.lbs_inode = sizeof(struct rcu_head);
-	lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
-	lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
-	lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
-	lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
-	lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
-	lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
-	lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
-	lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
-	lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
-	lsm_set_blob_size(&needed->lbs_xattr_count,
-			  &blob_sizes.lbs_xattr_count);
-	lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
-	lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
-	lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
-	lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
-}
-
-/* Prepare LSM for initialization. */
-static void __init prepare_lsm(struct lsm_info *lsm)
-{
-	int enabled = lsm_allowed(lsm);
-
-	/* Record enablement (to handle any following exclusive LSMs). */
-	set_enabled(lsm, enabled);
-
-	/* If enabled, do pre-initialization work. */
-	if (enabled) {
-		if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
-			exclusive = lsm;
-			init_debug("exclusive chosen:   %s\n", lsm->name);
-		}
-
-		lsm_set_blob_sizes(lsm->blobs);
-	}
-}
-
-/* Initialize a given LSM, if it is enabled. */
-static void __init initialize_lsm(struct lsm_info *lsm)
-{
-	if (is_enabled(lsm)) {
-		int ret;
-
-		init_debug("initializing %s\n", lsm->name);
-		ret = lsm->init();
-		WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
-	}
-}
-
-/*
- * Current index to use while initializing the lsm id list.
- */
-u32 lsm_active_cnt __ro_after_init;
-const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
-
-/* Populate ordered LSMs list from comma-separated LSM name list. */
-static void __init ordered_lsm_parse(const char *order, const char *origin)
-{
-	struct lsm_info *lsm;
-	char *sep, *name, *next;
-
-	/* LSM_ORDER_FIRST is always first. */
-	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
-		if (lsm->order == LSM_ORDER_FIRST)
-			append_ordered_lsm(lsm, "  first");
-	}
-
-	/* Process "security=", if given. */
-	if (chosen_major_lsm) {
-		struct lsm_info *major;
-
-		/*
-		 * To match the original "security=" behavior, this
-		 * explicitly does NOT fallback to another Legacy Major
-		 * if the selected one was separately disabled: disable
-		 * all non-matching Legacy Major LSMs.
-		 */
-		for (major = __start_lsm_info; major < __end_lsm_info;
-		     major++) {
-			if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
-			    strcmp(major->name, chosen_major_lsm) != 0) {
-				set_enabled(major, false);
-				init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
-					   chosen_major_lsm, major->name);
-			}
-		}
-	}
-
-	sep = kstrdup(order, GFP_KERNEL);
-	next = sep;
-	/* Walk the list, looking for matching LSMs. */
-	while ((name = strsep(&next, ",")) != NULL) {
-		bool found = false;
-
-		for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
-			if (strcmp(lsm->name, name) == 0) {
-				if (lsm->order == LSM_ORDER_MUTABLE)
-					append_ordered_lsm(lsm, origin);
-				found = true;
-			}
-		}
-
-		if (!found)
-			init_debug("%s ignored: %s (not built into kernel)\n",
-				   origin, name);
-	}
-
-	/* Process "security=", if given. */
-	if (chosen_major_lsm) {
-		for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
-			if (exists_ordered_lsm(lsm))
-				continue;
-			if (strcmp(lsm->name, chosen_major_lsm) == 0)
-				append_ordered_lsm(lsm, "security=");
-		}
-	}
-
-	/* LSM_ORDER_LAST is always last. */
-	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
-		if (lsm->order == LSM_ORDER_LAST)
-			append_ordered_lsm(lsm, "   last");
-	}
-
-	/* Disable all LSMs not in the ordered list. */
-	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
-		if (exists_ordered_lsm(lsm))
-			continue;
-		set_enabled(lsm, false);
-		init_debug("%s skipped: %s (not in requested order)\n",
-			   origin, lsm->name);
-	}
-
-	kfree(sep);
-}
-
-static void __init lsm_static_call_init(struct security_hook_list *hl)
-{
-	struct lsm_static_call *scall = hl->scalls;
-	int i;
-
-	for (i = 0; i < MAX_LSM_COUNT; i++) {
-		/* Update the first static call that is not used yet */
-		if (!scall->hl) {
-			__static_call_update(scall->key, scall->trampoline,
-					     hl->hook.lsm_func_addr);
-			scall->hl = hl;
-			static_branch_enable(scall->active);
-			return;
-		}
-		scall++;
-	}
-	panic("%s - Ran out of static slots.\n", __func__);
-}
-
-static void __init lsm_early_cred(struct cred *cred);
-static void __init lsm_early_task(struct task_struct *task);
-
-static int lsm_append(const char *new, char **result);
-
-static void __init report_lsm_order(void)
-{
-	struct lsm_info **lsm, *early;
-	int first = 0;
-
-	pr_info("initializing lsm=");
-
-	/* Report each enabled LSM name, comma separated. */
-	for (early = __start_early_lsm_info;
-	     early < __end_early_lsm_info; early++)
-		if (is_enabled(early))
-			pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
-	for (lsm = ordered_lsms; *lsm; lsm++)
-		if (is_enabled(*lsm))
-			pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
-
-	pr_cont("\n");
-}
-
-static void __init ordered_lsm_init(void)
-{
-	struct lsm_info **lsm;
-
-	if (chosen_lsm_order) {
-		if (chosen_major_lsm) {
-			pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
-				chosen_major_lsm, chosen_lsm_order);
-			chosen_major_lsm = NULL;
-		}
-		ordered_lsm_parse(chosen_lsm_order, "cmdline");
-	} else
-		ordered_lsm_parse(builtin_lsm_order, "builtin");
-
-	for (lsm = ordered_lsms; *lsm; lsm++)
-		prepare_lsm(*lsm);
-
-	report_lsm_order();
-
-	init_debug("cred blob size       = %d\n", blob_sizes.lbs_cred);
-	init_debug("file blob size       = %d\n", blob_sizes.lbs_file);
-	init_debug("ib blob size         = %d\n", blob_sizes.lbs_ib);
-	init_debug("inode blob size      = %d\n", blob_sizes.lbs_inode);
-	init_debug("ipc blob size        = %d\n", blob_sizes.lbs_ipc);
-#ifdef CONFIG_KEYS
-	init_debug("key blob size        = %d\n", blob_sizes.lbs_key);
-#endif /* CONFIG_KEYS */
-	init_debug("msg_msg blob size    = %d\n", blob_sizes.lbs_msg_msg);
-	init_debug("sock blob size       = %d\n", blob_sizes.lbs_sock);
-	init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
-	init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
-	init_debug("task blob size       = %d\n", blob_sizes.lbs_task);
-	init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
-	init_debug("xattr slots          = %d\n", blob_sizes.lbs_xattr_count);
-	init_debug("bdev blob size       = %d\n", blob_sizes.lbs_bdev);
-	init_debug("bpf map blob size    = %d\n", blob_sizes.lbs_bpf_map);
-	init_debug("bpf prog blob size   = %d\n", blob_sizes.lbs_bpf_prog);
-	init_debug("bpf token blob size  = %d\n", blob_sizes.lbs_bpf_token);
-
-	/*
-	 * Create any kmem_caches needed for blobs
-	 */
-	if (blob_sizes.lbs_file)
-		lsm_file_cache = kmem_cache_create("lsm_file_cache",
-						   blob_sizes.lbs_file, 0,
-						   SLAB_PANIC, NULL);
-	if (blob_sizes.lbs_inode)
-		lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
-						    blob_sizes.lbs_inode, 0,
-						    SLAB_PANIC, NULL);
-
-	lsm_early_cred((struct cred *) current->cred);
-	lsm_early_task(current);
-	for (lsm = ordered_lsms; *lsm; lsm++)
-		initialize_lsm(*lsm);
-}
-
-int __init early_security_init(void)
-{
-	struct lsm_info *lsm;
-
-	for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
-		if (!lsm->enabled)
-			lsm->enabled = &lsm_enabled_true;
-		prepare_lsm(lsm);
-		initialize_lsm(lsm);
-	}
-
-	return 0;
-}
-
 /**
- * security_init - initializes the security framework
+ * lsm_file_alloc - allocate a composite file blob
+ * @file: the file that needs a blob
  *
- * This should be called early in the kernel initialization sequence.
- */
-int __init security_init(void)
-{
-	struct lsm_info *lsm;
-
-	init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
-	init_debug("  CONFIG_LSM=%s\n", builtin_lsm_order);
-	init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");
-
-	/*
-	 * Append the names of the early LSM modules now that kmalloc() is
-	 * available
-	 */
-	for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
-		init_debug("  early started: %s (%s)\n", lsm->name,
-			   is_enabled(lsm) ? "enabled" : "disabled");
-		if (lsm->enabled)
-			lsm_append(lsm->name, &lsm_names);
-	}
-
-	/* Load LSMs in specified order. */
-	ordered_lsm_init();
-
-	return 0;
-}
-
-/* Save user chosen LSM */
-static int __init choose_major_lsm(char *str)
-{
-	chosen_major_lsm = str;
-	return 1;
-}
-__setup("security=", choose_major_lsm);
-
-/* Explicitly choose LSM initialization order. */
-static int __init choose_lsm_order(char *str)
-{
-	chosen_lsm_order = str;
-	return 1;
-}
-__setup("lsm=", choose_lsm_order);
-
-/* Enable LSM order debugging. */
-static int __init enable_debug(char *str)
-{
-	debug = true;
-	return 1;
-}
-__setup("lsm.debug", enable_debug);
-
-static bool match_last_lsm(const char *list, const char *lsm)
-{
-	const char *last;
-
-	if (WARN_ON(!list || !lsm))
-		return false;
-	last = strrchr(list, ',');
-	if (last)
-		/* Pass the comma, strcmp() will check for '\0' */
-		last++;
-	else
-		last = list;
-	return !strcmp(last, lsm);
-}
-
-static int lsm_append(const char *new, char **result)
-{
-	char *cp;
-
-	if (*result == NULL) {
-		*result = kstrdup(new, GFP_KERNEL);
-		if (*result == NULL)
-			return -ENOMEM;
-	} else {
-		/* Check if it is the last registered name */
-		if (match_last_lsm(*result, new))
-			return 0;
-		cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
-		if (cp == NULL)
-			return -ENOMEM;
-		kfree(*result);
-		*result = cp;
-	}
-	return 0;
-}
-
-/**
- * security_add_hooks - Add a modules hooks to the hook lists.
- * @hooks: the hooks to add
- * @count: the number of hooks to add
- * @lsmid: the identification information for the security module
+ * Allocate the file blob for all the modules
  *
- * Each LSM has to register its hooks with the infrastructure.
+ * Returns 0, or -ENOMEM if memory can't be allocated.
  */
-void __init security_add_hooks(struct security_hook_list *hooks, int count,
-			       const struct lsm_id *lsmid)
+static int lsm_file_alloc(struct file *file)
 {
-	int i;
-
-	/*
-	 * A security module may call security_add_hooks() more
-	 * than once during initialization, and LSM initialization
-	 * is serialized. Landlock is one such case.
-	 * Look at the previous entry, if there is one, for duplication.
-	 */
-	if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
-		if (lsm_active_cnt >= MAX_LSM_COUNT)
-			panic("%s Too many LSMs registered.\n", __func__);
-		lsm_idlist[lsm_active_cnt++] = lsmid;
+	if (!lsm_file_cache) {
+		file->f_security = NULL;
+		return 0;
 	}
 
-	for (i = 0; i < count; i++) {
-		hooks[i].lsmid = lsmid;
-		lsm_static_call_init(&hooks[i]);
-	}
-
-	/*
-	 * Don't try to append during early_security_init(), we'll come back
-	 * and fix this up afterwards.
-	 */
-	if (slab_is_available()) {
-		if (lsm_append(lsmid->name, &lsm_names) < 0)
-			panic("%s - Cannot get early memory.\n", __func__);
-	}
+	file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
+	if (file->f_security == NULL)
+		return -ENOMEM;
+	return 0;
 }
 
 /**
@@ -679,46 +199,11 @@ static int lsm_blob_alloc(void **dest, size_t size, gfp_t gfp)
  *
  * Returns 0, or -ENOMEM if memory can't be allocated.
  */
-static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
+int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
 {
 	return lsm_blob_alloc(&cred->security, blob_sizes.lbs_cred, gfp);
 }
 
-/**
- * lsm_early_cred - during initialization allocate a composite cred blob
- * @cred: the cred that needs a blob
- *
- * Allocate the cred blob for all the modules
- */
-static void __init lsm_early_cred(struct cred *cred)
-{
-	int rc = lsm_cred_alloc(cred, GFP_KERNEL);
-
-	if (rc)
-		panic("%s: Early cred alloc failed.\n", __func__);
-}
-
-/**
- * lsm_file_alloc - allocate a composite file blob
- * @file: the file that needs a blob
- *
- * Allocate the file blob for all the modules
- *
- * Returns 0, or -ENOMEM if memory can't be allocated.
- */
-static int lsm_file_alloc(struct file *file)
-{
-	if (!lsm_file_cache) {
-		file->f_security = NULL;
-		return 0;
-	}
-
-	file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
-	if (file->f_security == NULL)
-		return -ENOMEM;
-	return 0;
-}
-
 /**
  * lsm_inode_alloc - allocate a composite inode blob
  * @inode: the inode that needs a blob
@@ -749,7 +234,7 @@ static int lsm_inode_alloc(struct inode *inode, gfp_t gfp)
  *
  * Returns 0, or -ENOMEM if memory can't be allocated.
  */
-static int lsm_task_alloc(struct task_struct *task)
+int lsm_task_alloc(struct task_struct *task)
 {
 	return lsm_blob_alloc(&task->security, blob_sizes.lbs_task, GFP_KERNEL);
 }
@@ -851,20 +336,6 @@ static int lsm_bpf_token_alloc(struct bpf_token *token)
 }
 #endif /* CONFIG_BPF_SYSCALL */
 
-/**
- * lsm_early_task - during initialization allocate a composite task blob
- * @task: the task that needs a blob
- *
- * Allocate the task blob for all the modules
- */
-static void __init lsm_early_task(struct task_struct *task)
-{
-	int rc = lsm_task_alloc(task);
-
-	if (rc)
-		panic("%s: Early task alloc failed.\n", __func__);
-}
-
 /**
  * lsm_superblock_alloc - allocate a composite superblock blob
  * @sb: the superblock that needs a blob
-- 
2.50.1


^ permalink raw reply related

* [PATCH v3 01/34] lsm: split the notifier code out into lsm_notifier.c
From: Paul Moore @ 2025-08-14 22:50 UTC (permalink / raw)
  To: linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
	Mickaël Salaün, Günther Noack, Kees Cook,
	Micah Morton, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
	Xiu Jianfeng
In-Reply-To: <20250814225159.275901-36-paul@paul-moore.com>

In an effort to decompose security/security.c somewhat to make it less
twisted and unwieldy, pull out the LSM notifier code into a new file
as it is fairly well self-contained.

No code changes.

Reviewed-by: Kees Cook <kees@kernel.org>
Reviewed-by: John Johansen <john.johansen@canonical.com>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
 security/Makefile       |  2 +-
 security/lsm_notifier.c | 31 +++++++++++++++++++++++++++++++
 security/security.c     | 23 -----------------------
 3 files changed, 32 insertions(+), 24 deletions(-)
 create mode 100644 security/lsm_notifier.c

diff --git a/security/Makefile b/security/Makefile
index 22ff4c8bd8ce..14d87847bce8 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) 			+= lsm_syscalls.o
 obj-$(CONFIG_MMU)			+= min_addr.o
 
 # Object file lists
-obj-$(CONFIG_SECURITY)			+= security.o
+obj-$(CONFIG_SECURITY)			+= security.o lsm_notifier.o
 obj-$(CONFIG_SECURITYFS)		+= inode.o
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/
diff --git a/security/lsm_notifier.c b/security/lsm_notifier.c
new file mode 100644
index 000000000000..c92fad5d57d4
--- /dev/null
+++ b/security/lsm_notifier.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LSM notifier functions
+ *
+ */
+
+#include <linux/notifier.h>
+#include <linux/security.h>
+
+static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
+
+int call_blocking_lsm_notifier(enum lsm_event event, void *data)
+{
+	return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
+					    event, data);
+}
+EXPORT_SYMBOL(call_blocking_lsm_notifier);
+
+int register_blocking_lsm_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
+						nb);
+}
+EXPORT_SYMBOL(register_blocking_lsm_notifier);
+
+int unregister_blocking_lsm_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
+						  nb);
+}
+EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
diff --git a/security/security.c b/security/security.c
index ca126b02d2fe..8cb049bebc57 100644
--- a/security/security.c
+++ b/security/security.c
@@ -90,8 +90,6 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
 	[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
 };
 
-static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
-
 static struct kmem_cache *lsm_file_cache;
 static struct kmem_cache *lsm_inode_cache;
 
@@ -649,27 +647,6 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
 	}
 }
 
-int call_blocking_lsm_notifier(enum lsm_event event, void *data)
-{
-	return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
-					    event, data);
-}
-EXPORT_SYMBOL(call_blocking_lsm_notifier);
-
-int register_blocking_lsm_notifier(struct notifier_block *nb)
-{
-	return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
-						nb);
-}
-EXPORT_SYMBOL(register_blocking_lsm_notifier);
-
-int unregister_blocking_lsm_notifier(struct notifier_block *nb)
-{
-	return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
-						  nb);
-}
-EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
-
 /**
  * lsm_blob_alloc - allocate a composite blob
  * @dest: the destination for the blob
-- 
2.50.1


^ permalink raw reply related

* [RFC PATCH v3 0/34] Rework the LSM initialization
From: Paul Moore @ 2025-08-14 22:50 UTC (permalink / raw)
  To: linux-security-module, linux-integrity, selinux
  Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
	Mickaël Salaün, Günther Noack, Kees Cook,
	Micah Morton, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
	Xiu Jianfeng

The third revision of the LSM initialization rework patchset.  While
there was a good deal of change between RFC/v1 and RFC/v2, the number
of changes in the v3 revision are relatively minor; I think the biggest
change is simply bringing it up to date with the current lsm/dev branch.

Please take a look if you haven't already, and to my fellow LSM
maintainers, please focus on the patch associated with your LSM and
either send me an ACK or reply back with the changes you would like to
see before this is merged.  I think we're in a good place with this
patchset, and I'd like to get this merged during this dev cycle if
possible.

The RFC/v2 patchset:
https://lore.kernel.org/linux-security-module/20250721232142.77224-36-paul@paul-moore.com/

The RFC/v1 patchset is below, the cover letter provides some background
and motivation for this series which still applies:
https://lore.kernel.org/linux-security-module/20250409185019.238841-31-paul@paul-moore.com/

CHANGELOG
v3:
- rebased to lsm/dev branch
- fixed IMA/EVM initcall comment (Roberto)
- fixed CONFIG_IMA and CONFIG_EVM problems (Nicolas, Roberto)
- fixed CONFIG_SECURITY_SMACK_NETFILTER problems (Roberto)
- fixed the IMA/EVM header file include macro protections
- fixed an off-by-one string length issue in lsm_read() (Casey)
RFC/v2:
- rename lsm_prep_single() to lsm_prepare()
- drop the lsm_prop counting patch
- drop the platform_certs changes from the IMA/EVM patch (Mimi)
- split/reorder anough patches in the patchset that I lost track
- added missing function comment blocks in the SELinux patches
- split patch 04/29 into smaller patches (Kees)
- fix an LSM list output problem in an intermediate patch (Kees)
- preserve the "lsm_active_cnt" variable name (Casey)
- cache the lsm_read() string (Kees)
- squashed, split, and reordered the enabled/ordering patches
- reworked the Smack patch (Casey)
- conditionalized the SELinux IB init code (Stephen)
- fixed missing Smack "__init" annotation (Fan)
- fixed a potential unused variable warning in IMA/EVM (John)
- fixed the placeholder commit descriptions (various)
RFC/v1:
- initial version

--
Paul Moore (34):
      lsm: split the notifier code out into lsm_notifier.c
      lsm: split the init code out into lsm_init.c
      lsm: consolidate lsm_allowed() and prepare_lsm() into
         lsm_prepare()
      lsm: introduce looping macros for the initialization code
      lsm: integrate report_lsm_order() code into caller
      lsm: integrate lsm_early_cred() and lsm_early_task() into caller
      lsm: rename ordered_lsm_init() to lsm_init_ordered()
      lsm: replace the name field with a pointer to the lsm_id struct
      lsm: rename the lsm order variables for consistency
      lsm: rework lsm_active_cnt and lsm_idlist[]
      lsm: get rid of the lsm_names list and do some cleanup
      lsm: rework the LSM enable/disable setter/getter functions
      lsm: rename exists_ordered_lsm() to lsm_order_exists()
      lsm: rename/rework append_ordered_lsm() into lsm_order_append()
      lsm: rename/rework ordered_lsm_parse() to lsm_order_parse()
      lsm: cleanup the LSM blob size code
      lsm: cleanup initialize_lsm() and rename to lsm_init_single()
      lsm: fold lsm_init_ordered() into security_init()
      lsm: add/tweak function header comment blocks in lsm_init.c
      lsm: cleanup the debug and console output in lsm_init.c
      lsm: output available LSMs when debugging
      lsm: group lsm_order_parse() with the other lsm_order_*()
         functions
      lsm: introduce an initcall mechanism into the LSM framework
      loadpin: move initcalls to the LSM framework
      ipe: move initcalls to the LSM framework
      smack: move initcalls to the LSM framework
      tomoyo: move initcalls to the LSM framework
      safesetid: move initcalls to the LSM framework
      apparmor: move initcalls to the LSM framework
      lockdown: move initcalls to the LSM framework
      ima,evm: move initcalls to the LSM framework
      selinux: move initcalls to the LSM framework
      lsm: consolidate all of the LSM framework initcalls
      lsm: add a LSM_STARTED_ALL notification event

 include/linux/lsm_hooks.h              |   73 +-
 include/linux/security.h               |    3 
 security/Makefile                      |    2 
 security/apparmor/apparmorfs.c         |    4 
 security/apparmor/crypto.c             |    3 
 security/apparmor/include/apparmorfs.h |    2 
 security/apparmor/include/crypto.h     |    1 
 security/apparmor/lsm.c                |   11 
 security/bpf/hooks.c                   |    2 
 security/commoncap.c                   |    2 
 security/inode.c                       |   62 ++
 security/integrity/Makefile            |    2 
 security/integrity/evm/evm_main.c      |    8 
 security/integrity/iint.c              |    4 
 security/integrity/ima/ima_main.c      |    8 
 security/integrity/initcalls.c         |   41 +
 security/integrity/initcalls.h         |   28 +
 security/ipe/fs.c                      |    4 
 security/ipe/ipe.c                     |    3 
 security/ipe/ipe.h                     |    2 
 security/landlock/setup.c              |    2 
 security/loadpin/loadpin.c             |   15 
 security/lockdown/lockdown.c           |    5 
 security/lsm.h                         |   42 +
 security/lsm_init.c                    |  563 ++++++++++++++++++++++
 security/lsm_notifier.c                |   31 +
 security/lsm_syscalls.c                |    2 
 security/min_addr.c                    |    5 
 security/safesetid/lsm.c               |    3 
 security/safesetid/lsm.h               |    2 
 security/safesetid/securityfs.c        |    3 
 security/security.c                    |  623 +------------------------
 security/selinux/Makefile              |    2 
 security/selinux/hooks.c               |   11 
 security/selinux/ibpkey.c              |    5 
 security/selinux/include/audit.h       |    9 
 security/selinux/include/initcalls.h   |   19 
 security/selinux/initcalls.c           |   52 ++
 security/selinux/netif.c               |    5 
 security/selinux/netlink.c             |    5 
 security/selinux/netnode.c             |    5 
 security/selinux/netport.c             |    5 
 security/selinux/selinuxfs.c           |    5 
 security/selinux/ss/services.c         |   26 -
 security/smack/smack.h                 |   14 
 security/smack/smack_lsm.c             |   11 
 security/smack/smack_netfilter.c       |    4 
 security/smack/smackfs.c               |    4 
 security/tomoyo/common.h               |    2 
 security/tomoyo/securityfs_if.c        |    4 
 security/tomoyo/tomoyo.c               |    3 
 security/yama/yama_lsm.c               |    2 
 52 files changed, 1043 insertions(+), 711 deletions(-)


^ permalink raw reply

* Re: [PATCH v3 07/11] rust: security: replace `core::mem::zeroed` with `pin_init::zeroed`
From: Paul Moore @ 2025-08-14 22:01 UTC (permalink / raw)
  To: Benno Lossin
  Cc: Miguel Ojeda, Alice Ryhl, Miguel Ojeda, Alex Gaynor, Boqun Feng,
	Gary Guo, Björn Roy Baron, Andreas Hindborg, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Jocelyn Falempe, rust-for-linux,
	linux-kernel, linux-security-module
In-Reply-To: <DC2BTIG40SRU.16QBMDH0PP01Q@kernel.org>

On Thu, Aug 14, 2025 at 1:28 PM Benno Lossin <lossin@kernel.org> wrote:
> On Thu Aug 14, 2025 at 5:54 PM CEST, Paul Moore wrote:
> > On Thu, Aug 14, 2025 at 11:31 AM Miguel Ojeda
> > <miguel.ojeda.sandonis@gmail.com> wrote:
> >> On Thu, Aug 14, 2025 at 5:19 PM Paul Moore <paul@paul-moore.com> wrote:
> >> >
> >> > I'm happy to take this via the LSM tree, but it would be nice to see a
> >> > Reviewed-by/Acked-by from someone with a better understanding of Rust
> >> > :)
> >>
> >> I think the idea is to take all these through the Rust one with
> >> Acked-bys from the maintainers (or we can skip this one and do it in a
> >> future cycle when the first patches get in).
> >
> > [CC'd the LSM list, as I just realized it wasn't on the original patch
> > posting; in the future please include the LSM list on LSM related Rust
> > patchsets/patches]
>
> I checked and I didn't find a maintainers entry for that this file & the
> LSM list. I'm using scripts/get_maintainer.pl to get the people I send
> patches to and that also checks git commits, so I guess it added you
> through that (which is very good :). So can we add a maintainers entry
> for `rust/kernel/security.rs` so people don't miss this in the future?
> Thanks!

Here ya go ...

https://lore.kernel.org/linux-security-module/20250814215952.238316-2-paul@paul-moore.com/

-- 
paul-moore.com

^ permalink raw reply

* [PATCH] MAINTAINERS: add the associated Rust helper to the LSM section
From: Paul Moore @ 2025-08-14 21:59 UTC (permalink / raw)
  To: linux-security-module; +Cc: Benno Lossin

Suggested-by: Benno Lossin <lossin@kernel.org>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
 MAINTAINERS | 1 +
 1 file changed, 1 insertion(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fe168477caa4..d61f7246e5bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22790,6 +22790,7 @@ F:	include/linux/security.h
 F:	include/uapi/linux/lsm.h
 F:	security/
 F:	tools/testing/selftests/lsm/
+F:	rust/kernel/security.rs
 X:	security/selinux/
 K:	\bsecurity_[a-z_0-9]\+\b
 
-- 
2.50.1


^ permalink raw reply related

* Re: [PATCH v3 09/12] libbpf: Update light skeleton for signing
From: Andrii Nakryiko @ 2025-08-14 18:46 UTC (permalink / raw)
  To: KP Singh
  Cc: bpf, linux-security-module, bboscaccy, paul, kys, ast, daniel,
	andrii
In-Reply-To: <20250813205526.2992911-10-kpsingh@kernel.org>

On Wed, Aug 13, 2025 at 1:55 PM KP Singh <kpsingh@kernel.org> wrote:
>
> * The metadata map is created with as an exclusive map (with an
> excl_prog_hash) This restricts map access exclusively to the signed
> loader program, preventing tampering by other processes.
>
> * The map is then frozen, making it read-only from userspace.
>
> * BPF_OBJ_GET_INFO_BY_ID instructs the kernel to compute the hash of the
>   metadata map (H') and store it in bpf_map->sha.
>
> * The loader is then loaded with the signature which is then verified by
>   the kernel.
>
> The sekeleton currently uses the session keyring
> (KEY_SPEC_SESSION_KEYRING) by default but this can
> be overridden by the user of the skeleton.
>
> loading signed programs prebuilt into the kernel are not currently
> supported. These can supported by enabling BPF_OBJ_GET_INFO_BY_ID to be
> called from the kernel.
>
> Signed-off-by: KP Singh <kpsingh@kernel.org>
> ---
>  tools/lib/bpf/skel_internal.h | 75 +++++++++++++++++++++++++++++++++--
>  1 file changed, 71 insertions(+), 4 deletions(-)
>

[...]

> +static inline int skel_obj_get_info_by_fd(int fd)
> +{
> +       const size_t attr_sz = offsetofend(union bpf_attr, info);
> +       __u8 sha[SHA256_DIGEST_LENGTH];
> +       struct bpf_map_info info = {};

memset(0) this instead of relying on = {}

> +       __u32 info_len = sizeof(info);
> +       union bpf_attr attr;
> +
> +       info.hash = (long) &sha;
> +       info.hash_size = SHA256_DIGEST_LENGTH;
> +
> +       memset(&attr, 0, attr_sz);
> +       attr.info.bpf_fd = fd;
> +       attr.info.info = (long) &info;
> +       attr.info.info_len = info_len;
> +       return skel_sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, attr_sz);
> +}

[...]

^ permalink raw reply

* Re: [PATCH v3 06/12] bpf: Return hashes of maps in BPF_OBJ_GET_INFO_BY_FD
From: Andrii Nakryiko @ 2025-08-14 18:46 UTC (permalink / raw)
  To: KP Singh
  Cc: bpf, linux-security-module, bboscaccy, paul, kys, ast, daniel,
	andrii
In-Reply-To: <20250813205526.2992911-7-kpsingh@kernel.org>

On Wed, Aug 13, 2025 at 1:55 PM KP Singh <kpsingh@kernel.org> wrote:
>
> Currently only array maps are supported, but the implementation can be
> extended for other maps and objects. The hash is memoized only for
> exclusive and frozen maps as their content is stable until the exclusive
> program modifies the map.
>
> This is required  for BPF signing, enabling a trusted loader program to
> verify a map's integrity. The loader retrieves
> the map's runtime hash from the kernel and compares it against an
> expected hash computed at build time.
>
> Signed-off-by: KP Singh <kpsingh@kernel.org>
> ---
>  include/linux/bpf.h                           |  3 +++
>  include/uapi/linux/bpf.h                      |  2 ++
>  kernel/bpf/arraymap.c                         | 13 +++++++++++
>  kernel/bpf/syscall.c                          | 23 +++++++++++++++++++
>  tools/include/uapi/linux/bpf.h                |  2 ++
>  .../selftests/bpf/progs/verifier_map_ptr.c    |  7 ++++--
>  6 files changed, 48 insertions(+), 2 deletions(-)
>

[...]

>  struct bpf_btf_info {
> diff --git a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
> index 11a079145966..e2767d27d8aa 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
> @@ -70,10 +70,13 @@ __naked void bpf_map_ptr_write_rejected(void)
>         : __clobber_all);
>  }
>
> +/* The first element of struct bpf_map is a SHA256 hash of 32 bytes, accessing
> + * into this array is valid. The opts field is now at offset 33.
> + */

Does hash have to be at the beginning of struct bpf_map? why not just
put it at the end and not have to adjust any tests?.. (which now will
fail on older kernel for no good reason, unless I miss something)


>  SEC("socket")
>  __description("bpf_map_ptr: read non-existent field rejected")
>  __failure
> -__msg("cannot access ptr member ops with moff 0 in struct bpf_map with off 1 size 4")
> +__msg("cannot access ptr member ops with moff 32 in struct bpf_map with off 33 size 4")
>  __failure_unpriv
>  __msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
>  __flag(BPF_F_ANY_ALIGNMENT)
> @@ -82,7 +85,7 @@ __naked void read_non_existent_field_rejected(void)
>         asm volatile ("                                 \
>         r6 = 0;                                         \
>         r1 = %[map_array_48b] ll;                       \
> -       r6 = *(u32*)(r1 + 1);                           \
> +       r6 = *(u32*)(r1 + 33);                          \
>         r0 = 1;                                         \
>         exit;                                           \
>  "      :
> --
> 2.43.0
>

^ permalink raw reply

* Re: [PATCH v3 04/12] libbpf: Support exclusive map creation
From: Andrii Nakryiko @ 2025-08-14 18:46 UTC (permalink / raw)
  To: KP Singh
  Cc: bpf, linux-security-module, bboscaccy, paul, kys, ast, daniel,
	andrii
In-Reply-To: <20250813205526.2992911-5-kpsingh@kernel.org>

On Wed, Aug 13, 2025 at 1:55 PM KP Singh <kpsingh@kernel.org> wrote:
>
> Implement setters and getters that allow map to be registers as

typo: registered

> exclusive to the specified program. The registration should be done
> before the exclusive program is loaded.
>
> Signed-off-by: KP Singh <kpsingh@kernel.org>
> ---
>  tools/lib/bpf/bpf.c      |  4 ++-
>  tools/lib/bpf/bpf.h      |  4 ++-
>  tools/lib/bpf/libbpf.c   | 66 ++++++++++++++++++++++++++++++++++++++++
>  tools/lib/bpf/libbpf.h   | 18 +++++++++++
>  tools/lib/bpf/libbpf.map |  2 ++
>  5 files changed, 92 insertions(+), 2 deletions(-)
>
> diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
> index ab40dbf9f020..6a08a1559237 100644
> --- a/tools/lib/bpf/bpf.c
> +++ b/tools/lib/bpf/bpf.c
> @@ -172,7 +172,7 @@ int bpf_map_create(enum bpf_map_type map_type,
>                    __u32 max_entries,
>                    const struct bpf_map_create_opts *opts)
>  {
> -       const size_t attr_sz = offsetofend(union bpf_attr, map_token_fd);
> +       const size_t attr_sz = offsetofend(union bpf_attr, excl_prog_hash);
>         union bpf_attr attr;
>         int fd;
>
> @@ -203,6 +203,8 @@ int bpf_map_create(enum bpf_map_type map_type,
>         attr.map_ifindex = OPTS_GET(opts, map_ifindex, 0);
>
>         attr.map_token_fd = OPTS_GET(opts, token_fd, 0);
> +       attr.excl_prog_hash = ptr_to_u64(OPTS_GET(opts, excl_prog_hash, NULL));
> +       attr.excl_prog_hash_size = OPTS_GET(opts, excl_prog_hash_size, 0);
>
>         fd = sys_bpf_fd(BPF_MAP_CREATE, &attr, attr_sz);
>         return libbpf_err_errno(fd);
> diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
> index 7252150e7ad3..675a09bb7d2f 100644
> --- a/tools/lib/bpf/bpf.h
> +++ b/tools/lib/bpf/bpf.h
> @@ -54,9 +54,11 @@ struct bpf_map_create_opts {
>         __s32 value_type_btf_obj_fd;
>
>         __u32 token_fd;
> +       __u32 excl_prog_hash_size;

leaving a gap here, can you please reorder and have hash first,
followed by size?

> +       const void *excl_prog_hash;
>         size_t :0;
>  };
> -#define bpf_map_create_opts__last_field token_fd
> +#define bpf_map_create_opts__last_field excl_prog_hash
>
>  LIBBPF_API int bpf_map_create(enum bpf_map_type map_type,
>                               const char *map_name,
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 0bb3d71dcd9f..ed3294f69271 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -499,6 +499,7 @@ struct bpf_program {
>         __u32 line_info_rec_size;
>         __u32 line_info_cnt;
>         __u32 prog_flags;
> +       __u8  hash[SHA256_DIGEST_LENGTH];
>  };
>
>  struct bpf_struct_ops {
> @@ -578,6 +579,7 @@ struct bpf_map {
>         bool autocreate;
>         bool autoattach;
>         __u64 map_extra;
> +       struct bpf_program *excl_prog;
>  };
>
>  enum extern_type {
> @@ -4488,6 +4490,43 @@ bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx)
>         }
>  }
>
> +static int bpf_program__compute_hash(struct bpf_program *prog)

nit: this is not an API, so please don't use double underscores.
Something like bpf_prog_compute_hash() should do.

> +{
> +       struct bpf_insn *purged;
> +       int i, err;
> +
> +       purged = calloc(1, BPF_INSN_SZ * prog->insns_cnt);

we had some patch fixing similar argument misuse issue, so I'd rather
have calloc(prog->insns_cnt, BPF_INSN_SZ), if you don't mind

> +       if (!purged)
> +               return -ENOMEM;
> +
> +       /* If relocations have been done, the map_fd needs to be
> +        * discarded for the digest calculation.
> +        */
> +       for (i = 0; i < prog->insns_cnt; i++) {
> +               purged[i] = prog->insns[i];
> +               if (purged[i].code == (BPF_LD | BPF_IMM | BPF_DW) &&
> +                   (purged[i].src_reg == BPF_PSEUDO_MAP_FD ||
> +                    purged[i].src_reg == BPF_PSEUDO_MAP_VALUE)) {
> +                       purged[i].imm = 0;
> +                       i++;
> +                       if (i >= prog->insns_cnt ||
> +                           prog->insns[i].code != 0 ||
> +                           prog->insns[i].dst_reg != 0 ||
> +                           prog->insns[i].src_reg != 0 ||
> +                           prog->insns[i].off != 0) {
> +                               err = -EINVAL;
> +                               goto out;
> +                       }
> +                       purged[i] = prog->insns[i];
> +                       purged[i].imm = 0;
> +               }
> +       }
> +       err = libbpf_sha256(purged, prog->insns_cnt * sizeof(struct bpf_insn), prog->hash, SHA256_DIGEST_LENGTH);

too long, wrap before prog->hash?

> +out:
> +       free(purged);
> +       return err;
> +}
> +
>  static int bpf_program__record_reloc(struct bpf_program *prog,
>                                      struct reloc_desc *reloc_desc,
>                                      __u32 insn_idx, const char *sym_name,
> @@ -5227,6 +5266,18 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
>         create_attr.token_fd = obj->token_fd;
>         if (obj->token_fd)
>                 create_attr.map_flags |= BPF_F_TOKEN_FD;
> +       if (map->excl_prog) {
> +               if (map->excl_prog->obj->state == OBJ_LOADED) {
> +                       pr_warn("exclusive program already loaded\n");
> +                       return libbpf_err(-EINVAL);
> +               }

unnecessary check, maps are always created before programs, so if
map->excl_prog belongs to the same bpf_object (and it should), then we
implicitly have a guarantee it's not yet created. So please drop.

> +               err = bpf_program__compute_hash(map->excl_prog);
> +               if (err)
> +                       return err;
> +
> +               create_attr.excl_prog_hash = map->excl_prog->hash;
> +               create_attr.excl_prog_hash_size = SHA256_DIGEST_LENGTH;
> +       }
>
>         if (bpf_map__is_struct_ops(map)) {
>                 create_attr.btf_vmlinux_value_type_id = map->btf_vmlinux_value_type_id;
> @@ -10517,6 +10568,21 @@ int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd)
>         return 0;
>  }
>
> +int bpf_map__set_exclusive_program(struct bpf_map *map, struct bpf_program *prog)
> +{
> +       if (map_is_created(map)) {
> +               pr_warn("exclusive programs must be set before map creation\n");
> +               return libbpf_err(-EINVAL);
> +       }

should we worry about someone providing a bpf_program that doesn't
belong to the same bpf_object that map belongs to? it's easy to check,
just compare map->obj and prog->obj

> +       map->excl_prog = prog;
> +       return 0;
> +}
> +
> +struct bpf_program *bpf_map__get_exclusive_program(struct bpf_map *map)

libbpf getters don't have "get_" prefix, so just bpf_map__exclusive_program()

> +{
> +       return map->excl_prog;
> +}
> +
>  static struct bpf_map *
>  __bpf_map__iter(const struct bpf_map *m, const struct bpf_object *obj, int i)
>  {
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index 455a957cb702..ddaf58c8a298 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -1266,7 +1266,25 @@ LIBBPF_API int bpf_map__lookup_and_delete_elem(const struct bpf_map *map,
>   */
>  LIBBPF_API int bpf_map__get_next_key(const struct bpf_map *map,
>                                      const void *cur_key, void *next_key, size_t key_sz);
> +/**
> + * @brief **bpf_map__set_exclusive_program()** sets map to be exclusive to the
> + * to the specified program. The program must not be loaded yet.

typo: "to the" duplicated

Also, I think the more important restriction is that the map should
not have been created yet (so this has to be called between opening
and prepare/load steps, just like setting read-only global variables).
This by implication will mean that the program is not loaded either,
as we'll restrict bpf_program to be from the same bpf_object (which
you can mention as well for clarity).

> + * @param map BPF map to make exclusive.
> + * @param prog BPF program to be the exclusive user of the map.
> + * @return 0 on success; a negative error code otherwise.
> + *
> + * Once a map is made exclusive, only the specified program can access its
> + * contents.
> + */
> +LIBBPF_API int bpf_map__set_exclusive_program(struct bpf_map *map, struct bpf_program *prog);
>
> +/**
> + * @brief **bpf_map__get_exclusive_program()** returns the exclusive program
> + * that is registered with the map (if any).
> + * @param map BPF map to which the exclusive program is registered.
> + * @return the registered exclusive program.
> + */
> +LIBBPF_API struct bpf_program *bpf_map__get_exclusive_program(struct bpf_map *map);
>  struct bpf_xdp_set_link_opts {
>         size_t sz;
>         int old_fd;
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index d7bd463e7017..a5c5d0f2db5c 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -436,6 +436,8 @@ LIBBPF_1.6.0 {
>                 bpf_linker__add_buf;
>                 bpf_linker__add_fd;
>                 bpf_linker__new_fd;
> +               bpf_map__set_exclusive_program;
> +               bpf_map__get_exclusive_program;

we are in LIBBPF_1.7.0 now, so please move

pw-bot: cr


>                 bpf_object__prepare;
>                 bpf_prog_stream_read;
>                 bpf_program__attach_cgroup_opts;
> --
> 2.43.0
>

^ permalink raw reply

* Re: [PATCH v3 03/12] libbpf: Implement SHA256 internal helper
From: Andrii Nakryiko @ 2025-08-14 18:46 UTC (permalink / raw)
  To: KP Singh
  Cc: bpf, linux-security-module, bboscaccy, paul, kys, ast, daniel,
	andrii
In-Reply-To: <20250813205526.2992911-4-kpsingh@kernel.org>

On Wed, Aug 13, 2025 at 1:55 PM KP Singh <kpsingh@kernel.org> wrote:
>
> Use AF_ALG sockets to not have libbpf depend on OpenSSL. The helper is
> used for the loader generation code to embed the metadata hash in the
> loader program and also by the bpf_map__make_exclusive API to calculate
> the hash of the program the map is exclusive to.
>
> Signed-off-by: KP Singh <kpsingh@kernel.org>
> ---
>  tools/lib/bpf/libbpf.c          | 59 +++++++++++++++++++++++++++++++++
>  tools/lib/bpf/libbpf_internal.h |  4 +++
>  2 files changed, 63 insertions(+)
>

LGTM, but see note about unnecessary libbpf_err()

Acked-by: Andrii Nakryiko <andrii@kernel.org>

> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 8f5a81b672e1..0bb3d71dcd9f 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -43,6 +43,9 @@
>  #include <sys/vfs.h>
>  #include <sys/utsname.h>
>  #include <sys/resource.h>
> +#include <sys/socket.h>
> +#include <linux/if_alg.h>
> +#include <linux/socket.h>
>  #include <libelf.h>
>  #include <gelf.h>
>  #include <zlib.h>
> @@ -14207,3 +14210,59 @@ void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s)
>         free(s->progs);
>         free(s);
>  }
> +
> +int libbpf_sha256(const void *data, size_t data_sz, void *sha_out, size_t sha_out_sz)
> +{
> +       struct sockaddr_alg sa = {
> +               .salg_family = AF_ALG,
> +               .salg_type   = "hash",
> +               .salg_name   = "sha256"
> +       };
> +       int sock_fd = -1;
> +       int op_fd = -1;
> +       int err = 0;
> +
> +       if (sha_out_sz != SHA256_DIGEST_LENGTH) {
> +               pr_warn("sha_out_sz should be exactly 32 bytes for a SHA256 digest");
> +               return libbpf_err(-EINVAL);

this is an internal function, so there is no need to use libbpf_err()
to return error codes. Here and everywhere below should be just
`return -Exxx;`


> +       }
> +
> +       sock_fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
> +       if (sock_fd < 0) {
> +               err = -errno;
> +               pr_warn("failed to create AF_ALG socket for SHA256: %s\n", errstr(err));
> +               return libbpf_err(err);
> +       }
> +
> +       if (bind(sock_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
> +               err = -errno;
> +               pr_warn("failed to bind to AF_ALG socket for SHA256: %s\n", errstr(err));
> +               goto out;
> +       }
> +
> +       op_fd = accept(sock_fd, NULL, 0);
> +       if (op_fd < 0) {
> +               err = -errno;
> +               pr_warn("failed to accept from AF_ALG socket for SHA256: %s\n", errstr(err));
> +               goto out;
> +       }
> +
> +       if (write(op_fd, data, data_sz) != data_sz) {
> +               err = -errno;
> +               pr_warn("failed to write data to AF_ALG socket for SHA256: %s\n", errstr(err));
> +               goto out;
> +       }
> +
> +       if (read(op_fd, sha_out, SHA256_DIGEST_LENGTH) != SHA256_DIGEST_LENGTH) {
> +               err = -errno;
> +               pr_warn("failed to read SHA256 from AF_ALG socket: %s\n", errstr(err));
> +               goto out;
> +       }
> +
> +out:
> +       if (op_fd >= 0)
> +               close(op_fd);
> +       if (sock_fd >= 0)
> +               close(sock_fd);
> +       return libbpf_err(err);
> +}
> diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
> index 477a3b3389a0..8a055de0d324 100644
> --- a/tools/lib/bpf/libbpf_internal.h
> +++ b/tools/lib/bpf/libbpf_internal.h
> @@ -736,4 +736,8 @@ int elf_resolve_pattern_offsets(const char *binary_path, const char *pattern,
>
>  int probe_fd(int fd);
>
> +#define SHA256_DIGEST_LENGTH 32
> +#define SHA256_DWORD_SIZE SHA256_DIGEST_LENGTH / sizeof(__u64)
> +
> +int libbpf_sha256(const void *data, size_t data_sz, void *sha_out, size_t sha_out_sz);
>  #endif /* __LIBBPF_LIBBPF_INTERNAL_H */
> --
> 2.43.0
>

^ permalink raw reply

* Re: [PATCH v3 07/11] rust: security: replace `core::mem::zeroed` with `pin_init::zeroed`
From: Miguel Ojeda @ 2025-08-14 17:48 UTC (permalink / raw)
  To: Paul Moore
  Cc: Benno Lossin, Alice Ryhl, Miguel Ojeda, Alex Gaynor, Boqun Feng,
	Gary Guo, Björn Roy Baron, Andreas Hindborg, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Jocelyn Falempe, rust-for-linux,
	linux-kernel, linux-security-module
In-Reply-To: <CAHC9VhQNi31KSpB-MtvZO9e5fzuM_87VWb6rrMtxcqOGSPTiNg@mail.gmail.com>

On Thu, Aug 14, 2025 at 5:54 PM Paul Moore <paul@paul-moore.com> wrote:
>
> That's fine, it wasn't clear from the post that was the plan, and I
> vaguely recalled from past conversations with Rust devs that they
> preferred Rust wrappers/helpers to go in via the associated subsystem
> tree.

Yeah, it is still the case that Rust abstractions and Rust code in
general should land through the best tree possible.

However, sometimes tree-wide improvements may be easy to do with
simple Acked-bys.

> My comment asking for additional review/ACK tags wasn't due to any
> distrust of Benno - thank you for your work Benno - it is just a

Ah, sorry, I didn't mean that you distrusted Benno or anything like
that. I was trying to give context in case it helped you evaluate the
patch/risk, which also allowed me at the same time to acknowledge
Benno's experience/work.

We were mainly expecting the Acked-bys to proceed with the cleanups
here to remove these unsafe blocks, but we definitely want more tags
if possible in all patches, as always (by the time this gets applied,
we will hopefully get some more).

Cheers,
Miguel

^ permalink raw reply

* Re: [PATCH v3 07/11] rust: security: replace `core::mem::zeroed` with `pin_init::zeroed`
From: Benno Lossin @ 2025-08-14 17:28 UTC (permalink / raw)
  To: Paul Moore, Miguel Ojeda
  Cc: Alice Ryhl, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Jocelyn Falempe, rust-for-linux,
	linux-kernel, linux-security-module
In-Reply-To: <CAHC9VhQNi31KSpB-MtvZO9e5fzuM_87VWb6rrMtxcqOGSPTiNg@mail.gmail.com>

On Thu Aug 14, 2025 at 5:54 PM CEST, Paul Moore wrote:
> On Thu, Aug 14, 2025 at 11:31 AM Miguel Ojeda
> <miguel.ojeda.sandonis@gmail.com> wrote:
>> On Thu, Aug 14, 2025 at 5:19 PM Paul Moore <paul@paul-moore.com> wrote:
>> >
>> > I'm happy to take this via the LSM tree, but it would be nice to see a
>> > Reviewed-by/Acked-by from someone with a better understanding of Rust
>> > :)
>>
>> I think the idea is to take all these through the Rust one with
>> Acked-bys from the maintainers (or we can skip this one and do it in a
>> future cycle when the first patches get in).
>
> [CC'd the LSM list, as I just realized it wasn't on the original patch
> posting; in the future please include the LSM list on LSM related Rust
> patchsets/patches]

I checked and I didn't find a maintainers entry for that this file & the
LSM list. I'm using scripts/get_maintainer.pl to get the people I send
patches to and that also checks git commits, so I guess it added you
through that (which is very good :). So can we add a maintainers entry
for `rust/kernel/security.rs` so people don't miss this in the future?
Thanks!

---
Cheers,
Benno

^ permalink raw reply

* [PATCH v2] apparmor: Remove unused value
From: Chandra Mohan Sundar @ 2025-08-14 16:54 UTC (permalink / raw)
  To: john.johansen, paul, jmorris, serge, apparmor,
	linux-security-module, linux-kernel, shuah
  Cc: Chandra Mohan Sundar, linux-kernel-mentees

The value "new" is being assigned to NULL but that statement does not
have effect since "new" is being overwritten in the subsequent
fallback case.

Remove the unused value. This issue was reported by coverity static
analyzer.

Fixes: a9eb185be84e9 ("apparmor: fix x_table_lookup when stacking is not the first entry")
Signed-off-by: Chandra Mohan Sundar <chandramohan.explore@gmail.com>
---
Changes since v1:
    Fixed the commit message and Fixes tag message formatting.

 security/apparmor/domain.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 267da82afb14..9c0c7fa8de46 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -592,7 +592,6 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
 		if (!new || **lookupname != '&')
 			break;
 		stack = new;
-		new = NULL;
 		fallthrough;	/* to X_NAME */
 	case AA_X_NAME:
 		if (xindex & AA_X_CHILD)
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v3 11/12] bpftool: Add support for signing BPF programs
From: Blaise Boscaccy @ 2025-08-14 16:50 UTC (permalink / raw)
  To: KP Singh, bpf, linux-security-module
  Cc: paul, kys, ast, daniel, andrii, KP Singh
In-Reply-To: <20250813205526.2992911-12-kpsingh@kernel.org>

KP Singh <kpsingh@kernel.org> writes:

> Two modes of operation being added:
>
> Add two modes of operation:
>
> * For prog load, allow signing a program immediately before loading. This
>   is essential for command-line testing and administration.
>
>       bpftool prog load -S -k <private_key> -i <identity_cert> fentry_test.bpf.o
>
> * For gen skeleton, embed a pre-generated signature into the C skeleton
>   file. This supports the use of signed programs in compiled applications.
>
>       bpftool gen skeleton -S -k <private_key> -i <identity_cert> fentry_test.bpf.o
>
> Generation of the loader program and its metadata map is implemented in
> libbpf (bpf_obj__gen_loader). bpftool generates a skeleton that loads
> the program and automates the required steps: freezing the map, creating
> an exclusive map, loading, and running. Users can use standard libbpf
> APIs directly or integrate loader program generation into their own
> toolchains.
>
> Signed-off-by: KP Singh <kpsingh@kernel.org>
> ---
>  .../bpf/bpftool/Documentation/bpftool-gen.rst |  16 +-
>  .../bpftool/Documentation/bpftool-prog.rst    |  18 +-
>  tools/bpf/bpftool/Makefile                    |   6 +-
>  tools/bpf/bpftool/cgroup.c                    |   4 +
>  tools/bpf/bpftool/gen.c                       |  60 ++++-
>  tools/bpf/bpftool/main.c                      |  26 ++-
>  tools/bpf/bpftool/main.h                      |  11 +
>  tools/bpf/bpftool/prog.c                      |  27 ++-
>  tools/bpf/bpftool/sign.c                      | 212 ++++++++++++++++++
>  9 files changed, 367 insertions(+), 13 deletions(-)
>  create mode 100644 tools/bpf/bpftool/sign.c
>
> diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> index ca860fd97d8d..cef469d758ed 100644
> --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> @@ -16,7 +16,8 @@ SYNOPSIS
>  
>  **bpftool** [*OPTIONS*] **gen** *COMMAND*
>  
> -*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } }
> +*OPTIONS* := { |COMMON_OPTIONS| [ { **-L** | **--use-loader** } ]
> +[ { { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> } ] }}
>  
>  *COMMAND* := { **object** | **skeleton** | **help** }
>  
> @@ -186,6 +187,19 @@ OPTIONS
>      skeleton). A light skeleton contains a loader eBPF program. It does not use
>      the majority of the libbpf infrastructure, and does not need libelf.
>  
> +-S, --sign
> +    For skeletons, generate a signed skeleton. This option must be used with
> +    **-k** and **-i**. Using this flag implicitly enables **--use-loader**.
> +    See the "Signed Skeletons" section in the description of the
> +    **gen skeleton** command for more details.
> +
> +-k <private_key.pem>
> +    Path to the private key file in PEM format, required for signing.
> +
> +-i <certificate.x509>
> +    Path to the X.509 certificate file in PEM or DER format, required for
> +    signing.
> +
>  EXAMPLES
>  ========
>  **$ cat example1.bpf.c**
> diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
> index f69fd92df8d8..55b812761df2 100644
> --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
> +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
> @@ -16,9 +16,9 @@ SYNOPSIS
>  
>  **bpftool** [*OPTIONS*] **prog** *COMMAND*
>  
> -*OPTIONS* := { |COMMON_OPTIONS| |
> -{ **-f** | **--bpffs** } | { **-m** | **--mapcompat** } | { **-n** | **--nomount** } |
> -{ **-L** | **--use-loader** } }
> +*OPTIONS* := { |COMMON_OPTIONS| [ { **-f** | **--bpffs** } ] [ { **-m** | **--mapcompat** } ]
> +[ { **-n** | **--nomount** } ] [ { **-L** | **--use-loader** } ]
> +[ { { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> } ] }
>  
>  *COMMANDS* :=
>  { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** |
> @@ -248,6 +248,18 @@ OPTIONS
>      creating the maps, and loading the programs (see **bpftool prog tracelog**
>      as a way to dump those messages).
>  
> +-S, --sign
> +    Enable signing of the BPF program before loading. This option must be
> +    used with **-k** and **-i**. Using this flag implicitly enables
> +    **--use-loader**.
> +
> +-k <private_key.pem>
> +    Path to the private key file in PEM format, required when signing.
> +
> +-i <certificate.x509>
> +    Path to the X.509 certificate file in PEM or DER format, required when
> +    signing.
> +
>  EXAMPLES
>  ========
>  **# bpftool prog show**
> diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
> index 9e9a5f006cd2..586d1b2595d1 100644
> --- a/tools/bpf/bpftool/Makefile
> +++ b/tools/bpf/bpftool/Makefile
> @@ -130,8 +130,8 @@ include $(FEATURES_DUMP)
>  endif
>  endif
>  
> -LIBS = $(LIBBPF) -lelf -lz
> -LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz
> +LIBS = $(LIBBPF) -lelf -lz -lcrypto
> +LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz -lcrypto
>  
>  ifeq ($(feature-libelf-zstd),1)
>  LIBS += -lzstd
> @@ -194,7 +194,7 @@ endif
>  
>  BPFTOOL_BOOTSTRAP := $(BOOTSTRAP_OUTPUT)bpftool
>  
> -BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o)
> +BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o sign.o)
>  $(BOOTSTRAP_OBJS): $(LIBBPF_BOOTSTRAP)
>  
>  OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
> diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c
> index 944ebe21a216..ec356deb27c9 100644
> --- a/tools/bpf/bpftool/cgroup.c
> +++ b/tools/bpf/bpftool/cgroup.c
> @@ -2,6 +2,10 @@
>  // Copyright (C) 2017 Facebook
>  // Author: Roman Gushchin <guro@fb.com>
>  
> +#undef GCC_VERSION
> +#ifndef _GNU_SOURCE
> +#define _GNU_SOURCE
> +#endif
>  #define _XOPEN_SOURCE 500
>  #include <errno.h>
>  #include <fcntl.h>
> diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> index 67a60114368f..427468c9e9c2 100644
> --- a/tools/bpf/bpftool/gen.c
> +++ b/tools/bpf/bpftool/gen.c
> @@ -688,10 +688,17 @@ static void codegen_destroy(struct bpf_object *obj, const char *obj_name)
>  static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *header_guard)
>  {
>  	DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
> +	struct bpf_load_and_run_opts sopts = {};
> +	char sig_buf[MAX_SIG_SIZE];
> +	__u8 prog_sha[SHA256_DIGEST_LENGTH];
>  	struct bpf_map *map;
> +
>  	char ident[256];
>  	int err = 0;
>  
> +	if (sign_progs)
> +		opts.gen_hash = true;
> +
>  	err = bpf_object__gen_loader(obj, &opts);
>  	if (err)
>  		return err;
> @@ -701,6 +708,7 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
>  		p_err("failed to load object file");
>  		goto out;
>  	}
> +
>  	/* If there was no error during load then gen_loader_opts
>  	 * are populated with the loader program.
>  	 */
> @@ -780,8 +788,51 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
>  	print_hex(opts.insns, opts.insns_sz);
>  	codegen("\
>  		\n\
> -		\";							    \n\
> -									    \n\
> +		\";\n");
> +
> +	if (sign_progs) {
> +		sopts.insns = opts.insns;
> +		sopts.insns_sz = opts.insns_sz;
> +		sopts.excl_prog_hash = prog_sha;
> +		sopts.excl_prog_hash_sz = sizeof(prog_sha);
> +		sopts.signature = sig_buf;
> +		sopts.signature_sz = MAX_SIG_SIZE;
> +		sopts.keyring_id = KEY_SPEC_SESSION_KEYRING;
> +

This still has the session keyring hardcoded. 

> +		err = bpftool_prog_sign(&sopts);
> +		if (err < 0)
> +			return err;
> +
> +		codegen("\
> +		\n\
> +			static const char opts_sig[] __attribute__((__aligned__(8))) = \"\\\n\
> +		");
> +		print_hex((const void *)sig_buf, sopts.signature_sz);
> +		codegen("\
> +		\n\
> +		\";\n");
> +
> +		codegen("\
> +		\n\
> +			static const char opts_excl_hash[] __attribute__((__aligned__(8))) = \"\\\n\
> +		");
> +		print_hex((const void *)prog_sha, sizeof(prog_sha));
> +		codegen("\
> +		\n\
> +		\";\n");
> +
> +		codegen("\
> +		\n\
> +			opts.signature = (void *)opts_sig;			\n\
> +			opts.signature_sz = sizeof(opts_sig) - 1;		\n\
> +			opts.excl_prog_hash = (void *)opts_excl_hash;		\n\
> +			opts.excl_prog_hash_sz = sizeof(opts_excl_hash) - 1;	\n\
> +			opts.keyring_id = KEY_SPEC_SESSION_KEYRING;		\n\
> +		");

And here.

> +	}
> +
> +	codegen("\
> +		\n\
>  			opts.ctx = (struct bpf_loader_ctx *)skel;	    \n\
>  			opts.data_sz = sizeof(opts_data) - 1;		    \n\
>  			opts.data = (void *)opts_data;			    \n\
> @@ -1240,7 +1291,7 @@ static int do_skeleton(int argc, char **argv)
>  		err = -errno;
>  		libbpf_strerror(err, err_buf, sizeof(err_buf));
>  		p_err("failed to open BPF object file: %s", err_buf);
> -		goto out;
> +		goto out_obj;
>  	}
>  
>  	bpf_object__for_each_map(map, obj) {
> @@ -1552,6 +1603,7 @@ static int do_skeleton(int argc, char **argv)
>  	err = 0;
>  out:
>  	bpf_object__close(obj);
> +out_obj:
>  	if (obj_data)
>  		munmap(obj_data, mmap_sz);
>  	close(fd);
> @@ -1930,7 +1982,7 @@ static int do_help(int argc, char **argv)
>  		"       %1$s %2$s help\n"
>  		"\n"
>  		"       " HELP_SPEC_OPTIONS " |\n"
> -		"                    {-L|--use-loader} }\n"
> +		"                    {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ]}\n"
>  		"",
>  		bin_name, "gen");
>  
> diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
> index 0f1183b2ed0a..c78eb80b9c94 100644
> --- a/tools/bpf/bpftool/main.c
> +++ b/tools/bpf/bpftool/main.c
> @@ -33,6 +33,9 @@ bool relaxed_maps;
>  bool use_loader;
>  struct btf *base_btf;
>  struct hashmap *refs_table;
> +bool sign_progs;
> +const char *private_key_path;
> +const char *cert_path;
>  
>  static void __noreturn clean_and_exit(int i)
>  {
> @@ -448,6 +451,7 @@ int main(int argc, char **argv)
>  		{ "nomount",	no_argument,	NULL,	'n' },
>  		{ "debug",	no_argument,	NULL,	'd' },
>  		{ "use-loader",	no_argument,	NULL,	'L' },
> +		{ "sign",	no_argument,	NULL,	'S' },
>  		{ "base-btf",	required_argument, NULL, 'B' },
>  		{ 0 }
>  	};
> @@ -474,7 +478,7 @@ int main(int argc, char **argv)
>  	bin_name = "bpftool";
>  
>  	opterr = 0;
> -	while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l",
> +	while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
>  				  options, NULL)) >= 0) {
>  		switch (opt) {
>  		case 'V':
> @@ -520,6 +524,16 @@ int main(int argc, char **argv)
>  		case 'L':
>  			use_loader = true;
>  			break;
> +		case 'S':
> +			sign_progs = true;
> +			use_loader = true;
> +			break;
> +		case 'k':
> +			private_key_path = optarg;
> +			break;
> +		case 'i':
> +			cert_path = optarg;
> +			break;
>  		default:
>  			p_err("unrecognized option '%s'", argv[optind - 1]);
>  			if (json_output)
> @@ -534,6 +548,16 @@ int main(int argc, char **argv)
>  	if (argc < 0)
>  		usage();
>  
> +	if (sign_progs && (private_key_path == NULL || cert_path == NULL)) {
> +		p_err("-i <identity_x509_cert> and -k <private> key must be supplied with -S for signing");
> +		return -EINVAL;
> +	}
> +
> +	if (!sign_progs && (private_key_path != NULL || cert_path != NULL)) {
> +		p_err("-i <identity_x509_cert> and -k <private> also need --sign to be used for sign programs");
> +		return -EINVAL;
> +	}
> +
>  	if (version_requested)
>  		ret = do_version(argc, argv);
>  	else
> diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
> index a2bb0714b3d6..f7f5b39b66c8 100644
> --- a/tools/bpf/bpftool/main.h
> +++ b/tools/bpf/bpftool/main.h
> @@ -6,9 +6,14 @@
>  
>  /* BFD and kernel.h both define GCC_VERSION, differently */
>  #undef GCC_VERSION
> +#ifndef _GNU_SOURCE
> +#define _GNU_SOURCE
> +#endif
>  #include <stdbool.h>
>  #include <stdio.h>
> +#include <errno.h>
>  #include <stdlib.h>
> +#include <bpf/skel_internal.h>
>  #include <linux/bpf.h>
>  #include <linux/compiler.h>
>  #include <linux/kernel.h>
> @@ -52,6 +57,7 @@ static inline void *u64_to_ptr(__u64 ptr)
>  	})
>  
>  #define ERR_MAX_LEN	1024
> +#define MAX_SIG_SIZE	4096
>  
>  #define BPF_TAG_FMT	"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
>  
> @@ -85,6 +91,9 @@ extern bool relaxed_maps;
>  extern bool use_loader;
>  extern struct btf *base_btf;
>  extern struct hashmap *refs_table;
> +extern bool sign_progs;
> +extern const char *private_key_path;
> +extern const char *cert_path;
>  
>  void __printf(1, 2) p_err(const char *fmt, ...);
>  void __printf(1, 2) p_info(const char *fmt, ...);
> @@ -275,4 +284,6 @@ int pathname_concat(char *buf, int buf_sz, const char *path,
>  /* print netfilter bpf_link info */
>  void netfilter_dump_plain(const struct bpf_link_info *info);
>  void netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr);
> +int bpftool_prog_sign(struct bpf_load_and_run_opts *opts);
> +__u32 register_session_key(const char *key_der_path);
>  #endif
> diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
> index 9722d841abc0..82b8da084504 100644
> --- a/tools/bpf/bpftool/prog.c
> +++ b/tools/bpf/bpftool/prog.c
> @@ -23,6 +23,7 @@
>  #include <linux/err.h>
>  #include <linux/perf_event.h>
>  #include <linux/sizes.h>
> +#include <linux/keyctl.h>
>  
>  #include <bpf/bpf.h>
>  #include <bpf/btf.h>
> @@ -1930,6 +1931,8 @@ static int try_loader(struct gen_loader_opts *gen)
>  {
>  	struct bpf_load_and_run_opts opts = {};
>  	struct bpf_loader_ctx *ctx;
> +	char sig_buf[MAX_SIG_SIZE];
> +	__u8 prog_sha[SHA256_DIGEST_LENGTH];
>  	int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
>  					     sizeof(struct bpf_prog_desc));
>  	int log_buf_sz = (1u << 24) - 1;
> @@ -1953,6 +1956,24 @@ static int try_loader(struct gen_loader_opts *gen)
>  	opts.insns = gen->insns;
>  	opts.insns_sz = gen->insns_sz;
>  	fds_before = count_open_fds();
> +
> +	if (sign_progs) {
> +		opts.excl_prog_hash = prog_sha;
> +		opts.excl_prog_hash_sz = sizeof(prog_sha);
> +		opts.signature = sig_buf;
> +		opts.signature_sz = MAX_SIG_SIZE;
> +		opts.keyring_id = KEY_SPEC_SESSION_KEYRING;
> +

And here as well. 

> +		err = bpftool_prog_sign(&opts);
> +		if (err < 0)
> +			return err;
> +
> +		err = register_session_key(cert_path);
> +		if (err < 0) {
> +			p_err("failed to add session key");
> +			goto out;
> +		}
> +	}
>  	err = bpf_load_and_run(&opts);
>  	fd_delta = count_open_fds() - fds_before;
>  	if (err < 0 || verifier_logs) {
> @@ -1961,6 +1982,7 @@ static int try_loader(struct gen_loader_opts *gen)
>  			fprintf(stderr, "loader prog leaked %d FDs\n",
>  				fd_delta);
>  	}
> +out:
>  	free(log_buf);
>  	return err;
>  }
> @@ -1988,6 +2010,9 @@ static int do_loader(int argc, char **argv)
>  		goto err_close_obj;
>  	}
>  
> +	if (sign_progs)
> +		gen.gen_hash = true;
> +
>  	err = bpf_object__gen_loader(obj, &gen);
>  	if (err)
>  		goto err_close_obj;
> @@ -2562,7 +2587,7 @@ static int do_help(int argc, char **argv)
>  		"       METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
>  		"       " HELP_SPEC_OPTIONS " |\n"
>  		"                    {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
> -		"                    {-L|--use-loader} }\n"
> +		"                    {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ] \n"
>  		"",
>  		bin_name, argv[-2]);
>  
> diff --git a/tools/bpf/bpftool/sign.c b/tools/bpf/bpftool/sign.c
> new file mode 100644
> index 000000000000..b29d825bb1d4
> --- /dev/null
> +++ b/tools/bpf/bpftool/sign.c
> @@ -0,0 +1,212 @@
> +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +/*
> + * Copyright (C) 2025 Google LLC.
> + */
> +
> +#ifndef _GNU_SOURCE
> +#define _GNU_SOURCE
> +#endif
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <string.h>
> +#include <getopt.h>
> +#include <err.h>
> +#include <openssl/opensslv.h>
> +#include <openssl/bio.h>
> +#include <openssl/evp.h>
> +#include <openssl/pem.h>
> +#include <openssl/err.h>
> +#include <openssl/cms.h>
> +#include <linux/keyctl.h>
> +#include <errno.h>
> +
> +#include <bpf/skel_internal.h>
> +
> +#include "main.h"
> +
> +#define OPEN_SSL_ERR_BUF_LEN 256
> +
> +static void display_openssl_errors(int l)
> +{
> +	char buf[OPEN_SSL_ERR_BUF_LEN];
> +	const char *file;
> +	const char *data;
> +	unsigned long e;
> +	int flags;
> +	int line;
> +
> +	while ((e = ERR_get_error_all(&file, &line, NULL, &data, &flags))) {
> +		ERR_error_string_n(e, buf, sizeof(buf));
> +		if (data && (flags & ERR_TXT_STRING)) {
> +			p_err("OpenSSL %s: %s:%d: %s", buf, file, line, data);
> +		} else {
> +			p_err("OpenSSL %s: %s:%d", buf, file, line);
> +		}
> +	}
> +}
> +
> +#define DISPLAY_OSSL_ERR(cond)				 \
> +	do {						 \
> +		bool __cond = (cond);			 \
> +		if (__cond && ERR_peek_error())		 \
> +			display_openssl_errors(__LINE__);\
> +	} while (0)
> +
> +static EVP_PKEY *read_private_key(const char *pkey_path)
> +{
> +	EVP_PKEY *private_key = NULL;
> +	BIO *b;
> +
> +	b = BIO_new_file(pkey_path, "rb");
> +	private_key = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL);
> +	BIO_free(b);
> +	DISPLAY_OSSL_ERR(!private_key);
> +	return private_key;
> +}
> +
> +static X509 *read_x509(const char *x509_name)
> +{
> +	unsigned char buf[2];
> +	X509 *x509 = NULL;
> +	BIO *b;
> +	int n;
> +
> +	b = BIO_new_file(x509_name, "rb");
> +	if (!b)
> +		goto cleanup;
> +
> +	/* Look at the first two bytes of the file to determine the encoding */
> +	n = BIO_read(b, buf, 2);
> +	if (n != 2)
> +		goto cleanup;
> +
> +	if (BIO_reset(b) != 0)
> +		goto cleanup;
> +
> +	if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
> +		/* Assume raw DER encoded X.509 */
> +		x509 = d2i_X509_bio(b, NULL);
> +	else
> +		/* Assume PEM encoded X.509 */
> +		x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
> +
> +cleanup:
> +	BIO_free(b);
> +	DISPLAY_OSSL_ERR(!x509);
> +	return x509;
> +}
> +
> +__u32 register_session_key(const char *key_der_path)
> +{
> +	unsigned char *der_buf = NULL;
> +	X509 *x509 = NULL;
> +	int key_id = -1;
> +	int der_len;
> +
> +	if (!key_der_path)
> +		return key_id;
> +	x509 = read_x509(key_der_path);
> +	if (!x509)
> +		goto cleanup;
> +	der_len = i2d_X509(x509, &der_buf);
> +	if (der_len < 0)
> +		goto cleanup;
> +	key_id = syscall(__NR_add_key, "asymmetric", key_der_path, der_buf,
> +			     (size_t)der_len, KEY_SPEC_SESSION_KEYRING);
> +cleanup:
> +	X509_free(x509);
> +	OPENSSL_free(der_buf);
> +	DISPLAY_OSSL_ERR(key_id == -1);
> +	return key_id;
> +}
> +
> +int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
> +{
> +	BIO *bd_in = NULL, *bd_out = NULL;
> +	EVP_PKEY *private_key = NULL;
> +	CMS_ContentInfo *cms = NULL;
> +	long actual_sig_len = 0;
> +	X509 *x509 = NULL;
> +	int err = 0;
> +
> +	bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
> +	if (!bd_in) {
> +		err = -ENOMEM;
> +		goto cleanup;
> +	}
> +
> +	private_key = read_private_key(private_key_path);
> +	if (!private_key) {
> +		err = -EINVAL;
> +		goto cleanup;
> +	}
> +
> +	x509 = read_x509(cert_path);
> +	if (!x509) {
> +		err = -EINVAL;
> +		goto cleanup;
> +	}
> +
> +	cms = CMS_sign(NULL, NULL, NULL, NULL,
> +		       CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED |
> +			       CMS_STREAM);
> +	if (!cms) {
> +		err = -EINVAL;
> +		goto cleanup;
> +	}
> +
> +	if (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(),
> +			     CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
> +			     CMS_USE_KEYID | CMS_NOATTR)) {
> +		err = -EINVAL;
> +		goto cleanup;
> +	}
> +
> +	if (CMS_final(cms, bd_in, NULL, CMS_NOCERTS | CMS_BINARY) != 1) {
> +		err = -EIO;
> +		goto cleanup;
> +	}
> +
> +	EVP_Digest(opts->insns, opts->insns_sz, opts->excl_prog_hash,
> +		   &opts->excl_prog_hash_sz, EVP_sha256(), NULL);
> +
> +		bd_out = BIO_new(BIO_s_mem());
> +	if (!bd_out) {
> +		err = -ENOMEM;
> +		goto cleanup;
> +	}
> +
> +	if (!i2d_CMS_bio_stream(bd_out, cms, NULL, 0)) {
> +		err = -EIO;
> +		goto cleanup;
> +	}
> +
> +	actual_sig_len = BIO_get_mem_data(bd_out, NULL);
> +	if (actual_sig_len <= 0) {
> +		err = -EIO;
> +		goto cleanup;
> +	}
> +
> +	if ((size_t)actual_sig_len > opts->signature_sz) {
> +		err = -ENOSPC;
> +		goto cleanup;
> +	}
> +
> +	if (BIO_read(bd_out, opts->signature, actual_sig_len) != actual_sig_len) {
> +		err = -EIO;
> +		goto cleanup;
> +	}
> +
> +	opts->signature_sz = actual_sig_len;
> +cleanup:
> +	BIO_free(bd_out);
> +	CMS_ContentInfo_free(cms);
> +	X509_free(x509);
> +	EVP_PKEY_free(private_key);
> +	BIO_free(bd_in);
> +	DISPLAY_OSSL_ERR(err < 0);
> +	return err;
> +}
> -- 
> 2.43.0

^ permalink raw reply

* Re: [PATCH v3 07/11] rust: security: replace `core::mem::zeroed` with `pin_init::zeroed`
From: Paul Moore @ 2025-08-14 15:54 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Benno Lossin, Alice Ryhl, Miguel Ojeda, Alex Gaynor, Boqun Feng,
	Gary Guo, Björn Roy Baron, Andreas Hindborg, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Jocelyn Falempe, rust-for-linux,
	linux-kernel, linux-security-module
In-Reply-To: <CANiq72=vhPsGjSx9u0FvDa6uzMFkFQFP9qG+DhtZ_U5TyV=bJQ@mail.gmail.com>

On Thu, Aug 14, 2025 at 11:31 AM Miguel Ojeda
<miguel.ojeda.sandonis@gmail.com> wrote:
> On Thu, Aug 14, 2025 at 5:19 PM Paul Moore <paul@paul-moore.com> wrote:
> >
> > I'm happy to take this via the LSM tree, but it would be nice to see a
> > Reviewed-by/Acked-by from someone with a better understanding of Rust
> > :)
>
> I think the idea is to take all these through the Rust one with
> Acked-bys from the maintainers (or we can skip this one and do it in a
> future cycle when the first patches get in).

[CC'd the LSM list, as I just realized it wasn't on the original patch
posting; in the future please include the LSM list on LSM related Rust
patchsets/patches]

That's fine, it wasn't clear from the post that was the plan, and I
vaguely recalled from past conversations with Rust devs that they
preferred Rust wrappers/helpers to go in via the associated subsystem
tree.

> In any case, Benno is very knowledgeable in Rust -- he wrote the
> library being called here -- so unless you see something out of the
> ordinary, it seems OK to me.

My comment asking for additional review/ACK tags wasn't due to any
distrust of Benno - thank you for your work Benno - it is just a
matter of trying to make sure there are at least two sets of
(knowledgeable) eyes on a patch before it is merged.  If it is
something I'm merging into one the trees I maintain, normally I count
myself as the second set of eyes, but in this case I don't (yet)
consider myself a knowledgeable Rust reviewer so I was asking for an
additional explicit review tag.  If someone else is going to merge
this patch{set}, then it's up to them; although I would hope they
would do something similar.

-- 
paul-moore.com

^ permalink raw reply

* [PATCH] security: min_addr: use max() to improve code
From: Qianfeng Rong @ 2025-08-14 14:26 UTC (permalink / raw)
  To: Paul Moore, James Morris, Serge E. Hallyn, linux-security-module,
	linux-kernel
  Cc: Qianfeng Rong

Use max() to reduce the code in update_mmap_min_addr() and improve its
readability.

Signed-off-by: Qianfeng Rong <rongqianfeng@vivo.com>
---
 security/min_addr.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/security/min_addr.c b/security/min_addr.c
index df1bc643d886..50035bc9281c 100644
--- a/security/min_addr.c
+++ b/security/min_addr.c
@@ -16,10 +16,7 @@ unsigned long dac_mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
 static void update_mmap_min_addr(void)
 {
 #ifdef CONFIG_LSM_MMAP_MIN_ADDR
-	if (dac_mmap_min_addr > CONFIG_LSM_MMAP_MIN_ADDR)
-		mmap_min_addr = dac_mmap_min_addr;
-	else
-		mmap_min_addr = CONFIG_LSM_MMAP_MIN_ADDR;
+	mmap_min_addr = max(dac_mmap_min_addr, CONFIG_LSM_MMAP_MIN_ADDR);
 #else
 	mmap_min_addr = dac_mmap_min_addr;
 #endif
-- 
2.34.1


^ permalink raw reply related

* [PATCH] lockdown: avoid extra call to strlen() in lockdown_read()
From: Dmitry Antipov @ 2025-08-14 14:00 UTC (permalink / raw)
  To: Nicolas Bouchinet, Xiu Jianfeng, Paul Moore, James Morris,
	Serge E . Hallyn
  Cc: linux-security-module, Dmitry Antipov

Since s*printf() family of functions returns the number of characters
emitted, avoid redundant call to strlen() in lockdown_read() and prefer
snprintf() over sprintf() for an extra protection against buffer overflow.

Signed-off-by: Dmitry Antipov <dmantipov@yandex.ru>
---
 security/lockdown/lockdown.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
index cf83afa1d879..10537d7c4437 100644
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -106,9 +106,13 @@ static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count,
 			const char *label = lockdown_reasons[level];
 
 			if (kernel_locked_down == level)
-				offset += sprintf(temp+offset, "[%s] ", label);
+				offset += snprintf(temp + offset,
+						   sizeof(temp) - offset,
+						   "[%s] ", label);
 			else
-				offset += sprintf(temp+offset, "%s ", label);
+				offset += snprintf(temp + offset,
+						   sizeof(temp) - offset,
+						   "%s ", label);
 		}
 	}
 
@@ -116,7 +120,7 @@ static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count,
 	if (offset > 0)
 		temp[offset-1] = '\n';
 
-	return simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+	return simple_read_from_buffer(buf, count, ppos, temp, offset);
 }
 
 static ssize_t lockdown_write(struct file *file, const char __user *buf,
-- 
2.50.1


^ permalink raw reply related

* Re: [PATCH v2 0/3] Allow individual features to be locked down
From: Nicolas Bouchinet @ 2025-08-14 10:51 UTC (permalink / raw)
  To: Nikolay Borisov
  Cc: linux-security-module, linux-kernel, paul, serge, jmorris,
	dan.j.williams, Xiujianfeng
In-Reply-To: <60f17362-af8e-46db-9bcf-be85b84a525d@suse.com>

On Thu, Aug 14, 2025 at 01:02:36PM +0300, Nikolay Borisov wrote:
> 
> 
> On 8/14/25 11:59, Nicolas Bouchinet wrote:
> > Hi Nikolay,
> > 
> > After discussing with Xiu, we have decided not to accept this patchset.
> > 
> > The goal of Lockdown being to draw a clear line between ring-0 and uid-0,
> > having a more granular way to activate Lockdown will break it. Primarily
> > because most lockdown-reasons can be bypassed if used independently.
> > 
> > Even if the goal of Lockdown were to be redefined, we would need to ensure the
> > security interdependence between different lockdown-reasons. This is highly
> > tied to where people calls the `security_locked_down` hook and thus is out of
> > our maintenance scope.
> > 
> > Having coarse-grained lockdown reasons and integrity/confidentiality levels
> > allows us to ensure that all of the reasons are correctly locked down.
> > 
> > Best regards,
> > 
> > Nicolas
> 
> Thanks for the feedback, to try and not have all this code go to waste, what
> about consdering patch 2 - kunits tests. Apart from
> lockdown_test_individual_level() the other tests are applicable to the
> existing lockdown implementation and can aid in future developments?
> 

Yes of course, thanks a lot for those tests. Can you adapt them and send
them separately for review ?

i.e, the `lockdown_test_no_downgrade` should check for -EPERM.

FYI, I have a three week holiday starting today. I'll return on
Septembre the 8th. I let Xiu review those patches until then.

^ permalink raw reply

* Re: [PATCH v2 0/3] Allow individual features to be locked down
From: Nikolay Borisov @ 2025-08-14 10:02 UTC (permalink / raw)
  To: Nicolas Bouchinet
  Cc: linux-security-module, linux-kernel, paul, serge, jmorris,
	dan.j.williams, Xiujianfeng
In-Reply-To: <2vldrnqs6fbljqqp6vbwmkushx6dxcpephgb6svmu5i64lijpy@lg37aflddv3b>



On 8/14/25 11:59, Nicolas Bouchinet wrote:
> Hi Nikolay,
> 
> After discussing with Xiu, we have decided not to accept this patchset.
> 
> The goal of Lockdown being to draw a clear line between ring-0 and uid-0,
> having a more granular way to activate Lockdown will break it. Primarily
> because most lockdown-reasons can be bypassed if used independently.
> 
> Even if the goal of Lockdown were to be redefined, we would need to ensure the
> security interdependence between different lockdown-reasons. This is highly
> tied to where people calls the `security_locked_down` hook and thus is out of
> our maintenance scope.
> 
> Having coarse-grained lockdown reasons and integrity/confidentiality levels
> allows us to ensure that all of the reasons are correctly locked down.
> 
> Best regards,
> 
> Nicolas

Thanks for the feedback, to try and not have all this code go to waste, 
what about consdering patch 2 - kunits tests. Apart from 
lockdown_test_individual_level() the other tests are applicable to the 
existing lockdown implementation and can aid in future developments?


^ permalink raw reply

* Re: [PATCH v2 0/3] Allow individual features to be locked down
From: Nicolas Bouchinet @ 2025-08-14  8:59 UTC (permalink / raw)
  To: Nikolay Borisov
  Cc: linux-security-module, linux-kernel, paul, serge, jmorris,
	dan.j.williams, Xiujianfeng
In-Reply-To: <9b6fd06e-5438-4539-821c-6f3d5fa6b7d1@suse.com>

Hi Nikolay,

After discussing with Xiu, we have decided not to accept this patchset.

The goal of Lockdown being to draw a clear line between ring-0 and uid-0,
having a more granular way to activate Lockdown will break it. Primarily
because most lockdown-reasons can be bypassed if used independently.

Even if the goal of Lockdown were to be redefined, we would need to ensure the
security interdependence between different lockdown-reasons. This is highly
tied to where people calls the `security_locked_down` hook and thus is out of
our maintenance scope.

Having coarse-grained lockdown reasons and integrity/confidentiality levels
allows us to ensure that all of the reasons are correctly locked down.

Best regards,

Nicolas

^ permalink raw reply

* Re: [RFC PATCH 2/2] memfd: call security_inode_init_security_anon
From: Thiébaud Weksteen @ 2025-08-14  2:24 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: Paul Moore, James Morris, Hugh Dickins, Jeff Vander Stoep,
	Nick Kralevich, Jeff Xu, linux-kernel, linux-security-module,
	selinux
In-Reply-To: <CAEjxPJ5otOTmP44QcYXpqcAyg1JU+CTPmS6UgPugnQbFJ_9=dw@mail.gmail.com>

On Mon, Aug 11, 2025 at 11:09 PM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Mon, Aug 11, 2025 at 1:34 AM Thiébaud Weksteen <tweek@google.com> wrote:
> >
> > On Fri, Aug 8, 2025 at 10:41 PM Stephen Smalley
> > <stephen.smalley.work@gmail.com> wrote:
> > >
> > > On Fri, Aug 8, 2025 at 7:57 AM Stephen Smalley
> > > <stephen.smalley.work@gmail.com> wrote:
> >
> > Thanks for the review Stephen.
> >
> > > >
> > > > On Thu, Aug 7, 2025 at 3:57 AM Thiébaud Weksteen <tweek@google.com> wrote:
> > > > >
> > > > > Prior to this change, no security hooks were called at the creation of a
> > > > > memfd file. It means that, for SELinux as an example, it will receive
> > > > > the default type of the filesystem that backs the in-memory inode. In
> > > > > most cases, that would be tmpfs, but if MFD_HUGETLB is passed, it will
> > > > > be hugetlbfs. Both can be considered implementation details of memfd.
> > > > >
> > > > > It also means that it is not possible to differentiate between a file
> > > > > coming from memfd_create and a file coming from a standard tmpfs mount
> > > > > point.
> > > > >
> > > > > Additionally, no permission is validated at creation, which differs from
> > > > > the similar memfd_secret syscall.
> > > > >
> > > > > Call security_inode_init_security_anon during creation. This ensures
> > > > > that the file is setup similarly to other anonymous inodes. On SELinux,
> > > > > it means that the file will receive the security context of its task.
> > > > >
> > > > > The ability to limit fexecve on memfd has been of interest to avoid
> > > > > potential pitfalls where /proc/self/exe or similar would be executed
> > > > > [1][2]. Reuse the "execute_no_trans" and "entrypoint" access vectors,
> > > > > similarly to the file class. These access vectors may not make sense for
> > > > > the existing "anon_inode" class. Therefore, define and assign a new
> > > > > class "memfd_file" to support such access vectors.
> > > >
> > > > To provide backward compatibility, I would anticipate that you will
> > > > need to define a new SELinux policy capability and make this change
> > > > conditional on it being enabled, see:
> > > > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#adding-a-new-selinux-policy-capability
> > > > for instructions and links to examples.
> > > >
> >
> > I agree. Thanks, I'll add this to the next patchset.
> >
> > > > Otherwise, see below.
> > > >
> > > > >
> > > > > [1] https://crbug.com/1305267
> > > > > [2] https://lore.kernel.org/lkml/20221215001205.51969-1-jeffxu@google.com/
> > > > >
> > > > > Signed-off-by: Thiébaud Weksteen <tweek@google.com>
> > > > > ---
> > > > >  mm/memfd.c                          | 16 ++++++++++++++--
> > > > >  security/selinux/hooks.c            | 15 +++++++++++----
> > > > >  security/selinux/include/classmap.h |  2 ++
> > > > >  3 files changed, 27 insertions(+), 6 deletions(-)
> > > > >
> > > > > diff --git a/mm/memfd.c b/mm/memfd.c
> > > > > index bbe679895ef6..13bff0e91816 100644
> > > > > --- a/mm/memfd.c
> > > > > +++ b/mm/memfd.c
> > > > > @@ -433,6 +433,9 @@ static struct file *alloc_file(const char *name, unsigned int flags)
> > > > >  {
> > > > >         unsigned int *file_seals;
> > > > >         struct file *file;
> > > > > +       struct inode *inode;
> > > > > +       int err = 0;
> > > > > +       const char *anon_name = "[memfd]";
> > > > >
> > > > >         if (flags & MFD_HUGETLB) {
> > > > >                 file = hugetlb_file_setup(name, 0, VM_NORESERVE,
> > > > > @@ -444,12 +447,21 @@ static struct file *alloc_file(const char *name, unsigned int flags)
> > > > >         }
> > > > >         if (IS_ERR(file))
> > > > >                 return file;
> > > > > +
> > > > > +       inode = file_inode(file);
> > > > > +       err = security_inode_init_security_anon(inode,
> > > > > +                       LSM_ANON_INODE_MEMFD,
> > > > > +                       &QSTR(anon_name), NULL);
> > > >
> > > > Since the anon_name already indicates that this is a memfd, so can't
> > > > you already distinguish these via name-based type_transition rules?
> > > > Why do we need the enum argument?
> > >
> > > On second thought, I see that you are distinguishing not just the
> > > security context/type but also the security class, but the question
> > > remains: can't you compare the name to make this determination?
> > >
> >
> > I definitely can. My initial thought was that a string comparison
> > would not be adequate here (performance-wise), but I guess calls to
> > this method are infrequent enough that a strncmp would work here?
>
> Open to data showing otherwise, but would think that a fixed-size
> small string comparison would be in the noise here.
>

I agree.

> >
> > On the option of just relying on name-based type transitions, this has
> > an impact on the size of the policy: for Android, the transition from
> > ashmem to memfd means that virtually all domains will rely on such a
> > file. It means that we would need to define a type transition and a
> > new type for all our domains. (This is an argument on top of the need
> > for execute_no_trans, as I described in the commit message).
> >
> > > >
> > > > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > > > > index 8d36d5ebb6e5..49742930e706 100644
> > > > > --- a/security/selinux/hooks.c
> > > > > +++ b/security/selinux/hooks.c
> > > > > @@ -2367,8 +2367,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
> > > > >         ad.u.file = bprm->file;
> > > > >
> > > > >         if (new_tsec->sid == old_tsec->sid) {
> > > > > -               rc = avc_has_perm(old_tsec->sid, isec->sid,
> > > > > -                                 SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
> > > > > +               rc = avc_has_perm(old_tsec->sid, isec->sid, isec->sclass,
> > > > > +                                 FILE__EXECUTE_NO_TRANS, &ad);
> > > >
> > > > Here and below I am a little concerned that we could end up reaching
> > > > this code on an inode with an isec->sclass that does not define the
> > > > execute_no_trans and entrypoint permissions. We should do something to
> > > > make that never happens, or check for it and always deny in that case.
> >
> > I agree. I can add a condition to make sure only these 2 classes (file
> > or memfd_file) are used here.
> >
> > > >
> > > > >                 if (rc)
> > > > >                         return rc;
> > > > >         } else {
> > > > > @@ -2378,8 +2378,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
> > > > >                 if (rc)
> > > > >                         return rc;
> > > > >
> > > > > -               rc = avc_has_perm(new_tsec->sid, isec->sid,
> > > > > -                                 SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
> > > > > +               rc = avc_has_perm(new_tsec->sid, isec->sid, isec->sclass,
> > > > > +                                 FILE__ENTRYPOINT, &ad);
> > > > >                 if (rc)
> > > > >                         return rc;
> > > > >
> > > > > @@ -2997,6 +2997,13 @@ static int selinux_inode_init_security_anon(struct inode *inode,
> > > > >
> > > > >                 isec->sclass = context_isec->sclass;
> > > > >                 isec->sid = context_isec->sid;
> > > > > +       } else if (type == LSM_ANON_INODE_MEMFD) {
> > > > > +               isec->sclass = SECCLASS_MEMFD_FILE;
> > > > > +               rc = security_transition_sid(
> > > > > +                       sid, sid,
> > > > > +                       isec->sclass, name, &isec->sid);
> > > >
> > > > Again, name-based type_transitions ought to be able to distinguish
> > > > memfd based on the name argument IIUC.

^ permalink raw reply

* Re: [PATCH v3 08/12] bpf: Implement signature verification for BPF programs
From: Paul Moore @ 2025-08-13 22:17 UTC (permalink / raw)
  To: KP Singh; +Cc: bpf, linux-security-module, bboscaccy, kys, ast, daniel, andrii
In-Reply-To: <CACYkzJ7vBf3v-ezX1_xWp6HBJffDdUMHC3bgNUuSGUH-anKZKg@mail.gmail.com>

On Wed, Aug 13, 2025 at 5:37 PM KP Singh <kpsingh@kernel.org> wrote:
> On Wed, Aug 13, 2025 at 11:02 PM Paul Moore <paul@paul-moore.com> wrote:
> >
> > It's nice to see a v3 revision, but it would be good to see some
> > comments on Blaise's reply to your v2 revision.  From what I can see
> > it should enable the different use cases and requirements that have
> > been posted.
>
> I will defer to Alexei and others here (mostly due to time crunch). It
> would however be useful to explain the use-cases in which signed maps
> are useful (beyond being a different approach than the current
> delegated verification).

The use cases and requirements have been described quite a bit in
previous threads already, with both you and Alexei participating in
those discussions.  If you really can't find the threads on lore let
me know and I'll be happy to send you links to all of the various
threads from the past several months.

However, if I had to point to a single email that I felt best
summarized my requirements, I think it might be this:

<<< QUOTE >>>
The loader (+ implicit loader verification of maps w/original program)
signature verification scheme has been requested by Alexei/KP, and
that's fine, the code is trivial and if the user/admin is satisfied
with that as a solution, great.  However, the loader + map signature
verification scheme has some advantages and helps satisfy some
requirements that are not satisfied by only verifying the loader and
relying on the loader to verify the original program stored in the
maps.  One obvious advantage is that the lskel loader is much simpler
in this case as it doesn't need to worry about verification of the
program maps as that has already been done in bpf_check_signature().
I'm sure there are probably some other obvious reasons, but beyond the
one mentioned above, the other advantages that I'm interested in are a
little less obvious, or at least I haven't seen them brought up yet.
As I mentioned in an earlier thread, it's important to have the LSM
hook that handles authorization of a BPF program load *after* the BPF
program's signature has been verified.  This is not simply because the
LSM implementation might want to enforce and access control on a BPF
program load due to the signature state (signature verified vs no
signature), but also because the LSM might want to measure system
state and/or provide a record of the operation.  If we only verify the
lskel loader, at the point in time that the security_bpf_prog_load()
hook is called, we haven't properly verified both the loader and the
original BPF program stored in the map, that doesn't happen until much
later when the lskel loader executes.  Yes, I understand that may
sound very pedantic and fussy, but there are users who care very much
about those details, and if they see an event in the logs that
indicates that the BPF program signature has been verified as "good",
they need that log event to be fully, 100% true, and not have an
asterix of "only the lskel loader has been verified, the original BPF
program will potentially be verified later without any additional
events being logged to indicate the verification".
<<< /QUOTE >>>

The above was taken from this on-list email:
https://lore.kernel.org/linux-security-module/CAHC9VhQT=ymqssa9ymXtvssHTdVH_64T8Mpb0Mh8oxRD0Guo_Q@mail.gmail.com/

Of course I imagine Blaise might have a few things to add here, but
I'll let him comment on that if he has anything additional to add.

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH v3 08/12] bpf: Implement signature verification for BPF programs
From: KP Singh @ 2025-08-13 21:37 UTC (permalink / raw)
  To: Paul Moore
  Cc: bpf, linux-security-module, bboscaccy, kys, ast, daniel, andrii
In-Reply-To: <CAHC9VhR=VQ9QB6YfxOp2B8itj82PPtsiF8K+nyJCL26nFVdQww@mail.gmail.com>

On Wed, Aug 13, 2025 at 11:02 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Wed, Aug 13, 2025 at 4:55 PM KP Singh <kpsingh@kernel.org> wrote:
> >
> > This patch extends the BPF_PROG_LOAD command by adding three new fields
> > to `union bpf_attr` in the user-space API:
> >
> >   - signature: A pointer to the signature blob.
> >   - signature_size: The size of the signature blob.
> >   - keyring_id: The serial number of a loaded kernel keyring (e.g.,
> >     the user or session keyring) containing the trusted public keys.
> >
> > When a BPF program is loaded with a signature, the kernel:
> >
> > 1.  Retrieves the trusted keyring using the provided `keyring_id`.
> > 2.  Verifies the supplied signature against the BPF program's
> >     instruction buffer.
> > 3.  If the signature is valid and was generated by a key in the trusted
> >     keyring, the program load proceeds.
> > 4.  If no signature is provided, the load proceeds as before, allowing
> >     for backward compatibility. LSMs can chose to restrict unsigned
> >     programs and implement a security policy.
> > 5.  If signature verification fails for any reason,
> >     the program is not loaded.
> >
> > Signed-off-by: KP Singh <kpsingh@kernel.org>
> > ---
> >  crypto/asymmetric_keys/pkcs7_verify.c |  1 +
> >  include/linux/verification.h          |  1 +
> >  include/uapi/linux/bpf.h              | 10 +++++++
> >  kernel/bpf/helpers.c                  |  2 +-
> >  kernel/bpf/syscall.c                  | 42 ++++++++++++++++++++++++++-
> >  tools/include/uapi/linux/bpf.h        | 10 +++++++
> >  tools/lib/bpf/bpf.c                   |  2 +-
> >  7 files changed, 65 insertions(+), 3 deletions(-)
>
> It's nice to see a v3 revision, but it would be good to see some
> comments on Blaise's reply to your v2 revision.  From what I can see
> it should enable the different use cases and requirements that have
> been posted.

I will defer to Alexei and others here (mostly due to time crunch). It
would however be useful to explain the use-cases in which signed maps
are useful (beyond being a different approach than the current
delegated verification).

>
> https://lore.kernel.org/linux-security-module/87sei58vy3.fsf@microsoft.com


>
> --
> paul-moore.com

^ permalink raw reply

* Re: [PATCH v3 08/12] bpf: Implement signature verification for BPF programs
From: Paul Moore @ 2025-08-13 21:02 UTC (permalink / raw)
  To: KP Singh; +Cc: bpf, linux-security-module, bboscaccy, kys, ast, daniel, andrii
In-Reply-To: <20250813205526.2992911-9-kpsingh@kernel.org>

On Wed, Aug 13, 2025 at 4:55 PM KP Singh <kpsingh@kernel.org> wrote:
>
> This patch extends the BPF_PROG_LOAD command by adding three new fields
> to `union bpf_attr` in the user-space API:
>
>   - signature: A pointer to the signature blob.
>   - signature_size: The size of the signature blob.
>   - keyring_id: The serial number of a loaded kernel keyring (e.g.,
>     the user or session keyring) containing the trusted public keys.
>
> When a BPF program is loaded with a signature, the kernel:
>
> 1.  Retrieves the trusted keyring using the provided `keyring_id`.
> 2.  Verifies the supplied signature against the BPF program's
>     instruction buffer.
> 3.  If the signature is valid and was generated by a key in the trusted
>     keyring, the program load proceeds.
> 4.  If no signature is provided, the load proceeds as before, allowing
>     for backward compatibility. LSMs can chose to restrict unsigned
>     programs and implement a security policy.
> 5.  If signature verification fails for any reason,
>     the program is not loaded.
>
> Signed-off-by: KP Singh <kpsingh@kernel.org>
> ---
>  crypto/asymmetric_keys/pkcs7_verify.c |  1 +
>  include/linux/verification.h          |  1 +
>  include/uapi/linux/bpf.h              | 10 +++++++
>  kernel/bpf/helpers.c                  |  2 +-
>  kernel/bpf/syscall.c                  | 42 ++++++++++++++++++++++++++-
>  tools/include/uapi/linux/bpf.h        | 10 +++++++
>  tools/lib/bpf/bpf.c                   |  2 +-
>  7 files changed, 65 insertions(+), 3 deletions(-)

It's nice to see a v3 revision, but it would be good to see some
comments on Blaise's reply to your v2 revision.  From what I can see
it should enable the different use cases and requirements that have
been posted.

https://lore.kernel.org/linux-security-module/87sei58vy3.fsf@microsoft.com

-- 
paul-moore.com

^ 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