Skip to contentSkip to conversation

Reintroduce Hornet LSM

PATCH Applied Blaise Boscaccy 5 participants 34 messages May 7 – May 13

Subsystems (9)
Documentation Kbuild Kselftest BPF +5 more subsystems
lore
Share & tools
lore
#0 lore raw

v7 cover Reintroduce Hornet LSM

This patch series introduces the next iteration of the Hornet LSM. Hornet’s goal is to provide a secure and extensible in-kernel signature verification mechanism for eBPF programs.

Hornet addresses concerns from users who require strict audit trails and verification guarantees for eBPF programs, especially in security-sensitive environments. Many production systems need assurance that only authorized, unmodified eBPF programs are loaded into the kernel. Hornet provides this assurance through cryptographic signature verification.

The currently accepted loader-plus-map signature verification scheme, mandated by Alexei and KP, is simple to implement and generally acceptable if users and administrators are satisfied with it. However, verifying both the loader and the maps offers additional benefits beyond verifying the loader alone:

1. Security and Audit Integrity

A key advantage is that the LSM hook for authorizing BPF program loads can operate after signature verification. This ensures:

* Access control decisions are based on verified signature status.
* Accurate system state measurement and logging.
* Log entries claiming a verified signature are truthful, avoiding misleading records where only the loader was verified while the actual BPF program verification occurs later without logging.

2. TOCTOU Attack Prevention

The current map hash implementation may be vulnerable to a TOCTOU attack because it allows unfrozen maps to cache a previously calculated hash. The accepted “trusted loader” scheme cannot detect this and may permit loading altered maps.

3. Supply Chain Integrity

Verify that eBPF programs and their associated map data have not been modified since they were built and signed, in the kernel proper, may aid in protecting against supply chain attacks.

This approach addresses concerns from users who require strict audit trails and verification guarantees, especially in security-sensitive environments. Map hashes for extended verification are passed via the existing PKCS#7 UAPI and verified by the crypto subsystem. Hornet then calculates the program’s verification state. Hornet itself does not enforce a policy on whether unsigned or partially signed programs should be rejected. It delegates that decision to downstream LSMs hook, making it a composable building block in a larger security architecture.

Changes in V7:
- minor IPE fixes
- tooling fixes in gen_sig
- checkpatch fixes

Link to V6: https://lore.kernel.org/linux-security-module/[email protected]/

Changes in V6:
- Doc cleanup
- Error handling improvements
- Support for shareable maps

Link to V5: https://lore.kernel.org/linux-security-module/[email protected]/

Changes in V5:
- Added IPE documentation

Link to V4: https://lore.kernel.org/linux-security-module/[email protected]/

Changes in V4:
- IPE integration
- Arbitrary keyring support

Link to V3: https://lore.kernel.org/linux-security-module/[email protected]/

Changes in V3:
- Updated for signed attribute patch series changes
- Added some new result enum values
- Minor documentation clarification
- Misc style fixes
- Added missing signed-off-by tags

Link to V2: https://lore.kernel.org/linux-security-module/[email protected]/

Changes in V2:
- Addressed possible TocTou races in hash verification
- Improved documentation and tooling
- Added Alexie's nack

Link to RFC: https://lore.kernel.org/linux-security-module/[email protected]/

Blaise Boscaccy (6):
lsm: security: Add additional enum values for bpf integrity checks
security: Hornet LSM
hornet: Introduce gen_sig
hornet: Add a light skeleton data extractor scripts selftests/hornet: Add a selftest for the Hornet LSM
ipe: Add BPF program load policy enforcement via Hornet integration

James Bottomley (3):
crypto: pkcs7: add flag for validated trust on a signed info block
crypto: pkcs7: add ability to extract signed attributes by OID
crypto: pkcs7: add tests for pkcs7_get_authattr

Paul Moore (1):
lsm: framework for BPF integrity verification

 Documentation/admin-guide/LSM/Hornet.rst     | 323 +++++++++++++++
 Documentation/admin-guide/LSM/index.rst      |   1 +
 Documentation/admin-guide/LSM/ipe.rst        | 162 +++++++-
 Documentation/security/ipe.rst               |  68 ++++
 MAINTAINERS                                  |   9 +
 certs/system_keyring.c                       |   1 +
 crypto/asymmetric_keys/Makefile              |   4 +-
 crypto/asymmetric_keys/pkcs7_aa.asn1         |  18 +
 crypto/asymmetric_keys/pkcs7_key_type.c      |  44 +-
 crypto/asymmetric_keys/pkcs7_parser.c        |  81 ++++
 crypto/asymmetric_keys/pkcs7_parser.h        |   1 +
 crypto/asymmetric_keys/pkcs7_trust.c         |   1 +
 include/crypto/pkcs7.h                       |   4 +
 include/linux/lsm_hook_defs.h                |   5 +
 include/linux/oid_registry.h                 |   3 +
 include/linux/security.h                     |  28 ++
 include/uapi/linux/lsm.h                     |   1 +
 scripts/Makefile                             |   1 +
 scripts/hornet/Makefile                      |   5 +
 scripts/hornet/extract-insn.sh               |  27 ++
 scripts/hornet/extract-map.sh                |  27 ++
 scripts/hornet/extract-skel.sh               |  27 ++
 scripts/hornet/gen_sig.c                     | 401 +++++++++++++++++++
 scripts/hornet/write-sig.sh                  |  27 ++
 security/Kconfig                             |   3 +-
 security/Makefile                            |   1 +
 security/hornet/Kconfig                      |  13 +
 security/hornet/Makefile                     |   7 +
 security/hornet/hornet.asn1                  |  12 +
 security/hornet/hornet_lsm.c                 | 352 ++++++++++++++++
 security/ipe/Kconfig                         |  15 +
 security/ipe/audit.c                         |  15 +
 security/ipe/eval.c                          |  93 ++++-
 security/ipe/eval.h                          |  11 +
 security/ipe/hooks.c                         |  63 +++
 security/ipe/hooks.h                         |  15 +
 security/ipe/ipe.c                           |  14 +
 security/ipe/ipe.h                           |   3 +
 security/ipe/policy.h                        |  14 +
 security/ipe/policy_parser.c                 |  27 ++
 security/security.c                          |  75 +++-
 tools/testing/selftests/Makefile             |   1 +
 tools/testing/selftests/hornet/Makefile      |  63 +++
 tools/testing/selftests/hornet/loader.c      |  21 +
 tools/testing/selftests/hornet/trivial.bpf.c |  33 ++
 45 files changed, 2112 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
 create mode 100644 crypto/asymmetric_keys/pkcs7_aa.asn1
 create mode 100644 scripts/hornet/Makefile
 create mode 100755 scripts/hornet/extract-insn.sh
 create mode 100755 scripts/hornet/extract-map.sh
 create mode 100755 scripts/hornet/extract-skel.sh
 create mode 100644 scripts/hornet/gen_sig.c
 create mode 100755 scripts/hornet/write-sig.sh
 create mode 100644 security/hornet/Kconfig
 create mode 100644 security/hornet/Makefile
 create mode 100644 security/hornet/hornet.asn1
 create mode 100644 security/hornet/hornet_lsm.c
 create mode 100644 tools/testing/selftests/hornet/Makefile
 create mode 100644 tools/testing/selftests/hornet/loader.c
 create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c
Signature · 1 line
2.53.0
#1 lore raw

PATCH v7 1/10 crypto: pkcs7: add flag for validated trust on a signed info block

From: James Bottomley <[email protected]>

Allow consumers of struct pkcs7_message to tell if any of the sinfo fields has passed a trust validation. Note that this does not happen in parsing, pkcs7_validate_trust() must be explicitly called or called via validate_pkcs7_trust(). Since the way to get this trusted pkcs7 object is via verify_pkcs7_message_sig, export that so modules can use it.

---
 certs/system_keyring.c                | 1 +
 crypto/asymmetric_keys/pkcs7_parser.h | 1 +
 crypto/asymmetric_keys/pkcs7_trust.c  | 1 +
 3 files changed, 3 insertions(+)
certs/system_keyring.c +1 -0
index e0761436ec7f4..9bda49295bd02 100644
@@ -380,6 +380,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len,
 	pr_devel("<==%s() = %d\n", __func__, ret);
 	return ret;
 }
+EXPORT_SYMBOL(verify_pkcs7_message_sig);
 
 /**
  * verify_pkcs7_signature - Verify a PKCS#7-based signature on system data.
crypto/asymmetric_keys/pkcs7_parser.h +1 -0
index 6ef9f335bb17f..203062a33def6 100644
@@ -20,6 +20,7 @@ struct pkcs7_signed_info {
 	unsigned	index;
 	bool		unsupported_crypto;	/* T if not usable due to missing crypto */
 	bool		blacklisted;
+	bool		verified; /* T if this signer has validated trust */
 
 	/* Message digest - the digest of the Content Data (or NULL) */
 	const void	*msgdigest;
crypto/asymmetric_keys/pkcs7_trust.c +1 -0
index 9a87c34ed1733..78ebfb6373b61 100644
@@ -127,6 +127,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
 		for (p = sinfo->signer; p != x509; p = p->signer)
 			p->verified = true;
 	}
+	sinfo->verified = true;
 	kleave(" = 0");
 	return 0;
 }
Signature · 1 line
2.53.0
#2 lore raw

Re: [PATCH v7 01/10] crypto: pkcs7: add flag for validated trust on a signed info block

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [High] Exporting `verify_pkcs7_message_sig` as `EXPORT_SYMBOL` creates a non-GPL wrapper around strictly…

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Exporting verify_pkcs7_message_sig as EXPORT_SYMBOL creates a non-GPL wrapper around strictly GPL-only PKCS7 APIs.
- [High] The verified flags on x509_certificate and pkcs7_signed_info are not cleared between validations, causing severe state leakage and API breakage on subsequent validations.
- [Medium] The sinfo->verified flag is set to true for blacklisted signatures, creating a dangerous API hazard.
- [Medium] Concurrency data race on internal flags in pkcs7_message when verify_pkcs7_message_sig is called.
--

commit 4629d1adf43a0253ba7082b6c379d2bf57a8c8ce
Author: James Bottomley <[email protected]>
crypto: pkcs7: add flag for validated trust on a signed info block

This commit adds a flag to struct pkcs7_message to indicate if any sinfo fields have passed trust validation. It sets the verified flag during trust validation and exports verify_pkcs7_message_sig for module use.

certs/system_keyring.c int verify_pkcs7_message_sig(const void *data, size_t len, · line 383

Quoted 9 lines
certs/system_keyring.c +1 -0
index e0761436ec7f4..9bda49295bd02 100644
@@ -380,6 +380,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len,
 	pr_devel("<==%s() = %d\n", __func__, ret);
 	return ret;
 }
+EXPORT_SYMBOL(verify_pkcs7_message_sig);

Since the underlying subsystem functions like pkcs7_verify and pkcs7_validate_trust, as well as the alternative wrapper verify_pkcs7_signature, are all explicitly exported as EXPORT_SYMBOL_GPL, does exporting verify_pkcs7_message_sig as EXPORT_SYMBOL allow proprietary modules to bypass the GPL requirement and access internal cryptographic evaluations?

Should this use EXPORT_SYMBOL_GPL to match the rest of the subsystem?

Quoted 4 lines
crypto/asymmetric_keys/pkcs7_trust.c +0 -0
index 9a87c34ed1733..78ebfb6373b61 100644

[ ... ]

crypto/asymmetric_keys/pkcs7_trust.c static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, · line 130

Quoted 5 lines
crypto/asymmetric_keys/pkcs7_trust.c +1 -0
@@ -127,6 +127,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
 		for (p = sinfo->signer; p != x509; p = p->signer)
 			p->verified = true;
 	}
+	sinfo->verified = true;

If a consumer validates a message successfully against a keyring, the verified flags are permanently set to true since they are not cleared between validations.

If the same object is subsequently verified again, verify_pkcs7_message_sig first runs is_key_on_revocation_list using the blacklist_keyring. During this blacklist check, if the message has multiple sinfo blocks signed by the same key, the first block fails but sets seen to true.

The second block observes seen as true and the stale verified flag as true from the previous run. Will this falsely evaluate to success, causing is_key_on_revocation_list to return -EKEYREJECTED and making a previously validated message fail subsequent verifications?

Additionally, when verify_pkcs7_message_sig checks the blacklist, it calls pkcs7_validate_trust with the blacklist_keyring. If a signature matches a revoked key, pkcs7_validate_trust returns success and sets sinfo->verified to true.

Even though the outer function correctly returns -EKEYREJECTED, does this leave the parsed pkcs7 object with verified set to true for a revoked key? Could this confuse consumers inspecting the verified state after a failed call?

Finally, if multiple threads concurrently call verify_pkcs7_message_sig on the same pkcs7_message object, will the unprotected modifications to internal flags like p->seen, x509->verified, and sinfo->verified cause data races?

Could this lack of synchronization lead to random validation failures or incorrect trust bypasses?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
maintainer Applied
#3 lore raw

Re: [PATCH v7 1/10] crypto: pkcs7: add flag for validated trust on a signed info block

In reply to Blaise Boscaccy
Quoted 8 lines · On May 7, 2026 Blaise Boscaccy <[email protected]> wrote:

Allow consumers of struct pkcs7_message to tell if any of the sinfo fields has passed a trust validation. Note that this does not happen in parsing, pkcs7_validate_trust() must be explicitly called or called via validate_pkcs7_trust(). Since the way to get this trusted pkcs7 object is via verify_pkcs7_message_sig, export that so modules can use it.

Quoted 7 lines
---
 certs/system_keyring.c                | 1 +
 crypto/asymmetric_keys/pkcs7_parser.h | 1 +
 crypto/asymmetric_keys/pkcs7_trust.c  | 1 +
 3 files changed, 3 insertions(+)

Merged into lsm/dev, thanks.

--
paul-moore.com

#4 lore raw

PATCH v7 2/10 crypto: pkcs7: add ability to extract signed attributes by OID

In reply to Blaise Boscaccy

From: James Bottomley <[email protected]>

Signers may add any information they like in signed attributes and sometimes this information turns out to be relevant to specific signing cases, so add an api pkcs7_get_authattr() to extract the value of an authenticated attribute by specific OID. The current implementation is designed for the single signer use case and simply terminates the search when it finds the relevant OID.

---
 crypto/asymmetric_keys/Makefile       |  4 +-
 crypto/asymmetric_keys/pkcs7_aa.asn1  | 18 ++++++
 crypto/asymmetric_keys/pkcs7_parser.c | 81 +++++++++++++++++++++++++++
 include/crypto/pkcs7.h                |  4 ++
 4 files changed, 106 insertions(+), 1 deletion(-)
 create mode 100644 crypto/asymmetric_keys/pkcs7_aa.asn1
crypto/asymmetric_keys/Makefile +3 -1
index bc65d3b98dcbf..f99b7169ae7cd 100644
@@ -53,12 +53,14 @@ clean-files	+= pkcs8.asn1.c pkcs8.asn1.h
 obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
 pkcs7_message-y := \
 	pkcs7.asn1.o \
+	pkcs7_aa.asn1.o \
 	pkcs7_parser.o \
 	pkcs7_trust.o \
 	pkcs7_verify.o
 
-$(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h
+$(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h $(obj)/pkcs7_aa.asn1.h
 $(obj)/pkcs7.asn1.o: $(obj)/pkcs7.asn1.c $(obj)/pkcs7.asn1.h
+$(obj)/pkcs7_aa.asn1.o: $(obj)/pkcs7_aa.asn1.c $(obj)/pkcs7_aa.asn1.h
 
 #
 # PKCS#7 parser testing key
crypto/asymmetric_keys/pkcs7_aa.asn1 new file +18 -0
new file mode 100644
index 0000000000000..7a8857bdf56e1
@@ -0,0 +1,18 @@
+-- SPDX-License-Identifier: BSD-3-Clause
+--
+-- Copyright (C) 2009 IETF Trust and the persons identified as authors
+-- of the code
+--
+-- https://www.rfc-editor.org/rfc/rfc5652#section-3
+
+AA ::= 	CHOICE {
+	aaSet		[0] IMPLICIT AASet,
+	aaSequence	[2] EXPLICIT SEQUENCE OF AuthenticatedAttribute
+}
+
+AASet ::= SET OF AuthenticatedAttribute
+
+AuthenticatedAttribute ::= SEQUENCE {
+	type	OBJECT IDENTIFIER ({ pkcs7_aa_note_OID }),
+	values	SET OF ANY ({ pkcs7_aa_note_attr })
+}
crypto/asymmetric_keys/pkcs7_parser.c +81 -0

93 lines not on this page · raw message

include/crypto/pkcs7.h +4 -0
index 38ec7f5f90411..bd83202cd805c 100644
@@ -25,6 +25,10 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
 extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
 				  const void **_data, size_t *_datalen,
 				  size_t *_headerlen);
+extern int pkcs7_get_authattr(const struct pkcs7_message *pkcs7,
+			      enum OID oid,
+			      const void **_data, size_t *_len);
+
 
 /*
  * pkcs7_trust.c
Signature · 1 line
2.53.0
#5 lore raw

Re: [PATCH v7 02/10] crypto: pkcs7: add ability to extract signed attributes by OID

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] Premature loop termination if a verified signer lacks authenticated attributes (`sinfo->authattrs…

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Premature loop termination if a verified signer lacks authenticated attributes (sinfo->authattrs == NULL).
- [Medium] The function returns the last matching attribute value rather than the first one, contradicting its API documentation. --

commit 8f64855e85aaa27bba380d666827191e50cceb99
Author: James Bottomley <[email protected]>

crypto: pkcs7: add ability to extract signed attributes by OID

This commit adds an API, pkcs7_get_authattr(), to extract the value of an authenticated attribute by a specific OID. It parses the signed attributes and is intended to return the first matching value.

Quoted 4 lines
crypto/asymmetric_keys/pkcs7_parser.c +0 -0
index 6e3ffdac83ace..d467866f7d930 100644

[ ... ]

Quoted 1 line
crypto/asymmetric_keys/pkcs7_parser.c +0 -0
@@ -211,6 +212,86 @@ int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,

[ ... ]

crypto/asymmetric_keys/pkcs7_parser.c int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, · line 246

Quoted 13 lines
crypto/asymmetric_keys/pkcs7_parser.c +13 -0
+int pkcs7_aa_note_attr(void *context, size_t hdrlen,
+		       unsigned char tag,
+		       const void *value, size_t vlen)
+{
+	struct pkcs7_aa_context *ctx = context;
+
+	if (ctx->found) {
+		ctx->data = value;
+		ctx->len = vlen;
+	}
+
+	return 0;
+}

The documentation for pkcs7_get_authattr() mentions it returns the first match in parse order.

If an authenticated attribute contains multiple values within its SET OF ANY, or if multiple attributes share the same OID, won't this callback continuously overwrite ctx->data and return the last matching value instead of the first one?

Could we enforce capturing only the first value by checking if ctx->data is already set before assigning it?

[ ... ]

Quoted 12 lines
crypto/asymmetric_keys/pkcs7_parser.c +12 -0
+int pkcs7_get_authattr(const struct pkcs7_message *pkcs7,
+		       enum OID oid,
+		       const void **_data, size_t *_len)
+{
+	struct pkcs7_signed_info *sinfo = pkcs7->signed_infos;
+	struct pkcs7_aa_context ctx;
+
+	ctx.data = NULL;
+	ctx.oid_to_find = oid;
+
+	for (; sinfo; sinfo = sinfo->next) {
+		int ret;

crypto/asymmetric_keys/pkcs7_parser.c int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, · line 283

Quoted 10 lines
crypto/asymmetric_keys/pkcs7_parser.c +10 -0
+
+		/* only extract OIDs from validated signers */
+		if (!sinfo->verified)
+			continue;
+
+		ret = asn1_ber_decoder(&pkcs7_aa_decoder, &ctx,
+				       sinfo->authattrs, sinfo->authattrs_len);
+		if (ret < 0 || ctx.data != NULL)
+			break;
+	}

PKCS#7 allows authenticated attributes to be optional, meaning sinfo->authattrs could legitimately be NULL with a length of 0.

If a verified signer has no authenticated attributes, asn1_ber_decoder() will be called with datalen == 0 and fail with a data overrun error, returning a negative value.

Because the loop breaks on ret < 0, won't this permanently abort the search across all subsequent signers instead of skipping to the next one?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
maintainer Applied
#6 lore raw

Re: [PATCH v7 2/10] crypto: pkcs7: add ability to extract signed attributes by OID

In reply to Blaise Boscaccy
Quoted 10 lines · On May 7, 2026 Blaise Boscaccy <[email protected]> wrote:

Signers may add any information they like in signed attributes and sometimes this information turns out to be relevant to specific signing cases, so add an api pkcs7_get_authattr() to extract the value of an authenticated attribute by specific OID. The current implementation is designed for the single signer use case and simply terminates the search when it finds the relevant OID.

Quoted 7 lines
---
 crypto/asymmetric_keys/Makefile       |  4 +-
 crypto/asymmetric_keys/pkcs7_aa.asn1  | 18 ++++++
 crypto/asymmetric_keys/pkcs7_parser.c | 81 +++++++++++++++++++++++++++
 include/crypto/pkcs7.h                |  4 ++
 4 files changed, 106 insertions(+), 1 deletion(-)
 create mode 100644 crypto/asymmetric_keys/pkcs7_aa.asn1

Merged into lsm/dev, thanks.

--
paul-moore.com

#7 lore raw

PATCH v7 3/10 crypto: pkcs7: add tests for pkcs7_get_authattr

In reply to Blaise Boscaccy

From: James Bottomley <[email protected]>

Add example code to the test module pkcs7_key_type.c that verifies a message and then pulls out a known authenticated attribute.

---
 crypto/asymmetric_keys/pkcs7_key_type.c | 44 ++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)
crypto/asymmetric_keys/pkcs7_key_type.c +43 -1
index b930d3bbf1af5..e0b1ce0202f6d 100644
@@ -12,6 +12,7 @@
 #include <linux/verification.h>
 #include <linux/key-type.h>
 #include <keys/user-type.h>
+#include <crypto/pkcs7.h>
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("PKCS#7 testing key type");
@@ -51,16 +52,57 @@ static int pkcs7_view_content(void *ctx, const void *data, size_t len,
 static int pkcs7_preparse(struct key_preparsed_payload *prep)
 {
 	enum key_being_used_for usage = pkcs7_usage;
+	int ret;
+	struct pkcs7_message *pkcs7;
+	const void *data;
+	size_t len;
 
 	if (usage >= NR__KEY_BEING_USED_FOR) {
 		pr_err("Invalid usage type %d\n", usage);
 		return -EINVAL;
 	}
 
-	return verify_pkcs7_signature(NULL, 0,
+	ret = verify_pkcs7_signature(NULL, 0,
 				      prep->data, prep->datalen,
 				      VERIFY_USE_SECONDARY_KEYRING, usage,
 				      pkcs7_view_content, prep);
+	if (ret)
+		return ret;
+
+	pkcs7 = pkcs7_parse_message(prep->data, prep->datalen);
+	if (IS_ERR(pkcs7)) {
+		pr_err("pkcs7 parse error\n");
+		return PTR_ERR(pkcs7);
+	}
+
+	/*
+	 * the parsed message has no trusted signer, so nothing should
+	 * be returned here
+	 */
+	ret = pkcs7_get_authattr(pkcs7, OID_messageDigest, &data, &len);
+	if (ret == 0) {
+		pr_err("OID returned when no trust in signer\n");
+		goto out;
+	}
+	/* add trust and check again */
+	ret = verify_pkcs7_message_sig(NULL, 0, pkcs7,
+				       VERIFY_USE_SECONDARY_KEYRING, usage,
+				       NULL, NULL);
+	if (ret) {
+		pr_err("verify_pkcs7_message_sig failed!!\n");
+		goto out;
+	}
+	/* now we should find the OID */
+	ret = pkcs7_get_authattr(pkcs7, OID_messageDigest, &data, &len);
+	if (ret) {
+		pr_err("Failed to get message digest\n");
+		goto out;
+	}
+	pr_info("Correctly Got message hash, size=%zu\n", len);
+
+ out:
+	pkcs7_free_message(pkcs7);
+	return 0;
 }
 
 /*
Signature · 1 line
2.53.0
#8 lore raw

Re: [PATCH v7 03/10] crypto: pkcs7: add tests for pkcs7_get_authattr

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] Test failures in `pkcs7_preparse` are silently masked because the function unconditionally…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Test failures in pkcs7_preparse are silently masked because the function unconditionally returns 0 instead of propagating the error code. --

commit 49875871a24595817e686cb5ad55b01eebf05536
Author: James Bottomley <[email protected]>

crypto: pkcs7: add tests for pkcs7_get_authattr

Add example code to the test module pkcs7_key_type.c that verifies a message and then pulls out a known authenticated attribute.

Quoted 4 lines
crypto/asymmetric_keys/pkcs7_key_type.c +0 -0
index b930d3bbf1af5..e0b1ce0202f6d 100644

[ ... ]

Quoted 3 lines
crypto/asymmetric_keys/pkcs7_key_type.c +0 -0
@@ -51,16 +52,57 @@ static int pkcs7_view_content(void *ctx, const void *data, size_t len,
 static int pkcs7_preparse(struct key_preparsed_payload *prep)
 {

[ ... ]

crypto/asymmetric_keys/pkcs7_key_type.c static int pkcs7_view_content(void *ctx, const void *data, size_t len, · line 85

Quoted 8 lines
crypto/asymmetric_keys/pkcs7_key_type.c +8 -0
+	/*
+	 * the parsed message has no trusted signer, so nothing should
+	 * be returned here
+	 */
+	ret = pkcs7_get_authattr(pkcs7, OID_messageDigest, &data, &len);
+	if (ret == 0) {
+		pr_err("OID returned when no trust in signer\n");
+		goto out;

If the test fails here, does this need to set ret to a negative error code like -EINVAL before jumping to the out label? Since ret is 0 here, returning ret at the end would still signal success.

Quoted 10 lines
crypto/asymmetric_keys/pkcs7_key_type.c +10 -0
+	}
+	/* add trust and check again */
+	ret = verify_pkcs7_message_sig(NULL, 0, pkcs7,
+				       VERIFY_USE_SECONDARY_KEYRING, usage,
+				       NULL, NULL);
+	if (ret) {
+		pr_err("verify_pkcs7_message_sig failed!!\n");
+		goto out;
+	}
+	/* now we should find the OID */

crypto/asymmetric_keys/pkcs7_key_type.c static int pkcs7_view_content(void *ctx, const void *data, size_t len, · line 105

Quoted 10 lines
crypto/asymmetric_keys/pkcs7_key_type.c +10 -0
+	ret = pkcs7_get_authattr(pkcs7, OID_messageDigest, &data, &len);
+	if (ret) {
+		pr_err("Failed to get message digest\n");
+		goto out;
+	}
+	pr_info("Correctly Got message hash, size=%zu\n", len);
+
+ out:
+	pkcs7_free_message(pkcs7);
+	return 0;

Should this return ret instead of 0? If any of the earlier assertions fail and jump to the out label, returning 0 unconditionally discards the error state and signals a successful key instantiation to the keys subsystem.

Quoted 1 line

}

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
maintainer Applied
#9 lore raw

Re: [PATCH v7 3/10] crypto: pkcs7: add tests for pkcs7_get_authattr

In reply to Blaise Boscaccy
Quoted 10 lines · On May 7, 2026 Blaise Boscaccy <[email protected]> wrote:

Add example code to the test module pkcs7_key_type.c that verifies a message and then pulls out a known authenticated attribute.

---
 crypto/asymmetric_keys/pkcs7_key_type.c | 44 ++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

Merged into lsm/dev, thanks.

--
paul-moore.com

#10 lore raw

PATCH v7 4/10 lsm: framework for BPF integrity verification

In reply to Blaise Boscaccy

From: Paul Moore <[email protected]>

Add a new LSM hook and two new LSM hook callbacks to support LSMs that perform integrity verification, e.g. digital signature verification, of BPF programs.

While the BPF subsystem does implement a signature verification scheme, it does not satisfy a number of existing requirements, adding support for BPF program integrity verification to the LSM framework allows administrators to select additional integrity verification mechanisms to meet these needs while also providing a mechanism for future expansion. Additional on why this is necessary can be found at the lore archive link below:

https://lore.kernel.org/linux-security-module/CAHC9VhTQ_DR=ANzoDBjcCtrimV7XcCZVUsANPt=TjcvM4d-vjg@mail.gmail.com/

The LSM-based BPF integrity verification mechanism works within the existing security_bpf_prog_load() hook called by the BPF subsystem. It adds an additional dedicated integrity callback and a new LSM hook/callback to be called from within LSMs implementing integrity verification.

The first new callback, bpf_prog_load_integrity(), located within the security_bpf_prog_load() hook, is necessary to ensure that the integrity verification callbacks are executed before any of the existing LSMs are executed via the bpf_prog_load() callback. Reusing the existing bpf_prog_load() callback for integrity verification could result in LSMs not having access to the integrity verification results when asked to authorize the BPF program load in the bpf_prog_load() callback.

The new LSM hook, security_bpf_prog_load_post_integrity(), is intended to be called from within LSMs performing BPF program integrity verification. It is used to report the verdict of the integrity verification to other LSMs enforcing access control policy on BPF program loads. LSMs enforcing such access controls should register a bpf_prog_load_post_integrity() callback to receive integrity verdicts.

More information on these new callbacks and hook can be found in the code comments in this patch.

---
 include/linux/lsm_hook_defs.h |  5 +++
 include/linux/security.h      | 25 ++++++++++++
 security/security.c           | 75 +++++++++++++++++++++++++++++++++--
 3 files changed, 102 insertions(+), 3 deletions(-)
include/linux/lsm_hook_defs.h +5 -0
index 8c42b4bde09c0..4971d3c36d5b4 100644
@@ -434,6 +434,11 @@ LSM_HOOK(int, 0, bpf_prog, struct bpf_prog *prog)
 LSM_HOOK(int, 0, bpf_map_create, struct bpf_map *map, union bpf_attr *attr,
 	 struct bpf_token *token, bool kernel)
 LSM_HOOK(void, LSM_RET_VOID, bpf_map_free, struct bpf_map *map)
+LSM_HOOK(int, 0, bpf_prog_load_post_integrity, struct bpf_prog *prog,
+	 union bpf_attr *attr, struct bpf_token *token, bool kernel,
+	 const struct lsm_id *lsmid, enum lsm_integrity_verdict verdict)
+LSM_HOOK(int, 0, bpf_prog_load_integrity, struct bpf_prog *prog,
+	 union bpf_attr *attr, struct bpf_token *token, bool kernel)
 LSM_HOOK(int, 0, bpf_prog_load, struct bpf_prog *prog, union bpf_attr *attr,
 	 struct bpf_token *token, bool kernel)
 LSM_HOOK(void, LSM_RET_VOID, bpf_prog_free, struct bpf_prog *prog)
include/linux/security.h +25 -0
index ee88dd2d2d1f7..b3fd04baa78d0 100644
@@ -67,6 +67,7 @@ enum fs_value_type;
 struct watch;
 struct watch_notification;
 struct lsm_ctx;
+struct lsm_id;
 
 /* Default (no) options for the capable function */
 #define CAP_OPT_NONE 0x0
@@ -100,6 +101,14 @@ enum lsm_integrity_type {
 	LSM_INT_FSVERITY_BUILTINSIG_VALID,
 };
 
+enum lsm_integrity_verdict {
+	LSM_INT_VERDICT_NONE = 0,
+	LSM_INT_VERDICT_OK,
+	LSM_INT_VERDICT_UNSIGNED,
+	LSM_INT_VERDICT_PARTIALSIG,
+	LSM_INT_VERDICT_BADSIG,
+};
+
 /*
  * These are reasons that can be passed to the security_locked_down()
  * LSM hook. Lockdown reasons that protect kernel integrity (ie, the
@@ -2270,6 +2279,12 @@ extern int security_bpf_prog(struct bpf_prog *prog);
 extern int security_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
 				   struct bpf_token *token, bool kernel);
 extern void security_bpf_map_free(struct bpf_map *map);
+extern int security_bpf_prog_load_post_integrity(struct bpf_prog *prog,
+					union bpf_attr *attr,
+					struct bpf_token *token,
+					bool kernel,
+					const struct lsm_id *lsmid,
+					enum lsm_integrity_verdict verdict);
 extern int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
 				  struct bpf_token *token, bool kernel);
 extern void security_bpf_prog_free(struct bpf_prog *prog);
@@ -2304,6 +2319,16 @@ static inline int security_bpf_map_create(struct bpf_map *map, union bpf_attr *a
 static inline void security_bpf_map_free(struct bpf_map *map)
 { }
 
+static inline int security_bpf_prog_load_post_integrity(struct bpf_prog *prog,
+					  union bpf_attr *attr,
+					  struct bpf_token *token,
+					  bool kernel,
+					  const struct lsm_id *lsmid,
+					  enum lsm_integrity_verdict verdict)
+{
+	return 0;
+}
+
 static inline int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
 					 struct bpf_token *token, bool kernel)
 {
security/security.c +72 -3

95 lines not on this page · raw message

Signature · 1 line
2.53.0
#11 lore raw

Re: [PATCH v7 04/10] lsm: framework for BPF integrity verification

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Double execution of `security_bpf_prog_free()` on error paths, leading to duplicate invocation of…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Double execution of security_bpf_prog_free() on error paths, leading to duplicate invocation of LSM cleanup hooks. --

commit da3e568b0e83968c7e672f02d2dc4e5b4cb4a3e9
Author: Paul Moore <[email protected]>

lsm: framework for BPF integrity verification

Add a new LSM hook and two new LSM hook callbacks to support LSMs that perform integrity verification, e.g. digital signature verification, of BPF programs.

Quoted 4 lines
security/security.c +0 -0
index a26c1474e2e49..bb78f7e45a98f 100644

[ ... ]

Quoted 9 lines
security/security.c +4 -0
@@ -5255,9 +5315,18 @@ int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
 	if (unlikely(rc))
 		return rc;
 
+	rc = call_int_hook(bpf_prog_load_integrity, prog, attr, token, kernel);
+	if (unlikely(rc))
+		goto err;
+
 	rc = call_int_hook(bpf_prog_load, prog, attr, token, kernel);

security/security.c int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr, · line 5331

Quoted 10 lines
security/security.c +6 -1
 	if (unlikely(rc))
-		security_bpf_prog_free(prog);
+		goto err;
+
+	return rc;
+
+err:
+	security_bpf_prog_free(prog);
 	return rc;
 }

Does this introduce a regression by causing a double execution of security_bpf_prog_free() on error paths?

If either the new bpf_prog_load_integrity hook or the existing bpf_prog_load hook fails, the code jumps to the err label, calls security_bpf_prog_free(), and returns the error code.

However, the caller bpf_prog_load() in kernel/bpf/syscall.c receives the error code and jumps to the free_prog_sec label, which unconditionally calls security_bpf_prog_free() again:

kernel/bpf/syscall.c:bpf_prog_load() {
    ...
    err = security_bpf_prog_load(prog, attr, token, bpf_prog_is_kernel(prog));
    if (err)
        goto free_prog_sec;
    ...
free_prog_sec:
    ...
    security_bpf_prog_free(prog);
    ...
}

This causes the bpf_prog_free LSM hook to execute twice for the same BPF program. During the first call, prog->aux->security is freed and set to NULL. The second call invokes the hook again with the NULL security blob.

Could this duplicate invocation violate lifecycle symmetry and potentially cause reference count underflows, state corruption, or NULL pointer dereferences in attached BPF LSM programs?

While this double-free behavior already existed for the bpf_prog_load hook, this patch expands the flawed error handling to the newly introduced bpf_prog_load_integrity hook. Should the internal cleanup in security_bpf_prog_load() be removed to let the caller handle it?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
maintainer Applied
#12 lore raw

Re: [PATCH v7 4/10] lsm: framework for BPF integrity verification

In reply to Blaise Boscaccy
Quoted 39 lines · On May 7, 2026 Blaise Boscaccy <[email protected]> wrote:

Add a new LSM hook and two new LSM hook callbacks to support LSMs that perform integrity verification, e.g. digital signature verification, of BPF programs.

While the BPF subsystem does implement a signature verification scheme, it does not satisfy a number of existing requirements, adding support for BPF program integrity verification to the LSM framework allows administrators to select additional integrity verification mechanisms to meet these needs while also providing a mechanism for future expansion. Additional on why this is necessary can be found at the lore archive link below:

https://lore.kernel.org/linux-security-module/CAHC9VhTQ_DR=ANzoDBjcCtrimV7XcCZVUsANPt=TjcvM4d-vjg@mail.gmail.com/

The LSM-based BPF integrity verification mechanism works within the existing security_bpf_prog_load() hook called by the BPF subsystem. It adds an additional dedicated integrity callback and a new LSM hook/callback to be called from within LSMs implementing integrity verification.

The first new callback, bpf_prog_load_integrity(), located within the security_bpf_prog_load() hook, is necessary to ensure that the integrity verification callbacks are executed before any of the existing LSMs are executed via the bpf_prog_load() callback. Reusing the existing bpf_prog_load() callback for integrity verification could result in LSMs not having access to the integrity verification results when asked to authorize the BPF program load in the bpf_prog_load() callback.

The new LSM hook, security_bpf_prog_load_post_integrity(), is intended to be called from within LSMs performing BPF program integrity verification. It is used to report the verdict of the integrity verification to other LSMs enforcing access control policy on BPF program loads. LSMs enforcing such access controls should register a bpf_prog_load_post_integrity() callback to receive integrity verdicts.

More information on these new callbacks and hook can be found in the code comments in this patch.

Quoted 8 lines
---
 include/linux/lsm_hook_defs.h |  5 +++
 include/linux/security.h      | 25 ++++++++++++
 security/security.c           | 75 +++++++++++++++++++++++++++++++++--
 3 files changed, 102 insertions(+), 3 deletions(-)

Merged into lsm/dev, thanks.

--
paul-moore.com

#13 lore raw

PATCH v7 5/10 lsm: security: Add additional enum values for bpf integrity checks

In reply to Blaise Boscaccy

First add a generic LSM_INT_VERDICT_FAULT value to indicate a system failure during checking. Second, add a LSM_INT_VERDICT_UNKNOWNKEY to signal that the payload was signed with a key other than one that exists in the secondary keyring. And finally add an LSM_INT_VERDICT_UNEXPECTED enum value to indicate that a unexpected hash value was encountered at some stage of verification.

---
 include/linux/security.h | 3 +++
 1 file changed, 3 insertions(+)
include/linux/security.h +3 -0
index b3fd04baa78d0..4b4b8808f67de 100644
@@ -106,6 +106,9 @@ enum lsm_integrity_verdict {
 	LSM_INT_VERDICT_OK,
 	LSM_INT_VERDICT_UNSIGNED,
 	LSM_INT_VERDICT_PARTIALSIG,
+	LSM_INT_VERDICT_UNKNOWNKEY,
+	LSM_INT_VERDICT_UNEXPECTED,
+	LSM_INT_VERDICT_FAULT,
 	LSM_INT_VERDICT_BADSIG,
 };
 
Signature · 1 line
2.53.0
maintainer Applied
#14 lore raw

Re: [PATCH v7 5/10] lsm: security: Add additional enum values for bpf integrity checks

Quoted 8 lines · On May 7, 2026 Blaise Boscaccy <[email protected]> wrote:

First add a generic LSM_INT_VERDICT_FAULT value to indicate a system failure during checking. Second, add a LSM_INT_VERDICT_UNKNOWNKEY to signal that the payload was signed with a key other than one that exists in the secondary keyring. And finally add an LSM_INT_VERDICT_UNEXPECTED enum value to indicate that a unexpected hash value was encountered at some stage of verification.

Quoted 4 lines
---
 include/linux/security.h | 3 +++
 1 file changed, 3 insertions(+)

Merged into lsm/dev, thanks.

--
paul-moore.com

author NAK
#15 lore raw

PATCH v7 6/10 security: Hornet LSM

In reply to Blaise Boscaccy

This adds the Hornet Linux Security Module which provides enhanced signature verification and data validation for eBPF programs. This allows users to continue to maintain an invariant that all code running inside of the kernel has actually been signed and verified, by the kernel.

This effort builds upon the currently excepted upstream solution. It further hardens it by providing deterministic, in-kernel checking of map hashes to solidify auditing along with preventing TOCTOU attacks against lskel map hashes.

Target map hashes are passed in via PKCS#7 signed attributes. Hornet determines the extent which the eBFP program is signed and defers to other LSMs for policy decisions.

---
 Documentation/admin-guide/LSM/Hornet.rst | 323 +++++++++++++++++++++
 Documentation/admin-guide/LSM/index.rst  |   1 +
 MAINTAINERS                              |   9 +
 include/linux/oid_registry.h             |   3 +
 include/uapi/linux/lsm.h                 |   1 +
 security/Kconfig                         |   3 +-
 security/Makefile                        |   1 +
 security/hornet/Kconfig                  |  13 +
 security/hornet/Makefile                 |   7 +
 security/hornet/hornet.asn1              |  12 +
 security/hornet/hornet_lsm.c             | 352 +++++++++++++++++++++++
 11 files changed, 724 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
 create mode 100644 security/hornet/Kconfig
 create mode 100644 security/hornet/Makefile
 create mode 100644 security/hornet/hornet.asn1
 create mode 100644 security/hornet/hornet_lsm.c
Documentation/admin-guide/LSM/Hornet.rst new file +323 -0
new file mode 100644
index 0000000000000..0ade4c17374c6
@@ -0,0 +1,323 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======
+Hornet
+======
+
+Hornet is a Linux Security Module that provides extensible signature
+verification for eBPF programs. This is selectable at build-time with
+``CONFIG_SECURITY_HORNET``.
+
+Overview
+========
+
+Hornet addresses concerns from users who require strict audit trails and
+verification guarantees for eBPF programs, especially in
+security-sensitive environments. Many production systems need assurance
+that only authorized, unmodified eBPF programs are loaded into the
+kernel. Hornet provides this assurance through cryptographic signature
+verification.
+
+When an eBPF program is loaded via the ``bpf()`` syscall, Hornet
+verifies a PKCS#7 signature attached to the program instructions. The
+signature is checked against whichever keyring was specified by the user
+existing kernel cryptographic infrastructure. In addition to signing the
+program bytecode, Hornet supports signing SHA-256 hashes of associated
+BPF maps, enabling integrity verification of map contents at load time
+and at runtime.
+
+After verification, Hornet classifies the program into one of the
+following integrity states and passes the result to a downstream LSM hook
+(``bpf_prog_load_post_integrity``), allowing other security modules to
+make policy decisions based on the verification outcome:
+
+``LSM_INT_VERDICT_OK``
+  The program signature and all map hashes verified successfully.
+
+``LSM_INT_VERDICT_UNSIGNED``
+  No signature was provided with the program.
+
+``LSM_INT_VERDICT_PARTIALSIG``
+  The program signature verified, but the signature did not contain
+  hornet map hash data.
+
+``LSM_INT_VERDICT_UNKNOWNKEY``
+  The keyring requested by the user is invalid.
+
+``LSM_INT_VERDICT_FAULT``
+  A system error occurred during verification.
+
+``LSM_INT_VERDICT_UNEXPECTED``
+  An unexpected map hash value was encountered.
+
+``LSM_INT_VERDICT_BADSIG``
+  The signature or a map hash failed verification.
+
+Hornet itself does not enforce a policy on whether unsigned or partially
+signed programs should be rejected. It delegates that decision to
+downstream LSMs via the ``bpf_prog_load_post_integrity`` hook, making it
+a composable building block in a larger security architecture.
+
+Use Cases
+=========
+
+- **Locked-down production environments**: Ensure only eBPF programs
+  signed by a trusted authority can be loaded, preventing unauthorized
+  or tampered programs from running in the kernel.
+
+- **Audit and compliance**: Provide cryptographic evidence that loaded
+  eBPF programs match their expected build artifacts, supporting
+  compliance requirements in regulated industries.
+
+- **Supply chain integrity**: Verify that eBPF programs and their
+  associated map data have not been modified since they were built and
+  signed, protecting against supply chain attacks.
+
+Threat Model
+============
+
+Hornet protects against the following threats:
+
+- **Unauthorized eBPF program loading**: Programs that have not been
+  signed by a trusted key will be reported as unsigned or badly signed.
+
+- **Tampering with program instructions**: Any modification to the eBPF
+  bytecode after signing will cause signature verification to fail.
+
+- **Tampering with map data**: When map hashes are included in the
+  signature, Hornet verifies that frozen BPF maps match their expected
+  SHA-256 hashes at load time. Maps are also re-verified before program
+  execution via ``BPF_PROG_RUN``.
+
+Hornet does **not** protect against:
+
+- Compromise of the signing key itself.
+- Attacks that occur after a program has been loaded and verified.
+- Programs loaded by the kernel itself (kernel-internal loads bypass
+  the ``BPF_PROG_RUN`` map check).
+
+Known Limitations
+=================
+
+- Hornet requires programs to use :doc:`light skeletons
+  </bpf/libbpf/libbpf_naming_convention>` (lskels) for the signing
+  workflow, as the tooling operates on lskel-generated headers.
+
+- A maximum of 64 maps per program can be tracked for hash
+  verification.
+
+- Map hash verification requires the maps to be frozen before loading.
+  Maps that are not frozen at load time will cause verification to fail
+  when their hashes are included in the signature.
+
+- The only hashing algorithm available is SHA256 due to it be hardcoded
+  in the bpf subsystem.
+
+- Hornet guarantees that the signed program runs only with signed map
+  data. It does not guarantee positional binding of maps to specific
+  fd_array slots.
+
+- BPF_MAP_TYPE_PROG_ARRAY maps must be frozen for Hornet to verify
+  them. Unfrozen prog array maps are not covered by verification.
+
+Configuration
+=============
+
+Build Configuration
+-------------------
+
+Enable Hornet by setting the following kernel configuration option::
+
+  CONFIG_SECURITY_HORNET=y
+
+This option is found under :menuselection:`Security options --> Hornet
+support` and depends on ``CONFIG_SECURITY``.
+
+When enabled, Hornet is included in the default LSM initialization order
+and will appear in ``/sys/kernel/security/lsm``.
+
+Architecture
+============
+
+Signature Verification Flow
+---------------------------
+
+The following describes what happens when a userspace program calls
+``bpf(BPF_PROG_LOAD, ...)`` with a signature attached:
+
+1. The ``bpf_prog_load_integrity`` LSM hook is invoked.
+
+2. Hornet reads the signature from the userspace buffer specified by
+   ``attr->signature`` (with length ``attr->signature_size``).
+
+3. The PKCS#7 signature is verified against the program instructions
+   using ``verify_pkcs7_message_sig()`` with the user specified keyring.
+
+4. The PKCS#7 message is parsed and its trust chain is validated via
+   ``validate_pkcs7_trust()``.
+
+5. Hornet extracts the authenticated attribute identified by
+   ``OID_hornet_data`` (OID ``2.25.316487325684022475439036912669789383960``)
+   from the PKCS#7 message. This attribute contains an ASN.1-encoded set
+   of map index/hash pairs.
+
+6. For each map hash entry, Hornet retrieves the corresponding BPF map
+   via its file descriptor, confirms it is frozen, computes its SHA-256
+   hash, and compares it against the signed hash.
+
+7. The resulting integrity verdict is passed to the
+   ``bpf_prog_load_post_integrity`` hook so that downstream LSMs can
+   enforce policy.
+
+Runtime Map Verification
+------------------------
+
+When ``bpf(BPF_PROG_RUN, ...)`` is called from userspace, Hornet
+re-verifies the hashes of all maps associated with the program. This
+ensures that map contents have not been modified between program load
+and execution. If any map hash no longer matches, the ``BPF_PROG_RUN``
+command is denied.
+
+Userspace Interface
+-------------------
+
+Signatures are passed to the kernel through fields in ``union bpf_attr``
+when using the ``BPF_PROG_LOAD`` command:
+
+``signature``
+  A pointer to a userspace buffer containing the PKCS#7 signature.
+
+``signature_size``
+  The size of the signature buffer in bytes.
+
+ASN.1 Schema
+------------
+
+Map hashes are encoded as a signed attribute in the PKCS#7 message using
+the following ASN.1 schema::
+
+  HornetData ::= SET OF Map
+
+  Map ::= SEQUENCE {
+      index   INTEGER,
+      sha     OCTET STRING
+  }
+
+Each ``Map`` entry contains the index of the map in the program's
+``fd_array`` and its expected SHA-256 hash. A zero-length ``sha`` field
+indicates that the map at that index should be skipped during
+verification.
+
+Tooling
+=======
+
+Helper scripts and a signature generation tool are provided in
+``scripts/hornet/`` to support the development of signed eBPF light
+skeletons.
+
+gen_sig
+-------
+
+``gen_sig`` is a C program (using OpenSSL) that creates a PKCS#7
+signature over eBPF program instructions and optionally includes
+SHA-256 hashes of BPF maps as signed attributes.
+
+Usage::
+
+  gen_sig --data <instructions.bin> \
+          --cert <signer.crt> \
+          --key <signer.key> \
+          [--pass <passphrase>] \
+          --out <signature.p7b> \
+          [--add <mapfile.bin>:<index> ...]
+
+``--data``
+  Path to the binary file containing eBPF program instructions to sign.
+
+``--cert``
+  Path to the signing certificate (PEM or DER format).
+
+``--key``
+  Path to the private key (PEM or DER format).
+
+``--pass``
+  Optional passphrase for the private key.
+
+``--out``
+  Path to write the output PKCS#7 signature.
+
+``--add``
+  Attach a map hash as a signed attribute. The argument is a path to a
+  binary map file followed by a colon and the map's index in the
+  ``fd_array``. This option may be specified multiple times.
+
+extract-skel.sh
+---------------
+
+Extracts a named field from an autogenerated eBPF lskel header file.
+Used internally by other helper scripts.
+
+extract-insn.sh
+---------------
+
+Extracts the eBPF program instructions (``opts_insn``) from an lskel
+header into a binary file suitable for signing with ``gen_sig``.
+
+extract-map.sh
+--------------
+
+Extracts the map data (``opts_data``) from an lskel header into a
+binary file suitable for hashing with ``gen_sig``.
+
+write-sig.sh
+------------
+
+Replaces the signature data in an lskel header with a new signature
+from a binary file. This is used to embed a freshly generated signature
+back into the header after signing.
+
+Signing Workflow
+================
+
+A typical workflow for building and signing an eBPF light skeleton is:
+
+1. **Compile the eBPF program**::
+
+     clang -O2 -target bpf -c program.bpf.c -o program.bpf.o
+
+2. **Generate the light skeleton header** using ``bpftool``::
+
+     bpftool gen skeleton -S program.bpf.o > loader.h
+
+3. **Extract instructions and map data** from the generated header::
+
+     scripts/hornet/extract-insn.sh loader.h > insn.bin
+     scripts/hornet/extract-map.sh loader.h > map.bin
+
+4. **Generate the signature** with ``gen_sig``::
+
+     scripts/hornet/gen_sig \
+       --key signing_key.pem \
+       --cert signing_key.x509 \
+       --data insn.bin \
+       --add map.bin:0 \
+       --out sig.bin
+
+5. **Embed the signature** back into the header::
+
+     scripts/hornet/write-sig.sh loader.h sig.bin > signed_loader.h
+
+6. **Build the loader program** using the signed header::
+
+     cc -o loader loader.c -lbpf
+
+The resulting loader program will pass the embedded signature to the
+kernel when loading the eBPF program, enabling Hornet to verify it.
+
+Testing
+=======
+
+Self-tests are provided in ``tools/testing/selftests/hornet/``. The test
+suite builds a minimal eBPF program (``trivial.bpf.c``), signs it using
+the workflow described above, and verifies that the signed program loads
+successfully.
Documentation/admin-guide/LSM/index.rst +1 -0
index b44ef68f6e4da..57f6e9fbe5fd1 100644
@@ -49,3 +49,4 @@ subdirectories.
    SafeSetID
    ipe
    landlock
+   Hornet
MAINTAINERS +9 -0
index d1cc0e12fe1f0..0942f5453c04d 100644
@@ -11692,6 +11692,15 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml
 F:	drivers/iio/pressure/mprls0025pa*
 
+HORNET SECURITY MODULE
+M:	Blaise Boscaccy <[email protected]>
+L:	[email protected]
+S:	Supported
+T:	git https://github.com/blaiseboscaccy/hornet.git
+F:	Documentation/admin-guide/LSM/Hornet.rst
+F:	scripts/hornet/
+F:	security/hornet/
+
 HP BIOSCFG DRIVER
 M:	Jorge Lopez <[email protected]>
 L:	[email protected]
include/linux/oid_registry.h +3 -0
index ebce402854de4..bf852715aaea4 100644
@@ -150,6 +150,9 @@ enum OID {
 	OID_id_ml_dsa_65,			/* 2.16.840.1.101.3.4.3.18 */
 	OID_id_ml_dsa_87,			/* 2.16.840.1.101.3.4.3.19 */
 
+	/* Hornet LSM */
+	OID_hornet_data,	  /* 2.25.316487325684022475439036912669789383960 */
+
 	OID__NR
 };
 
include/uapi/linux/lsm.h +1 -0
index 938593dfd5daf..2ff9bcdd551e2 100644
@@ -65,6 +65,7 @@ struct lsm_ctx {
 #define LSM_ID_IMA		111
 #define LSM_ID_EVM		112
 #define LSM_ID_IPE		113
+#define LSM_ID_HORNET		114
 
 /*
  * LSM_ATTR_XXX definitions identify different LSM attributes
security/Kconfig +2 -1
index 6a4393fce9a17..283c4a1032094 100644
@@ -230,6 +230,7 @@ source "security/safesetid/Kconfig"
 source "security/lockdown/Kconfig"
 source "security/landlock/Kconfig"
 source "security/ipe/Kconfig"
+source "security/hornet/Kconfig"
 
 source "security/integrity/Kconfig"
 
@@ -274,7 +275,7 @@ config LSM
 	default "landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,ipe,bpf" if DEFAULT_SECURITY_APPARMOR
 	default "landlock,lockdown,yama,loadpin,safesetid,tomoyo,ipe,bpf" if DEFAULT_SECURITY_TOMOYO
 	default "landlock,lockdown,yama,loadpin,safesetid,ipe,bpf" if DEFAULT_SECURITY_DAC
-	default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf"
+	default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,hornet,bpf"
 	help
 	  A comma-separated list of LSMs, in initialization order.
 	  Any LSMs left off this list, except for those with order
security/Makefile +1 -0
index 4601230ba442a..b68cb56e419bc 100644
@@ -26,6 +26,7 @@ obj-$(CONFIG_CGROUPS)			+= device_cgroup.o
 obj-$(CONFIG_BPF_LSM)			+= bpf/
 obj-$(CONFIG_SECURITY_LANDLOCK)		+= landlock/
 obj-$(CONFIG_SECURITY_IPE)		+= ipe/
+obj-$(CONFIG_SECURITY_HORNET)		+= hornet/
 
 # Object integrity file lists
 obj-$(CONFIG_INTEGRITY)			+= integrity/
security/hornet/Kconfig new file +13 -0
new file mode 100644
index 0000000000000..5be71d97daee2
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SECURITY_HORNET
+	bool "Hornet support"
+	select CRYPTO_LIB_SHA256
+	select PKCS7_MESSAGE_PARSER
+	select SYSTEM_DATA_VERIFICATION
+	default n
+	help
+	  This selects Hornet.
+	  Further information can be found in
+	  Documentation/admin-guide/LSM/Hornet.rst.
+
+	  If you are unsure how to answer this question, answer N.
security/hornet/Makefile new file +7 -0
new file mode 100644
index 0000000000000..26b6f954f762e
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SECURITY_HORNET) := hornet.o
+
+hornet-y := hornet.asn1.o \
+	hornet_lsm.o \
+
+$(obj)/hornet.asn1.o: $(obj)/hornet.asn1.c $(obj)/hornet.asn1.h
security/hornet/hornet.asn1 new file +12 -0
new file mode 100644
index 0000000000000..e60abf451ae23
@@ -0,0 +1,12 @@
+-- SPDX-License-Identifier: BSD-3-Clause
+--
+-- Copyright (C) 2026 Microsoft
+--
+-- https://www.rfc-editor.org/rfc/rfc5652#section-3
+
+HornetData ::= SET OF Map
+
+Map ::= SEQUENCE {
+	index			INTEGER ({ hornet_map_index }),
+	sha			OCTET STRING ({ hornet_map_hash })
+} ({ hornet_next_map })
security/hornet/hornet_lsm.c new file +352 -0

352 lines not on this page · raw message

Signature · 1 line
2.53.0
#16 lore raw

Re: [PATCH v7 06/10] security: Hornet LSM

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider: - [Critical] Time-of-Check to Time-of-Use (TOCTOU) vulnerability during BPF program load allows bypassing…

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [Critical] Time-of-Check to Time-of-Use (TOCTOU) vulnerability during BPF program load allows bypassing map signature verification.
- [High] Unbounded memory allocation with user-provided attr->signature_size leads to denial of service.
- [High] Arbitrary user keyring injection allows unprivileged users to bypass system-wide signature verification.
- [High] Map verification logic allows map substitution by improperly tracking matched hashes.
- [High] Programs signed with an empty map hash SET bypass map verification entirely while receiving an OK verdict.
- [High] Runtime map verification only hooks bpf(BPF_PROG_RUN), leaving normal event-driven eBPF execution unprotected against runtime map tampering.
- [Medium] Skipping a map in the signature causes valid test runs to be denied with -EPERM if the map is frozen.
- [Medium] Off-by-one bounds check in hornet_next_map incorrectly rejects valid signatures containing exactly 64 maps.
--

commit 33400a9d1c16d89f1fcfeabbd6450005c549df6e
Author: Blaise Boscaccy <[email protected]>

security: Hornet LSM

This commit adds the Hornet Linux Security Module to provide eBPF signature verification and map validation. It uses PKCS#7 signed attributes to verify map hashes at load time and before execution.

Quoted 4 lines
security/hornet/hornet_lsm.c new file +0 -0
new file mode 100644

[ ... ]

Quoted 8 lines
security/hornet/hornet_lsm.c new file +8 -0
+static int hornet_verify_hashes(struct hornet_maps *maps,
+				struct hornet_parse_context *ctx,
+				struct bpf_prog *prog)
+{
+	int map_fd;
+	u32 i;
+	struct bpf_map *map;
+	int err = 0;

security/hornet/hornet_lsm.c line 71

Quoted 10 lines
security/hornet/hornet_lsm.c new file +10 -0
+	unsigned char hash[SHA256_DIGEST_SIZE];
+	struct hornet_prog_security_struct *security = hornet_bpf_prog_security(prog);
+
+	for (i = 0; i < ctx->hash_count; i++) {
+		if (ctx->skips[i])
+			continue;
+
+		err = copy_from_bpfptr_offset(&map_fd, maps->fd_array,
+					      ctx->indexes[i] * sizeof(map_fd),
+					      sizeof(map_fd));

Is this susceptible to a Time-of-Check to Time-of-Use race?

Since maps->fd_array is read from userspace here, and then read again later by the core BPF verifier, could a malicious application swap the map file descriptor to point to an unsigned map after this verification succeeds?

[ ... ]

security/hornet/hornet_lsm.c line 110

Quoted 8 lines
security/hornet/hornet_lsm.c new file +8 -0
+int hornet_next_map(void *context, size_t hdrlen,
+		     unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct hornet_parse_context *ctx = (struct hornet_parse_context *)context;
+
+	if (++ctx->hash_count >= MAX_USED_MAPS)
+		return -EINVAL;

Does this restrict the maximum number of maps to 63 instead of the intended 64?

If a valid signature contains exactly 64 maps, processing the 64th map will increment hash_count to 64, causing this check to evaluate to true and reject the signature.

[ ... ]

Quoted 4 lines
security/hornet/hornet_lsm.c new file +4 -0
+static int hornet_check_program(struct bpf_prog *prog, union bpf_attr *attr,
+				struct bpf_token *token, bool is_kernel,
+				enum lsm_integrity_verdict *verdict)
+{

[ ... ]

security/hornet/hornet_lsm.c line 176

Quoted 2 lines
security/hornet/hornet_lsm.c new file +2 -0
+	maps.fd_array = make_bpfptr(attr->fd_array, is_kernel);
+	sig = kzalloc(attr->signature_size, GFP_KERNEL);

Can a user provide an extremely large signature_size to trigger an unbounded memory allocation?

Without a bounds check on attr->signature_size, an attacker could request a size large enough to cause the slab allocator to fail, potentially triggering warnings or a system panic if panic_on_warn is enabled.

Quoted 4 lines
security/hornet/hornet_lsm.c new file +4 -0
+	if (!sig) {
+		err = -ENOMEM;
+		goto out;
+	}

[ ... ]

security/hornet/hornet_lsm.c line 197

Quoted 4 lines
security/hornet/hornet_lsm.c new file +4 -0
+	if (system_keyring_id_check(attr->keyring_id) == 0)
+		key = (struct key *)(unsigned long)attr->keyring_id;
+	else {
+		user_key = lookup_user_key(attr->keyring_id, 0, KEY_DEFER_PERM_CHECK);

Does this allow an unprivileged user to specify their own user keyring for signature verification?

If an attacker signs a malicious program with their own private key and loads it with their own user keyring ID, will Hornet successfully verify it and return an OK verdict to downstream LSMs?

[ ... ]

Quoted 11 lines
security/hornet/hornet_lsm.c new file +11 -0
+static int hornet_check_prog_maps(u32 ufd)
+{
+	CLASS(fd, f)(ufd);
+	struct bpf_prog *prog;
+	struct hornet_prog_security_struct *security;
+	unsigned char hash[SHA256_DIGEST_SIZE];
+	struct bpf_map *map;
+	int i, j;
+	bool found;
+	int covered_count = 0;
+

security/hornet/hornet_lsm.c line 280

Quoted 10 lines
security/hornet/hornet_lsm.c new file +10 -0
+	if (fd_empty(f))
+		return -EBADF;
+	if (fd_file(f)->f_op != &bpf_prog_fops)
+		return -EINVAL;
+
+	prog = fd_file(f)->private_data;
+	security = hornet_bpf_prog_security(prog);
+
+	if (!security->signed_hash_count)
+		return 0;

If a program is signed without including any map hashes, will this return success but allow an attacker to attach arbitrary unverified maps?

Since an empty map hash SET would produce an OK verdict at load time, returning early here bypasses runtime verification completely.

Quoted 12 lines
security/hornet/hornet_lsm.c new file +12 -0
+
+	mutex_lock(&prog->aux->used_maps_mutex);
+
+	/* Verify every used_map has a matching signed hash */
+	for (j = 0; j < prog->aux->used_map_cnt; j++) {
+		map = prog->aux->used_maps[j];
+
+		if (!READ_ONCE(map->frozen) || !map->ops->map_get_hash)
+			continue;
+
+		if (map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash))
+			continue;

security/hornet/hornet_lsm.c line 302

Quoted 10 lines
security/hornet/hornet_lsm.c new file +10 -0
+
+		found = false;
+		for (i = 0; i < security->signed_hash_count; i++) {
+			if (memcmp(hash,
+				   &security->signed_hashes[i * SHA256_DIGEST_SIZE],
+				   SHA256_DIGEST_SIZE) == 0) {
+				found = true;
+				break;
+			}
+		}

Does this allow map substitution if multiple maps share the same hash?

Because the matched hash isn't marked as consumed, an attacker could supply multiple copies of the same map to satisfy the covered_count check, entirely omitting another required map.

security/hornet/hornet_lsm.c line 305

Quoted 3 lines
security/hornet/hornet_lsm.c new file +3 -0
+		if (!found) {
+			mutex_unlock(&prog->aux->used_maps_mutex);
+			return -EPERM;

If a valid signature explicitly skips a map using a zero-length SHA, but the map happens to be frozen, will this incorrectly reject the execution?

Skipped maps are not added to signed_hashes at load time, so this loop will fail to find the hash and return -EPERM, breaking execution for valid programs.

[ ... ]

Quoted 6 lines
security/hornet/hornet_lsm.c new file +6 -0
+static int hornet_bpf(int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
+{
+	/* in horent_bpf(), anything that had originated from kernel space we assume
+	 * has already been checked, in some form or another, so we don't bother
+	 * checking the intergity of any maps. In hornet_bpf_prog_load_integrity(),
+	 * hornet doesn't make any opinion on that and delegates that to the downstream

security/hornet/hornet_lsm.c line 334

Quoted 10 lines
security/hornet/hornet_lsm.c new file +10 -0
+	 * policy enforcement.
+	 */
+
+	if (cmd != BPF_PROG_RUN)
+		return 0;
+	if (kernel)
+		return 0;
+
+	return hornet_check_prog_maps(attr->test.prog_fd);
+}

Does hooking only the BPF_PROG_RUN command leave normal event-driven eBPF executions unprotected?

Programs like XDP, TC, or kprobes execute asynchronously in response to kernel events and bypass the bpf() syscall entirely. Will this allow runtime map tampering for these workloads?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
maintainer Applied
#17 lore raw

Re: [PATCH v7 6/10] security: Hornet LSM

In reply to Blaise Boscaccy
Quoted 28 lines · On May 7, 2026 Blaise Boscaccy <[email protected]> wrote:

This adds the Hornet Linux Security Module which provides enhanced signature verification and data validation for eBPF programs. This allows users to continue to maintain an invariant that all code running inside of the kernel has actually been signed and verified, by the kernel.

This effort builds upon the currently excepted upstream solution. It further hardens it by providing deterministic, in-kernel checking of map hashes to solidify auditing along with preventing TOCTOU attacks against lskel map hashes.

Target map hashes are passed in via PKCS#7 signed attributes. Hornet determines the extent which the eBFP program is signed and defers to other LSMs for policy decisions.

---
 Documentation/admin-guide/LSM/Hornet.rst | 323 +++++++++++++++++++++
 Documentation/admin-guide/LSM/index.rst  |   1 +
 MAINTAINERS                              |   9 +
 include/linux/oid_registry.h             |   3 +
 include/uapi/linux/lsm.h                 |   1 +
 security/Kconfig                         |   3 +-
 security/Makefile                        |   1 +
 security/hornet/Kconfig                  |  13 +
 security/hornet/Makefile                 |   7 +
Quoted 8 lines
 security/hornet/hornet.asn1              |  12 +
 security/hornet/hornet_lsm.c             | 352 +++++++++++++++++++++++
 11 files changed, 724 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
 create mode 100644 security/hornet/Kconfig
 create mode 100644 security/hornet/Makefile
 create mode 100644 security/hornet/hornet.asn1
 create mode 100644 security/hornet/hornet_lsm.c

Merged into lsm/dev, thanks.

--
paul-moore.com

#18 lore raw

PATCH v7 7/10 hornet: Introduce gen_sig

In reply to Blaise Boscaccy

This introduces the gen_sig tool. It creates a pkcs#7 signature of a data payload. Additionally it appends a signed attribute containing a set of hashes.

Typical usage is to provide a payload containing the light skeleton ebpf syscall program binary and it's associated maps, which can be extracted from the auto-generated skeleton header.

---
 scripts/Makefile            |   1 +
 scripts/hornet/Makefile     |   5 +
 scripts/hornet/gen_sig.c    | 401 ++++++++++++++++++++++++++++++++++++
 scripts/hornet/write-sig.sh |  27 +++
 4 files changed, 434 insertions(+)
 create mode 100644 scripts/hornet/Makefile
 create mode 100644 scripts/hornet/gen_sig.c
 create mode 100755 scripts/hornet/write-sig.sh
scripts/Makefile +1 -0
index 0941e5ce7b575..dea8ab91bbe4e 100644
@@ -63,6 +63,7 @@ subdir-$(CONFIG_GENKSYMS) += genksyms
 subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms
 subdir-$(CONFIG_SECURITY_SELINUX) += selinux
 subdir-$(CONFIG_SECURITY_IPE) += ipe
+subdir-$(CONFIG_SECURITY_HORNET) += hornet
 
 # Let clean descend into subdirs
 subdir-	+= basic dtc gdb kconfig mod
scripts/hornet/Makefile new file +5 -0
new file mode 100644
index 0000000000000..3ee41e5e9a9ff
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+hostprogs-always-y	:= gen_sig
+
+HOSTCFLAGS_gen_sig.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
+HOSTLDLIBS_gen_sig = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
scripts/hornet/gen_sig.c new file +401 -0

401 lines not on this page · raw message

scripts/hornet/write-sig.sh new file +27 -0
new file mode 100755
index 0000000000000..7eaabe3bab9aa
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+    echo "Sample for rewriting an autogenerated eBPF lskel headers"
+    echo "with a new signature"
+    echo ""
+    echo "USAGE: header_file sig"
+    exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=2
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    SIG=$(xxd -p $2 | tr -d '\n' | sed 's/\(..\)/\\\\x\1/g')
+    sed '/const char opts_sig/,/;/c\\tstatic const char opts_sig[] __attribute__((__aligned__(8))) = "\\\n'"$(printf '%s\n' "$SIG")"'\";' $1
+fi
Signature · 1 line
2.53.0
#19 lore raw

Re: [PATCH v7 07/10] hornet: Introduce gen_sig

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider: - [High] Missing error checks for memory allocations and OpenSSL APIs can lead to crashes or silently…

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Missing error checks for memory allocations and OpenSSL APIs can lead to crashes or silently corrupted outputs.
- [Medium] Off-by-one error limits the maximum number of hashes.
- [Low] Command-line option parsing errors due to mismatched short_opts and missing switch cases.
- [Low] Premature teardown of global OpenSSL error strings inside a loop.
- [Low] Unquoted variables in write-sig.sh can cause word splitting and unexpected file reads.
--

commit deefd8583c7763e2999adf448f86a8e2512b3368
Author: Blaise Boscaccy <[email protected]>

hornet: Introduce gen_sig

This introduces the gen_sig tool to create a pkcs#7 signature of a data payload and append a signed attribute containing a set of hashes.

Quoted 5 lines
scripts/hornet/gen_sig.c new file +0 -0
new file mode 100644
index 0000000000000..4d59b70f5fff4

[ ... ]

Quoted 2 lines
scripts/hornet/gen_sig.c new file +2 -0
+static int sha256(const char *path, unsigned char out[SHA256_LEN], unsigned int *out_len)
+{

[ ... ]

Quoted 4 lines
scripts/hornet/gen_sig.c new file +4 -0
+	ERR_load_crypto_strings();
+
+	rc = -3;
+	ctx = EVP_MD_CTX_new();

[ ... ]

scripts/hornet/gen_sig.c line 253

Quoted 6 lines
scripts/hornet/gen_sig.c new file +6 -0
+done:
+	EVP_MD_CTX_free(ctx);
+	fclose(f);
+	ERR_free_strings();
+	return rc;
+}

Because sha256() is called inside a loop in main(), will calling ERR_free_strings() here prematurely free the global error string table? This might cause subsequent OpenSSL errors to only print raw numeric codes instead of human-readable messages.

scripts/hornet/gen_sig.c line 260

Quoted 6 lines
scripts/hornet/gen_sig.c new file +6 -0
+static void add_hash(MAP_SET *set, unsigned char *buffer, int buffer_len, int index)
+{
+	HORNET_MAP *map = NULL;
+
+	map = HORNET_MAP_new();
+	ASN1_INTEGER_set(map->index, index);

Should we check if map is NULL before passing it to ASN1_INTEGER_set()? If HORNET_MAP_new() fails during an out-of-memory condition, this could lead to a segmentation fault.

[ ... ]

Quoted 2 lines
scripts/hornet/gen_sig.c new file +2 -0
+int main(int argc, char **argv)
+{

[ ... ]

scripts/hornet/gen_sig.c line 291

Quoted 1 line
scripts/hornet/gen_sig.c new file +1 -0
+	const char *short_opts = "C:K:P:O:A:Sh";

This isn't a bug, but short_opts appears to be missing D: for the data argument. Will this cause the short -D option to be rejected even though it is defined in long_opts?

Quoted 13 lines
scripts/hornet/gen_sig.c new file +13 -0
+	static const struct option long_opts[] = {
+		{"cert", required_argument, 0, 'C'},
+		{"key",  required_argument, 0, 'K'},
+		{"pass",  required_argument, 0, 'P'},
+		{"out",  required_argument, 0, 'O'},
+		{"data",  required_argument, 0, 'D'},
+		{"add",  required_argument, 0, 'A'},
+		{"help",    no_argument,       0, 'h'},
+		{0, 0, 0, 0}
+	};
+
+	while ((opt = getopt_long_only(argc, argv, short_opts, long_opts, NULL)) != -1) {
+		switch (opt) {

[ ... ]

scripts/hornet/gen_sig.c line 325

Quoted 5 lines
scripts/hornet/gen_sig.c new file +5 -0
+		case 'A':
+			if (strchr(optarg, ':')) {
+				hashes[hash_count].file = strsep(&optarg, ":");
+				hashes[hash_count].index = atoi(optarg);
+				if (++hash_count >= MAX_HASHES) {

Does this condition inadvertently limit the maximum number of hashes to 63 instead of 64? If exactly 64 hashes are provided, hash_count increments to 64, the condition evaluates to true, and it immediately aborts.

scripts/hornet/gen_sig.c line 338

Quoted 13 lines
scripts/hornet/gen_sig.c new file +13 -0
+					usage(argv[0]);
+					return EXIT_FAILURE;
+				}
+			} else {
+				usage(argv[0]);
+				return EXIT_FAILURE;
+			}
+			break;
+		default:
+			usage(argv[0]);
+			return EXIT_FAILURE;
+		}
+	}

Passing --help maps to 'h', but there is no case for 'h' in the switch statement. Will this fall through to the default case and incorrectly return EXIT_FAILURE instead of EXIT_SUCCESS?

[ ... ]

scripts/hornet/gen_sig.c line 367

Quoted 2 lines
scripts/hornet/gen_sig.c new file +2 -0
+	set = MAP_SET_new();
+	set->maps = sk_HORNET_MAP_new_null();

Could MAP_SET_new() return NULL here, causing a crash when set->maps is subsequently accessed?

[ ... ]

scripts/hornet/gen_sig.c line 383

Quoted 2 lines
scripts/hornet/gen_sig.c new file +2 -0
+	der_len = ASN1_item_i2d((ASN1_VALUE *)set, &der, ASN1_ITEM_rptr(MAP_SET));
+	CMS_signed_add1_attr_by_OBJ(si, oid, V_ASN1_SEQUENCE, der, der_len);

ASN1_item_i2d() can return a negative value on failure. Could passing this unchecked der_len into CMS_signed_add1_attr_by_OBJ() cause an out-of-bounds read or crash?

Additionally, should the return value of CMS_signed_add1_attr_by_OBJ() be checked for errors?

[ ... ]

scripts/hornet/gen_sig.c line 394

Quoted 10 lines
scripts/hornet/gen_sig.c new file +10 -0
+	err = CMS_final(cms_out, data_in, NULL, CMS_NOCERTS | CMS_BINARY);
+	ERR(!err, "cms final failed");
+
+	OPENSSL_free(der);
+	MAP_SET_free(set);
+
+	b_out = bio_open_wr(out_path);
+	ERR(!b_out, "opening output path failed");
+
+	i2d_CMS_bio_stream(b_out, cms_out, NULL, 0);

Is it possible for i2d_CMS_bio_stream() to fail, for example if there is no space left on the device? Since the return value is ignored, could the tool exit successfully while leaving a corrupted or empty signature file?

Quoted 11 lines
scripts/hornet/gen_sig.c new file +6 -0
+	BIO_free(data_in);
+	BIO_free(b_out);
+	EVP_cleanup();
+	ERR_free_strings();
+	return 0;
+}
scripts/hornet/write-sig.sh new file +0 -0
new file mode 100755
index 0000000000000..7eaabe3bab9aa

[ ... ]

scripts/hornet/write-sig.sh line 27

Quoted 6 lines
scripts/hornet/write-sig.sh new file +6 -0
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    SIG=$(xxd -p $2 | tr -d '\n' | sed 's/\(..\)/\\\\x\1/g')
+    sed '/const char opts_sig/,/;/c\\tstatic const char opts_sig[] __attribute__((__aligned__(8))) = "\\\n'"$(printf '%s\n' "$SIG")"'\";' $1
+fi

This isn't a bug, but could leaving the variables $1 and $2 unquoted cause issues if the provided filenames contain spaces? This might result in word splitting and cause xxd or sed to read unintended files.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
maintainer Applied
#20 lore raw

Re: [PATCH v7 7/10] hornet: Introduce gen_sig

In reply to Blaise Boscaccy
Quoted 10 lines · On May 7, 2026 Blaise Boscaccy <[email protected]> wrote:

This introduces the gen_sig tool. It creates a pkcs#7 signature of a data payload. Additionally it appends a signed attribute containing a set of hashes.

Typical usage is to provide a payload containing the light skeleton ebpf syscall program binary and it's associated maps, which can be extracted from the auto-generated skeleton header.

Quoted 9 lines
---
 scripts/Makefile            |   1 +
 scripts/hornet/Makefile     |   5 +
 scripts/hornet/gen_sig.c    | 401 ++++++++++++++++++++++++++++++++++++
 scripts/hornet/write-sig.sh |  27 +++
 4 files changed, 434 insertions(+)
 create mode 100644 scripts/hornet/Makefile
 create mode 100644 scripts/hornet/gen_sig.c
 create mode 100755 scripts/hornet/write-sig.sh

Merged into lsm/dev, but I did add a .gitignore for scripts/hornet/ and I fixed up the SPDX tag (it wants C++ style comments).

--
paul-moore.com

#21 lore raw

PATCH v7 8/10 hornet: Add a light skeleton data extractor scripts

In reply to Blaise Boscaccy

These script eases light skeleton development against Hornet by generating a data payloads which can be used for signing a light skeleton binary using gen_sig.

---
 scripts/hornet/extract-insn.sh | 27 +++++++++++++++++++++++++++
 scripts/hornet/extract-map.sh  | 27 +++++++++++++++++++++++++++
 scripts/hornet/extract-skel.sh | 27 +++++++++++++++++++++++++++
 3 files changed, 81 insertions(+)
 create mode 100755 scripts/hornet/extract-insn.sh
 create mode 100755 scripts/hornet/extract-map.sh
 create mode 100755 scripts/hornet/extract-skel.sh
scripts/hornet/extract-insn.sh new file +27 -0
new file mode 100755
index 0000000000000..52338f057ff6b
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+    echo "Sample script for extracting instructions"
+    echo "autogenerated eBPF lskel headers"
+    echo ""
+    echo "USAGE: header_file"
+    exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=1
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    printf $(gcc -E $1 | grep "opts_insn" | \
+		 awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi
scripts/hornet/extract-map.sh new file +27 -0
new file mode 100755
index 0000000000000..c309f505c6238
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+    echo "Sample script for extracting instructions"
+    echo "autogenerated eBPF lskel headers"
+    echo ""
+    echo "USAGE: header_file"
+    exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=1
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    printf $(gcc -E $1 | grep "opts_data" | \
+		 awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi
scripts/hornet/extract-skel.sh new file +27 -0
new file mode 100755
index 0000000000000..6550a86b89917
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+    echo "Sample script for extracting instructions and map data out of"
+    echo "autogenerated eBPF lskel headers"
+    echo ""
+    echo "USAGE: header_file field"
+    exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=2
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    printf $(gcc -E $1 | grep "static const char opts_$2" | \
+		 awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi
Signature · 1 line
2.53.0
#22 lore raw

Re: [PATCH v7 08/10] hornet: Add a light skeleton data extractor scripts

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider: - [High] extract-skel.sh and extract-map.sh fail with a usage error when extracting empty payloads.…

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] extract-skel.sh and extract-map.sh fail with a usage error when extracting empty payloads.
- [High] The scripts strictly forbid passing include paths (-I) to the C preprocessor.
- [Medium] Unquoted variables in the scripts allow command injection and fail on filenames with spaces.
- [Medium] Improper use of printf with unquoted command substitution pollutes stderr and relies on undocumented behavior.
- [Medium] Trailing whitespace in the header file bypasses semicolon stripping, corrupting the payload.
- [Low] The usage function exits with a success status (0) on invalid arguments.
- [Low] Usage text in extract-map.sh incorrectly describes its functionality.
- [Low] Grammar mistakes in the commit message.
--

commit 4a15a4a590708cdca529a456f67bfad392cfd02a
Author: Blaise Boscaccy <[email protected]>

hornet: Add a light skeleton data extractor scripts

These script eases light skeleton development against Hornet by generating a data payloads which can be used for signing a light skeleton binary using gen_sig.

Quoted 5 lines

hornet: Add a light skeleton data extractor scripts

These script eases light skeleton development against Hornet by generating a data payloads which can be used for signing a light skeleton binary using gen_sig.

This isn't a bug, but there is a grammar mistake in the commit message. "These script eases" and "a data payloads" mix singular and plural forms.

Quoted 7 lines
scripts/hornet/extract-insn.sh new file +1 -0
new file mode 100755
index 0000000000000..52338f057ff6b
@@ -0,0 +1,27 @@
+#!/bin/bash

[ ... ]

scripts/hornet/extract-insn.sh line 16

Quoted 7 lines
scripts/hornet/extract-insn.sh new file +7 -0
+function usage() {
+    echo "Sample script for extracting instructions"
+    echo "autogenerated eBPF lskel headers"
+    echo ""
+    echo "USAGE: header_file"
+    exit
+}

Could this cause build scripts to silently continue even when arguments are missing? Calling exit without an argument defaults to the exit status of the last command, which is echo in this case, returning a success status of 0 instead of an error code.

scripts/hornet/extract-insn.sh line 24

Quoted 7 lines
scripts/hornet/extract-insn.sh new file +7 -0
+ARGC=$#
+
+EXPECTED_ARGS=1
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else

Will this strict argument limit break in standard kernel build environments?

BPF light skeletons include <bpf/skel_internal.h>, which typically requires passing include paths like -Itools/include to gcc -E. Enforcing exactly one argument prevents passing any such flags to the preprocessor.

Quoted 1 line

+ printf $(gcc -E $1 | grep "opts_insn" | \

Could the unquoted $1 cause gcc to fail if the header file path contains spaces?

Is there a risk of printf throwing a usage error and failing the build if a BPF object has no maps? If bpftool completely skips generating the data array, the grep command returns nothing, and the unquoted command substitution evaluates to zero arguments. A call to printf with no arguments will throw a bash usage error to stderr and exit with a non-zero status.

Also, since grep might match multiple lines such as the array declaration and opts.insns_sz = sizeof(...), the unquoted substitution will split them into multiple words. Does this cause printf to use the first word as a format string and issue warnings to stderr about ignoring excess arguments?

scripts/hornet/extract-insn.sh

Quoted 2 lines
scripts/hornet/extract-insn.sh new file +2 -0
+		 awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi

Does the sed 's/;\+$//' command safely handle carriage returns or trailing spaces?

If the generated C header contains whitespace after the semicolon, the line ending anchor $ will fail to match, leaving the semicolon in the string and potentially corrupting the extracted binary payload.

Quoted 7 lines
scripts/hornet/extract-map.sh new file +1 -0
new file mode 100755
index 0000000000000..c309f505c6238
@@ -0,0 +1,27 @@
+#!/bin/bash

[ ... ]

scripts/hornet/extract-map.sh

Quoted 3 lines
scripts/hornet/extract-map.sh new file +3 -0
+function usage() {
+    echo "Sample script for extracting instructions"
+    echo "autogenerated eBPF lskel headers"

This isn't a bug, but should this usage text mention that it extracts map data instead of instructions?

Quoted 5 lines
scripts/hornet/extract-skel.sh new file +0 -0
new file mode 100755
index 0000000000000..6550a86b89917

[ ... ]

scripts/hornet/extract-skel.sh line 27

Quoted 6 lines
scripts/hornet/extract-skel.sh new file +6 -0
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    printf $(gcc -E $1 | grep "static const char opts_$2" | \
+		 awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi

Is it safe to pass $2 directly into the unquoted grep command here? If the field argument contains spaces or shell characters like quotation marks or pipes, it could result in command injection.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8
maintainer Applied
#23 lore raw

Re: [PATCH v7 8/10] hornet: Add a light skeleton data extractor scripts

In reply to Blaise Boscaccy
Quoted 6 lines · On May 7, 2026 Blaise Boscaccy <[email protected]> wrote:

These script eases light skeleton development against Hornet by generating a data payloads which can be used for signing a light skeleton binary using gen_sig.

Quoted 8 lines
---
 scripts/hornet/extract-insn.sh | 27 +++++++++++++++++++++++++++
 scripts/hornet/extract-map.sh  | 27 +++++++++++++++++++++++++++
 scripts/hornet/extract-skel.sh | 27 +++++++++++++++++++++++++++
 3 files changed, 81 insertions(+)
 create mode 100755 scripts/hornet/extract-insn.sh
 create mode 100755 scripts/hornet/extract-map.sh
 create mode 100755 scripts/hornet/extract-skel.sh

Merged into lsm/dev, thanks.

--
paul-moore.com

#24 lore raw

PATCH v7 9/10 selftests/hornet: Add a selftest for the Hornet LSM

In reply to Blaise Boscaccy

This selftest contains a testcase that utilizes light skeleton eBPF loaders and exercises hornet's map validation.

---
 tools/testing/selftests/Makefile             |  1 +
 tools/testing/selftests/hornet/Makefile      | 63 ++++++++++++++++++++
 tools/testing/selftests/hornet/loader.c      | 21 +++++++
 tools/testing/selftests/hornet/trivial.bpf.c | 33 ++++++++++
 4 files changed, 118 insertions(+)
 create mode 100644 tools/testing/selftests/hornet/Makefile
 create mode 100644 tools/testing/selftests/hornet/loader.c
 create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c
tools/testing/selftests/Makefile +1 -0
index 450f13ba4cca9..4e2d1cd88c825 100644
@@ -44,6 +44,7 @@ TARGETS += ftrace
 TARGETS += futex
 TARGETS += gpio
 TARGETS += hid
+TARGETS += hornet
 TARGETS += intel_pstate
 TARGETS += iommu
 TARGETS += ipc
tools/testing/selftests/hornet/Makefile new file +63 -0

63 lines not on this page · raw message

tools/testing/selftests/hornet/loader.c new file +21 -0
new file mode 100644
index 0000000000000..f27580c7262b3
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/resource.h>
+#include <bpf/libbpf.h>
+#include <errno.h>
+#include  "signed_loader.h"
+
+int main(int argc, char **argv)
+{
+	struct trivial *skel;
+
+	skel = trivial__open_and_load();
+	if (!skel)
+		return -1;
+
+	trivial__destroy(skel);
+	return 0;
+}
tools/testing/selftests/hornet/trivial.bpf.c new file +33 -0
new file mode 100644
index 0000000000000..d38c5b53ff932
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+int monitored_pid = 0;
+
+SEC("tracepoint/syscalls/sys_enter_unlinkat")
+int handle_enter_unlink(struct trace_event_raw_sys_enter *ctx)
+{
+	char filename[128] = { 0 };
+	struct task_struct *task;
+	unsigned long start_time = 0;
+	int pid = bpf_get_current_pid_tgid() >> 32;
+	char *pathname_ptr = (char *) BPF_CORE_READ(ctx, args[1]);
+
+	bpf_probe_read_str(filename, sizeof(filename), pathname_ptr);
+	task = (struct task_struct *)bpf_get_current_task();
+	start_time = BPF_CORE_READ(task, start_time);
+
+	bpf_printk("BPF triggered unlinkat by PID: %d, start_time %ld. pathname = %s",
+		   pid, start_time, filename);
+
+	if (monitored_pid == pid)
+		bpf_printk("target pid found");
+
+	return 0;
+}
Signature · 1 line
2.53.0
#25 lore raw

Re: [PATCH v7 09/10] selftests/hornet: Add a selftest for the Hornet LSM

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider: - [High] Cross-compilation breakage due to `gen_sig` built with target compiler but executed on the host.…

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Cross-compilation breakage due to gen_sig built with target compiler but executed on the host.
- [High] Cross-compilation breakage due to hardcoded host paths for vmlinux.h and includes.
- [High] Incorrect helper used to read user-space memory in the BPF program.
- [High] Makefile custom targets lack $(OUTPUT)/ prefix, breaking out-of-tree builds.
- [High] Unconditional dependency on kernel signing keys breaks the global selftests build.
- [Medium] Selftest links against the system's libbpf instead of the locally built in-tree version.
- [Medium] Violation of the BPF selftest macro guidelines.
--

commit 7071c10449f31b01df208a3d3f4cf023f39c5b06
Author: Blaise Boscaccy <[email protected]>

selftests/hornet: Add a selftest for the Hornet LSM

This selftest contains a testcase that utilizes light skeleton eBPF loaders and exercises hornet's map validation.

Quoted 4 lines
tools/testing/selftests/Makefile +0 -0
index 450f13ba4cca9..4e2d1cd88c825 100644

[ ... ]

tools/testing/selftests/Makefile TARGETS += ftrace · line 47

Quoted 5 lines
tools/testing/selftests/Makefile +1 -0
@@ -44,6 +44,7 @@ TARGETS += ftrace
 TARGETS += futex
 TARGETS += gpio
 TARGETS += hid
+TARGETS += hornet

Will this break the global selftests build if module signing keys haven't been generated? The hornet Makefile seems to unconditionally depend on $(CERTDIR)/signing_key.pem, which might not exist in all build environments.

Quoted 9 lines
tools/testing/selftests/Makefile +0 -0
 TARGETS += intel_pstate
 TARGETS += iommu
 TARGETS += ipc
tools/testing/selftests/hornet/Makefile new file +0 -0
new file mode 100644
index 0000000000000..432bce59f54e7

[ ... ]

tools/testing/selftests/hornet/Makefile line 28

Quoted 4 lines
tools/testing/selftests/hornet/Makefile new file +4 -0
+BPF_CFLAGS := -target bpf \
+	-D__TARGET_ARCH_$(ARCH) \
+	-I/usr/include/$(shell uname -m)-linux-gnu \
+	$(KHDR_INCLUDES)

Is it intentional to use $(shell uname -m) here?

When cross-compiling, this will yield the host architecture instead of the target architecture, which might include the wrong host structures.

tools/testing/selftests/hornet/Makefile line 31

Quoted 3 lines
tools/testing/selftests/hornet/Makefile new file +3 -0
+
+vmlinux.h:
+	$(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

Does this read the host kernel's BTF instead of the target kernel's BTF? If cross-compiling, /sys/kernel/btf/vmlinux is the host machine's BTF.

Also, are these targets missing the $(OUTPUT)/ prefix? The Makefile includes ../lib.mk which automatically prepends $(OUTPUT)/ to TEST_GEN_FILES and TEST_GEN_PROGS.

When building out-of-tree with O=..., make might fail to find the rules for these targets without the $(OUTPUT)/ prefix.

Quoted 7 lines
tools/testing/selftests/hornet/Makefile new file +7 -0
+
+trivial.bpf.o: trivial.bpf.c vmlinux.h
+	$(CLANG) $(CFLAGS) $(BPF_CFLAGS) -c $< -o $@
+
+loader.h: trivial.bpf.o
+	$(BPFTOOL) gen skeleton -S -k $(CERTDIR)/signing_key.pem -i $(CERTDIR)/signing_key.x509 \
+		-L $< name trivial > $@

[ ... ]

tools/testing/selftests/hornet/Makefile line 54

Quoted 9 lines

+$(OUTPUT)/gen_sig: ../../../../scripts/hornet/gen_sig.c

tools/testing/selftests/hornet/Makefile new file +8 -0
+	$(call msg,GEN_SIG,,$@)
+	$(Q)$(CC) $(shell $(PKG_CONFIG) --cflags libcrypto 2> /dev/null) \
+		  $< -o $@ \
+		  $(shell $(PKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
+
+sig.bin: insn.bin map.bin $(OUTPUT)/gen_sig
+	$(OUTPUT)/gen_sig --key $(CERTDIR)/signing_key.pem --cert $(CERTDIR)/signing_key.x509 \
+		--data insn.bin --add map.bin:0 --out sig.bin

Since $(OUTPUT)/gen_sig is executed during the build process to generate sig.bin, shouldn't it be built with $(HOSTCC) instead of the target $(CC)?

During cross-compilation, building it with the target compiler and running it on the host could result in an 'Exec format error'.

tools/testing/selftests/hornet/Makefile line 60

Quoted 6 lines
tools/testing/selftests/hornet/Makefile new file +6 -0
+
+signed_loader.h: sig.bin
+	$(SCRIPTSDIR)/write-sig.sh loader.h sig.bin > $@
+
+loader: loader.c signed_loader.h
+	$(CC) $(CFLAGS) -I$(LIBDIR) -I$(APIDIR) $< -o $@ -lbpf

Does this link against the system's dynamic libbpf.so instead of the locally built in-tree version?

Typically, kernel selftests statically link against the in-tree libbpf.a to test the current kernel's library and avoid build failures on systems missing the package.

Quoted 8 lines
tools/testing/selftests/hornet/Makefile new file +3 -0
+
+
+EXTRA_CLEAN = $(OUTPUT)/gen_sig
tools/testing/selftests/hornet/loader.c new file +0 -0
new file mode 100644
index 0000000000000..f27580c7262b3

[ ... ]

tools/testing/selftests/hornet/loader.c line 17

Quoted 7 lines
tools/testing/selftests/hornet/loader.c new file +7 -0
+int main(int argc, char **argv)
+{
+	struct trivial *skel;
+
+	skel = trivial__open_and_load();
+	if (!skel)
+		return -1;

Should this use the BPF testing framework and modern ASSERT_*() macros like ASSERT_OK_PTR() to provide structured TAP output?

New BPF tests are expected to follow this standard.

Quoted 9 lines
tools/testing/selftests/hornet/loader.c new file +4 -0
+
+	trivial__destroy(skel);
+	return 0;
+}
tools/testing/selftests/hornet/trivial.bpf.c new file +0 -0
new file mode 100644
index 0000000000000..d38c5b53ff932

[ ... ]

tools/testing/selftests/hornet/trivial.bpf.c line 22

Quoted 10 lines
tools/testing/selftests/hornet/trivial.bpf.c new file +10 -0
+SEC("tracepoint/syscalls/sys_enter_unlinkat")
+int handle_enter_unlink(struct trace_event_raw_sys_enter *ctx)
+{
+	char filename[128] = { 0 };
+	struct task_struct *task;
+	unsigned long start_time = 0;
+	int pid = bpf_get_current_pid_tgid() >> 32;
+	char *pathname_ptr = (char *) BPF_CORE_READ(ctx, args[1]);
+
+	bpf_probe_read_str(filename, sizeof(filename), pathname_ptr);

Because args[1] of sys_enter_unlinkat is a user-space pointer to the pathname, does this need to use bpf_probe_read_user_str() instead of bpf_probe_read_str()?

On architectures with strict boundary protections or split address spaces, reading user-space memory with bpf_probe_read_str() could fail or read garbage.

Quoted 2 lines
tools/testing/selftests/hornet/trivial.bpf.c new file +2 -0
+	task = (struct task_struct *)bpf_get_current_task();
+	start_time = BPF_CORE_READ(task, start_time);
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
maintainer Applied
#26 lore raw

Re: [PATCH v7 9/10] selftests/hornet: Add a selftest for the Hornet LSM

In reply to Blaise Boscaccy
Quoted 5 lines · On May 7, 2026 Blaise Boscaccy <[email protected]> wrote:

This selftest contains a testcase that utilizes light skeleton eBPF loaders and exercises hornet's map validation.

Quoted 9 lines
---
 tools/testing/selftests/Makefile             |  1 +
 tools/testing/selftests/hornet/Makefile      | 63 ++++++++++++++++++++
 tools/testing/selftests/hornet/loader.c      | 21 +++++++
 tools/testing/selftests/hornet/trivial.bpf.c | 33 ++++++++++
 4 files changed, 118 insertions(+)
 create mode 100644 tools/testing/selftests/hornet/Makefile
 create mode 100644 tools/testing/selftests/hornet/loader.c
 create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c

Merged into lsm/dev, thanks.

--
paul-moore.com

#27 lore raw

PATCH v7 10/10 ipe: Add BPF program load policy enforcement via Hornet integration

In reply to Blaise Boscaccy

Add support for the bpf_prog_load_post_integrity LSM hook, enabling IPE to make policy decisions about BPF program loading based on integrity verdicts provided by the Hornet LSM.

New policy operation:
op=BPF_PROG_LOAD - Matches BPF program load events

New policy properties:

  bpf_signature=NONE      - No Verdict
  bpf_signature=OK        - Program signature and map hashes verified
  bpf_signature=UNSIGNED  - No signature provided
  bpf_signature=PARTIALSIG - Signature OK but no map hash data
  bpf_signature=UNKNOWNKEY - The keyring requested by the user is invalid
  bpf_signature=UNEXPECTED - An unexpected hash value was encountered
  bpf_signature=FAULT 	   - System error during verification
  bpf_signature=BADSIG    - Signature or map hash verification failed
  bpf_keyring=BUILTIN     - Program was signed using a builtin keyring
  bpf_keyring=SECONDARY   - Program was signed using the secondary keyring
  bpf_keyring=PLATFORM    - Program was signed using the platform keyring
  bpf_kernel=TRUE         - Program originated from kernelspace
  bpf_kernel=FALSE        - Program originated from userspace

These properties map directly to the lsm_integrity_verdict enum values provided by the Hornet LSM through security_bpf_prog_load_post_integrity.

The feature is gated on CONFIG_IPE_PROP_BPF_SIGNATURE which depends on CONFIG_SECURITY_HORNET.

---
 Documentation/admin-guide/LSM/ipe.rst | 162 +++++++++++++++++++++++++-
 Documentation/security/ipe.rst        |  68 +++++++++++
 security/ipe/Kconfig                  |  15 +++
 security/ipe/audit.c                  |  15 +++
 security/ipe/eval.c                   |  93 ++++++++++++++-
 security/ipe/eval.h                   |  11 ++
 security/ipe/hooks.c                  |  63 ++++++++++
 security/ipe/hooks.h                  |  15 +++
 security/ipe/ipe.c                    |  14 +++
 security/ipe/ipe.h                    |   3 +
 security/ipe/policy.h                 |  14 +++
 security/ipe/policy_parser.c          |  27 +++++
 12 files changed, 498 insertions(+), 2 deletions(-)
Documentation/admin-guide/LSM/ipe.rst +161 -1
index a756d81585317..d68ba9d98859e 100644
@@ -559,7 +559,8 @@ policy. Two properties are built-into the policy parser: 'op' and 'action'.
 The other properties are used to restrict immutable security properties
 about the files being evaluated. Currently those properties are:
 '``boot_verified``', '``dmverity_signature``', '``dmverity_roothash``',
-'``fsverity_signature``', '``fsverity_digest``'. A description of all
+'``fsverity_signature``', '``fsverity_digest``', '``bpf_signature``',
+'``bpf_keyring``', '``bpf_kernel``'. A description of all
 properties supported by IPE are listed below:
 
 op
@@ -603,6 +604,14 @@ as the first token. IPE supports the following operations:
       Controls loading IMA certificates through the Kconfigs,
       ``CONFIG_IMA_X509_PATH`` and ``CONFIG_EVM_X509_PATH``.
 
+   ``BPF_PROG_LOAD``:
+
+      Pertains to BPF programs being loaded via the ``bpf()`` syscall.
+      This operation is used in conjunction with the ``bpf_signature``,
+      ``bpf_keyring``, and ``bpf_kernel`` properties to control BPF
+      program loading based on integrity verification provided by the
+      Hornet LSM.
+
 action
 ~~~~~~
 
@@ -713,6 +722,105 @@ fsverity_signature
 
       fsverity_signature=(TRUE|FALSE)
 
+bpf_signature
+~~~~~~~~~~~~~
+
+   This property can be utilized for authorization of BPF program loads based
+   on the integrity verdict provided by the Hornet LSM. When a BPF program is
+   loaded, Hornet performs cryptographic verification of the program's PKCS#7
+   signature (if present) and passes an integrity verdict to IPE via the
+   ``security_bpf_prog_load_post_integrity`` hook. IPE can then allow or deny
+   the load based on the verdict.
+
+   This property depends on ``SECURITY_HORNET`` and is controlled by the
+   ``IPE_PROP_BPF_SIGNATURE`` config option.
+   The format of this property is::
+
+      bpf_signature=(NONE|OK|UNSIGNED|PARTIALSIG|UNKNOWNKEY|UNEXPECTED|FAULT|BADSIG)
+
+   The possible values correspond to the integrity verdicts from Hornet:
+
+      ``NONE``
+
+         No integrity verdict was set (default/uninitialized).
+
+      ``OK``
+
+         The BPF program's signature and all map hashes were successfully
+         verified.
+
+      ``UNSIGNED``
+
+         No signature was provided with the BPF program.
+
+      ``PARTIALSIG``
+
+         The program signature was verified, but no authenticated map hash
+         data was present.
+
+      ``UNKNOWNKEY``
+
+         The keyring requested by the user is invalid.
+
+      ``UNEXPECTED``
+
+         An unexpected map hash value was encountered during verification.
+
+      ``FAULT``
+
+         A system error occurred during signature verification.
+
+      ``BADSIG``
+
+         The signature or hash verification failed.
+
+bpf_keyring
+~~~~~~~~~~~~
+
+   This property can be utilized for authorization of BPF program loads based
+   on the keyring specified in the ``bpf_attr`` during the ``BPF_PROG_LOAD``
+   syscall. This allows policies to restrict which keyring must be used for
+   signature verification of BPF programs.
+
+   This property shares the ``IPE_PROP_BPF_SIGNATURE`` config option with
+   ``bpf_signature``.
+   The format of this property is::
+
+      bpf_keyring=(BUILTIN|SECONDARY|PLATFORM)
+
+   The possible values correspond to the system keyrings:
+
+      ``BUILTIN``
+
+         The builtin trusted keyring (``.builtin_trusted_keys``), which
+         contains keys embedded at kernel compile time.
+
+      ``SECONDARY``
+
+         The secondary trusted keyring (``.secondary_trusted_keys``), which
+         includes both builtin trusted keys and keys added at runtime.
+
+      ``PLATFORM``
+
+         The platform keyring (``.platform``), which contains keys provided
+         by the platform firmware (e.g. UEFI db keys).
+
+bpf_kernel
+~~~~~~~~~~
+
+   This property can be utilized for authorization of BPF program loads based
+   on whether the load originated from kernel space or user space. The BPF
+   light skeleton infrastructure performs a secondary kernel-originated program
+   load that will not carry a signature. This property allows policies to
+   permit such kernel-originated loads while still requiring signatures for
+   user-space loads.
+
+   This property shares the ``IPE_PROP_BPF_SIGNATURE`` config option with
+   ``bpf_signature``.
+   The format of this property is::
+
+      bpf_kernel=(TRUE|FALSE)
+
 Policy Examples
 ---------------
 
@@ -788,6 +896,58 @@ Allow execution of a specific fs-verity file
 
    op=EXECUTE fsverity_digest=sha256:fd88f2b8824e197f850bf4c5109bea5cf0ee38104f710843bb72da796ba5af9e action=ALLOW
 
+Allow only signed BPF programs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name=Allow_Signed_BPF policy_version=0.0.0
+   DEFAULT action=ALLOW
+
+   DEFAULT op=BPF_PROG_LOAD action=DENY
+   op=BPF_PROG_LOAD bpf_kernel=TRUE action=ALLOW
+   op=BPF_PROG_LOAD bpf_signature=OK action=ALLOW
+
+This policy allows all other operations but restricts BPF program loading
+to only programs that either originate from kernel space (e.g. light skeleton
+reloads) or have a valid signature verified by the Hornet LSM. Unsigned or
+improperly signed BPF programs from user space will be denied.
+
+Allow signed BPF programs from a specific keyring
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name=Allow_BPF_Builtin_Keyring policy_version=0.0.0
+   DEFAULT action=ALLOW
+
+   DEFAULT op=BPF_PROG_LOAD action=DENY
+   op=BPF_PROG_LOAD bpf_kernel=TRUE action=ALLOW
+   op=BPF_PROG_LOAD bpf_signature=OK bpf_keyring=BUILTIN action=ALLOW
+
+This policy further restricts BPF program loading to only accept programs
+whose signatures were verified using the builtin trusted keyring. Programs
+signed against the secondary or platform keyrings will be denied, providing
+tighter control over which signing keys are acceptable.
+
+Allow signed BPF programs with relaxed partial signatures
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name=Allow_BPF_Partial policy_version=0.0.0
+   DEFAULT action=ALLOW
+
+   DEFAULT op=BPF_PROG_LOAD action=DENY
+   op=BPF_PROG_LOAD bpf_kernel=TRUE action=ALLOW
+   op=BPF_PROG_LOAD bpf_signature=OK action=ALLOW
+   op=BPF_PROG_LOAD bpf_signature=PARTIALSIG action=ALLOW
+
+This policy allows BPF programs that have been fully verified (``OK``) as
+well as programs with a valid program signature but without authenticated
+map hash data (``PARTIALSIG``). This can be useful during development or
+for programs that do not use maps.
+
 Additional Information
 ----------------------
 
Documentation/security/ipe.rst +68 -0

80 lines not on this page · raw message

security/ipe/Kconfig +15 -0
index a110a6cd848b7..95775139612df 100644
@@ -95,6 +95,21 @@ config IPE_PROP_FS_VERITY_BUILTIN_SIG
 
 	  if unsure, answer Y.
 
+config IPE_PROP_BPF_SIGNATURE
+	bool "Enable support for Hornet BPF program signature verification"
+	depends on SECURITY_HORNET
+	help
+	  This option enables the 'bpf_signature', 'bpf_kernel' and
+	  'bpf_keyring' properties within IPE policies. The
+	  'bpf_signature' property allows IPE to make policy decisions
+	  based on the integrity verdict provided by the Hornet LSM
+	  when a BPF program is loaded.  Verdicts include OK,
+	  UNSIGNED, PARTIALSIG, BADSIG, and others.  The 'bpf_keyring'
+	  property allows policies to match against the keyring
+	  specified in bpf_attr (BUILTIN, SECONDARY, PLATFORM).
+
+	  If unsure, answer Y.
+
 endmenu
 
 config SECURITY_IPE_KUNIT_TEST
security/ipe/audit.c +15 -0
index 3f0deeb549127..251c6ec2f8423 100644
@@ -41,6 +41,7 @@ static const char *const audit_op_names[__IPE_OP_MAX + 1] = {
 	"KEXEC_INITRAMFS",
 	"POLICY",
 	"X509_CERT",
+	"BPF_PROG_LOAD",
 	"UNKNOWN",
 };
 
@@ -51,6 +52,7 @@ static const char *const audit_hook_names[__IPE_HOOK_MAX] = {
 	"MPROTECT",
 	"KERNEL_READ",
 	"KERNEL_LOAD",
+	"BPF_PROG_LOAD",
 };
 
 static const char *const audit_prop_names[__IPE_PROP_MAX] = {
@@ -62,6 +64,19 @@ static const char *const audit_prop_names[__IPE_PROP_MAX] = {
 	"fsverity_digest=",
 	"fsverity_signature=FALSE",
 	"fsverity_signature=TRUE",
+	"bpf_signature=NONE",
+	"bpf_signature=OK",
+	"bpf_signature=UNSIGNED",
+	"bpf_signature=PARTIALSIG",
+	"bpf_signature=UNKNOWNKEY",
+	"bpf_signature=UNEXPECTED",
+	"bpf_signature=FAULT",
+	"bpf_signature=BADSIG",
+	"bpf_keyring=BUILTIN",
+	"bpf_keyring=SECONDARY",
+	"bpf_keyring=PLATFORM",
+	"bpf_kernel=FALSE",
+	"bpf_kernel=TRUE",
 };
 
 /**
security/ipe/eval.c +92 -1

112 lines not on this page · raw message

security/ipe/eval.h +11 -0
index fef65a36468cb..b061cb5ade27e 100644
@@ -37,6 +37,12 @@ struct ipe_inode {
 };
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
 
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+struct ipe_bpf_prog {
+	enum lsm_integrity_verdict verdict;
+};
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
+
 struct ipe_eval_ctx {
 	enum ipe_op_type op;
 	enum ipe_hook_type hook;
@@ -52,6 +58,11 @@ struct ipe_eval_ctx {
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 	const struct ipe_inode *ipe_inode;
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+	enum lsm_integrity_verdict bpf_verdict;
+	s32 bpf_keyring_id;
+	bool bpf_kernel;
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 };
 
 enum ipe_match {
security/ipe/hooks.c +63 -0

66 lines not on this page · raw message

security/ipe/hooks.h +15 -0
index 07db373327402..8a6d1a459e00c 100644
@@ -10,6 +10,7 @@
 #include <linux/security.h>
 #include <linux/blk_types.h>
 #include <linux/fsverity.h>
+#include <linux/bpf.h>
 
 enum ipe_hook_type {
 	IPE_HOOK_BPRM_CHECK = 0,
@@ -18,6 +19,7 @@ enum ipe_hook_type {
 	IPE_HOOK_MPROTECT,
 	IPE_HOOK_KERNEL_READ,
 	IPE_HOOK_KERNEL_LOAD,
+	IPE_HOOK_BPF_PROG_LOAD,
 	__IPE_HOOK_MAX
 };
 
@@ -52,4 +54,17 @@ int ipe_inode_setintegrity(const struct inode *inode, enum lsm_integrity_type ty
 			   const void *value, size_t size);
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
 
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+int ipe_bpf_prog_load_post_integrity(struct bpf_prog *prog,
+				     union bpf_attr *attr,
+				     struct bpf_token *token,
+				     bool kernel,
+				     const struct lsm_id *lsmid,
+				     enum lsm_integrity_verdict verdict);
+int ipe_bpf_prog_load(struct bpf_prog *prog,
+		      union bpf_attr *attr,
+		      struct bpf_token *token,
+		      bool kernel);
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
+
 #endif /* _IPE_HOOKS_H */
security/ipe/ipe.c +14 -0
index 495bb765de1b8..5af13903287fe 100644
@@ -19,6 +19,9 @@ static struct lsm_blob_sizes ipe_blobs __ro_after_init = {
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 	.lbs_inode = sizeof(struct ipe_inode),
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+	.lbs_bpf_prog = sizeof(struct ipe_bpf_prog),
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 };
 
 static const struct lsm_id ipe_lsmid = {
@@ -45,6 +48,13 @@ struct ipe_inode *ipe_inode(const struct inode *inode)
 }
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
 
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+struct ipe_bpf_prog *ipe_bpf_prog(const struct bpf_prog *prog)
+{
+	return prog->aux->security + ipe_blobs.lbs_bpf_prog;
+}
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
+
 static struct security_hook_list ipe_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(bprm_check_security, ipe_bprm_check_security),
 	LSM_HOOK_INIT(bprm_creds_for_exec, ipe_bprm_creds_for_exec),
@@ -60,6 +70,10 @@ static struct security_hook_list ipe_hooks[] __ro_after_init = {
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 	LSM_HOOK_INIT(inode_setintegrity, ipe_inode_setintegrity),
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+	LSM_HOOK_INIT(bpf_prog_load_post_integrity, ipe_bpf_prog_load_post_integrity),
+	LSM_HOOK_INIT(bpf_prog_load, ipe_bpf_prog_load),
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 };
 
 /**
security/ipe/ipe.h +3 -0
index 25cfdb8f0c20a..47de32b5bc938 100644
@@ -22,6 +22,9 @@ struct ipe_bdev *ipe_bdev(struct block_device *b);
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 struct ipe_inode *ipe_inode(const struct inode *inode);
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+struct ipe_bpf_prog *ipe_bpf_prog(const struct bpf_prog *prog);
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 
 int ipe_init_securityfs(void);
 
security/ipe/policy.h +14 -0
index 5bfbdbddeef86..748bea92beb19 100644
@@ -17,6 +17,7 @@ enum ipe_op_type {
 	IPE_OP_KEXEC_INITRAMFS,
 	IPE_OP_POLICY,
 	IPE_OP_X509,
+	IPE_OP_BPF_PROG_LOAD,
 	__IPE_OP_MAX,
 };
 
@@ -39,6 +40,19 @@ enum ipe_prop_type {
 	IPE_PROP_FSV_DIGEST,
 	IPE_PROP_FSV_SIG_FALSE,
 	IPE_PROP_FSV_SIG_TRUE,
+	IPE_PROP_BPF_SIG_NONE,
+	IPE_PROP_BPF_SIG_OK,
+	IPE_PROP_BPF_SIG_UNSIGNED,
+	IPE_PROP_BPF_SIG_PARTIALSIG,
+	IPE_PROP_BPF_SIG_UNKNOWNKEY,
+	IPE_PROP_BPF_SIG_UNEXPECTED,
+	IPE_PROP_BPF_SIG_FAULT,
+	IPE_PROP_BPF_SIG_BADSIG,
+	IPE_PROP_BPF_KEYRING_BUILTIN,
+	IPE_PROP_BPF_KEYRING_SECONDARY,
+	IPE_PROP_BPF_KEYRING_PLATFORM,
+	IPE_PROP_BPF_KERNEL_FALSE,
+	IPE_PROP_BPF_KERNEL_TRUE,
 	__IPE_PROP_MAX
 };
 
security/ipe/policy_parser.c +27 -0
index 6fa5bebf84714..71f63de56616b 100644
@@ -237,6 +237,7 @@ static const match_table_t operation_tokens = {
 	{IPE_OP_KEXEC_INITRAMFS,	"op=KEXEC_INITRAMFS"},
 	{IPE_OP_POLICY,			"op=POLICY"},
 	{IPE_OP_X509,			"op=X509_CERT"},
+	{IPE_OP_BPF_PROG_LOAD,		"op=BPF_PROG_LOAD"},
 	{IPE_OP_INVALID,		NULL}
 };
 
@@ -281,6 +282,19 @@ static const match_table_t property_tokens = {
 	{IPE_PROP_FSV_DIGEST,		"fsverity_digest=%s"},
 	{IPE_PROP_FSV_SIG_FALSE,	"fsverity_signature=FALSE"},
 	{IPE_PROP_FSV_SIG_TRUE,		"fsverity_signature=TRUE"},
+	{IPE_PROP_BPF_SIG_NONE,		"bpf_signature=NONE"},
+	{IPE_PROP_BPF_SIG_OK,		"bpf_signature=OK"},
+	{IPE_PROP_BPF_SIG_UNSIGNED,	"bpf_signature=UNSIGNED"},
+	{IPE_PROP_BPF_SIG_PARTIALSIG,	"bpf_signature=PARTIALSIG"},
+	{IPE_PROP_BPF_SIG_UNKNOWNKEY,	"bpf_signature=UNKNOWNKEY"},
+	{IPE_PROP_BPF_SIG_UNEXPECTED,	"bpf_signature=UNEXPECTED"},
+	{IPE_PROP_BPF_SIG_FAULT,	"bpf_signature=FAULT"},
+	{IPE_PROP_BPF_SIG_BADSIG,	"bpf_signature=BADSIG"},
+	{IPE_PROP_BPF_KEYRING_BUILTIN,	"bpf_keyring=BUILTIN"},
+	{IPE_PROP_BPF_KEYRING_SECONDARY,	"bpf_keyring=SECONDARY"},
+	{IPE_PROP_BPF_KEYRING_PLATFORM,	"bpf_keyring=PLATFORM"},
+	{IPE_PROP_BPF_KERNEL_FALSE,	"bpf_kernel=FALSE"},
+	{IPE_PROP_BPF_KERNEL_TRUE,	"bpf_kernel=TRUE"},
 	{IPE_PROP_INVALID,		NULL}
 };
 
@@ -331,6 +345,19 @@ static int parse_property(char *t, struct ipe_rule *r)
 	case IPE_PROP_DMV_SIG_TRUE:
 	case IPE_PROP_FSV_SIG_FALSE:
 	case IPE_PROP_FSV_SIG_TRUE:
+	case IPE_PROP_BPF_SIG_NONE:
+	case IPE_PROP_BPF_SIG_OK:
+	case IPE_PROP_BPF_SIG_UNSIGNED:
+	case IPE_PROP_BPF_SIG_PARTIALSIG:
+	case IPE_PROP_BPF_SIG_UNKNOWNKEY:
+	case IPE_PROP_BPF_SIG_UNEXPECTED:
+	case IPE_PROP_BPF_SIG_FAULT:
+	case IPE_PROP_BPF_SIG_BADSIG:
+	case IPE_PROP_BPF_KEYRING_BUILTIN:
+	case IPE_PROP_BPF_KEYRING_SECONDARY:
+	case IPE_PROP_BPF_KEYRING_PLATFORM:
+	case IPE_PROP_BPF_KERNEL_FALSE:
+	case IPE_PROP_BPF_KERNEL_TRUE:
 		p->type = token;
 		break;
 	default:
Signature · 1 line
2.53.0
#28 lore raw

Re: [PATCH v7 10/10] ipe: Add BPF program load policy enforcement via Hornet integration

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] The `IPE_PROP_BPF_KERNEL_FALSE` property evaluates to `true` when `CONFIG_IPE_PROP_BPF_SIGNATURE`…

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The IPE_PROP_BPF_KERNEL_FALSE property evaluates to true when CONFIG_IPE_PROP_BPF_SIGNATURE is disabled, violating IPE's fail-closed design.
- [High] ipe_bpf_prog_load_post_integrity() completely ignores the lsmid parameter, allowing other LSMs to silently overwrite Hornet's integrity verdict.
- [Medium] The documentation provides an insecure policy example that allows unprivileged users to bypass the bpf_signature=OK check using their own keyrings. --

commit 341207097f16b88e2b641d2d93566b204484bce3
Author: Blaise Boscaccy <[email protected]>

ipe: Add BPF program load policy enforcement via Hornet integration

Add support for the bpf_prog_load_post_integrity LSM hook, enabling IPE to make policy decisions about BPF program loading based on integrity verdicts provided by the Hornet LSM.

Quoted 4 lines
Documentation/admin-guide/LSM/ipe.rst +0 -0
index a756d81585317..d68ba9d98859e 100644

[ ... ]

Quoted 5 lines
Documentation/admin-guide/LSM/ipe.rst +1 -0
@@ -788,6 +896,58 @@ Allow execution of a specific fs-verity file
 
    op=EXECUTE fsverity_digest=sha256:fd88f2b8824e197f850bf4c5109bea5cf0ee38104f710843bb72da796ba5af9e action=ALLOW
 
+Allow only signed BPF programs

Documentation/admin-guide/LSM/ipe.rst Allow execution of a specific fs-verity file · line 909

Quoted 10 lines
Documentation/admin-guide/LSM/ipe.rst +10 -0
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name=Allow_Signed_BPF policy_version=0.0.0
+   DEFAULT action=ALLOW
+
+   DEFAULT op=BPF_PROG_LOAD action=DENY
+   op=BPF_PROG_LOAD bpf_kernel=TRUE action=ALLOW
+   op=BPF_PROG_LOAD bpf_signature=OK action=ALLOW

Since Hornet verifies the program signature against the user-provided keyring ID, could an unprivileged user bypass this policy by creating a personal user keyring, signing a malicious BPF program with their own key, and passing their keyring ID?

Would it be better to recommend explicitly restricting the bpf_keyring property in this example, perhaps to bpf_keyring=BUILTIN?

[ ... ]

Quoted 4 lines
security/ipe/eval.c +0 -0
index 21439c5be3364..705c4ecfda696 100644

[ ... ]

Quoted 1 line
security/ipe/eval.c +0 -0
@@ -297,6 +362,32 @@ static bool evaluate_property(const struct ipe_eval_ctx *const ctx,

[ ... ]

security/ipe/eval.c static bool evaluate_property(const struct ipe_eval_ctx *const ctx, · line 388

Quoted 2 lines
security/ipe/eval.c +2 -0
+	case IPE_PROP_BPF_KERNEL_FALSE:
+		return !evaluate_bpf_kernel(ctx);

When CONFIG_IPE_PROP_BPF_SIGNATURE is disabled, the stub for evaluate_bpf_kernel() unconditionally returns false:

    static bool evaluate_bpf_kernel(const struct ipe_eval_ctx *const ctx)
    {
        return false;
    }

This causes !evaluate_bpf_kernel(ctx) to return true.

Does this inadvertently cause any IPE policy rule using bpf_kernel=FALSE to always match when the feature is disabled? Should this regression be addressed with a dedicated stub that returns false when disabled to maintain the fail-closed design?

[ ... ]

Quoted 4 lines
security/ipe/hooks.c +0 -0
index 0ae54a880405a..9271e129a2cf2 100644

[ ... ]

Quoted 3 lines
security/ipe/hooks.c +3 -0
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+/**
+ * ipe_bpf_prog_load_post_integrity() - Store integrity verdict in per-prog blob.

[ ... ]

security/ipe/hooks.c int ipe_inode_setintegrity(const struct inode *inode, · line 369

Quoted 11 lines

+ */

security/ipe/hooks.c +10 -0
+int ipe_bpf_prog_load_post_integrity(struct bpf_prog *prog,
+				     union bpf_attr *attr,
+				     struct bpf_token *token,
+				     bool kernel,
+				     const struct lsm_id *lsmid,
+				     enum lsm_integrity_verdict verdict)
+{
+	struct ipe_bpf_prog *blob = ipe_bpf_prog(prog);
+
+	blob->verdict = verdict;

Should this check the lsmid parameter before storing the verdict?

In a multi-LSM environment, it looks like any other integrity provider calling this hook could silently overwrite the verdict previously set by Hornet, potentially replacing an LSM_INT_VERDICT_BADSIG verdict with an LSM_INT_VERDICT_OK or LSM_INT_VERDICT_NONE verdict.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=10
maintainer Acked-by on v7 10/10
#29 lore raw

Re: [PATCH v7 10/10] ipe: Add BPF program load policy enforcement via Hornet integration

In reply to Blaise Boscaccy
Quoted 23 lines · Blaise Boscaccy

Add support for the bpf_prog_load_post_integrity LSM hook, enabling IPE to make policy decisions about BPF program loading based on integrity verdicts provided by the Hornet LSM.

New policy operation:
op=BPF_PROG_LOAD - Matches BPF program load events

New policy properties:

  bpf_signature=NONE      - No Verdict
  bpf_signature=OK        - Program signature and map hashes verified
  bpf_signature=UNSIGNED  - No signature provided
  bpf_signature=PARTIALSIG - Signature OK but no map hash data
  bpf_signature=UNKNOWNKEY - The keyring requested by the user is invalid
  bpf_signature=UNEXPECTED - An unexpected hash value was encountered
  bpf_signature=FAULT      - System error during verification
  bpf_signature=BADSIG    - Signature or map hash verification failed
  bpf_keyring=BUILTIN     - Program was signed using a builtin keyring
  bpf_keyring=SECONDARY   - Program was signed using the secondary keyring
  bpf_keyring=PLATFORM    - Program was signed using the platform keyring
  bpf_kernel=TRUE         - Program originated from kernelspace
  bpf_kernel=FALSE        - Program originated from userspace
Quoted 7 lines

These properties map directly to the lsm_integrity_verdict enum values provided by the Hornet LSM through security_bpf_prog_load_post_integrity.

The feature is gated on CONFIG_IPE_PROP_BPF_SIGNATURE which depends on CONFIG_SECURITY_HORNET.

maintainer Applied
#30 lore raw

Re: [PATCH v7 10/10] ipe: Add BPF program load policy enforcement via Hornet integration

In reply to Blaise Boscaccy
Quoted 31 lines · On May 7, 2026 Blaise Boscaccy <[email protected]> wrote:

Add support for the bpf_prog_load_post_integrity LSM hook, enabling IPE to make policy decisions about BPF program loading based on integrity verdicts provided by the Hornet LSM.

New policy operation:
op=BPF_PROG_LOAD - Matches BPF program load events

New policy properties:

  bpf_signature=NONE      - No Verdict
  bpf_signature=OK        - Program signature and map hashes verified
  bpf_signature=UNSIGNED  - No signature provided
  bpf_signature=PARTIALSIG - Signature OK but no map hash data
  bpf_signature=UNKNOWNKEY - The keyring requested by the user is invalid
  bpf_signature=UNEXPECTED - An unexpected hash value was encountered
  bpf_signature=FAULT 	   - System error during verification
  bpf_signature=BADSIG    - Signature or map hash verification failed
  bpf_keyring=BUILTIN     - Program was signed using a builtin keyring
  bpf_keyring=SECONDARY   - Program was signed using the secondary keyring
  bpf_keyring=PLATFORM    - Program was signed using the platform keyring
  bpf_kernel=TRUE         - Program originated from kernelspace
  bpf_kernel=FALSE        - Program originated from userspace

These properties map directly to the lsm_integrity_verdict enum values provided by the Hornet LSM through security_bpf_prog_load_post_integrity.

The feature is gated on CONFIG_IPE_PROP_BPF_SIGNATURE which depends on CONFIG_SECURITY_HORNET.

Quoted 14 lines
---
 Documentation/admin-guide/LSM/ipe.rst | 162 +++++++++++++++++++++++++-
 Documentation/security/ipe.rst        |  68 +++++++++++
 security/ipe/Kconfig                  |  15 +++
 security/ipe/audit.c                  |  15 +++
 security/ipe/eval.c                   |  93 ++++++++++++++-
 security/ipe/eval.h                   |  11 ++
 security/ipe/hooks.c                  |  63 ++++++++++
 security/ipe/hooks.h                  |  15 +++
 security/ipe/ipe.c                    |  14 +++
 security/ipe/ipe.h                    |   3 +
 security/ipe/policy.h                 |  14 +++
 security/ipe/policy_parser.c          |  27 +++++
 12 files changed, 498 insertions(+), 2 deletions(-)

Merged into lsm/dev, thanks.

--
paul-moore.com

maintainer
#31 lore raw
In reply to Blaise Boscaccy
Quoted 42 lines · Blaise Boscaccy

This patch series introduces the next iteration of the Hornet LSM. Hornet’s goal is to provide a secure and extensible in-kernel signature verification mechanism for eBPF programs.

Hornet addresses concerns from users who require strict audit trails and verification guarantees for eBPF programs, especially in security-sensitive environments. Many production systems need assurance that only authorized, unmodified eBPF programs are loaded into the kernel. Hornet provides this assurance through cryptographic signature verification.

The currently accepted loader-plus-map signature verification scheme, mandated by Alexei and KP, is simple to implement and generally acceptable if users and administrators are satisfied with it. However, verifying both the loader and the maps offers additional benefits beyond verifying the loader alone:

1. Security and Audit Integrity

A key advantage is that the LSM hook for authorizing BPF program loads can operate after signature verification. This ensures:

* Access control decisions are based on verified signature status.
* Accurate system state measurement and logging.
* Log entries claiming a verified signature are truthful, avoiding misleading records where only the loader was verified while the actual BPF program verification occurs later without logging.

2. TOCTOU Attack Prevention

The current map hash implementation may be vulnerable to a TOCTOU attack because it allows unfrozen maps to cache a previously calculated hash. The accepted “trusted loader” scheme cannot detect this and may permit loading altered maps.

3. Supply Chain Integrity

Verify that eBPF programs and their associated map data have not been modified since they were built and signed, in the kernel proper, may aid in protecting against supply chain attacks.

Quoted 9 lines

This approach addresses concerns from users who require strict audit trails and verification guarantees, especially in security-sensitive environments. Map hashes for extended verification are passed via the existing PKCS#7 UAPI and verified by the crypto subsystem. Hornet then calculates the program’s verification state. Hornet itself does not enforce a policy on whether unsigned or partially signed programs should be rejected. It delegates that decision to downstream LSMs hook, making it a composable building block in a larger security architecture.

[NOTE: trimmed changelog for brevity]

Quoted 69 lines

Blaise Boscaccy (6):
lsm: security: Add additional enum values for bpf integrity checks
security: Hornet LSM
hornet: Introduce gen_sig
hornet: Add a light skeleton data extractor scripts selftests/hornet: Add a selftest for the Hornet LSM
ipe: Add BPF program load policy enforcement via Hornet integration

James Bottomley (3):
crypto: pkcs7: add flag for validated trust on a signed info block
crypto: pkcs7: add ability to extract signed attributes by OID
crypto: pkcs7: add tests for pkcs7_get_authattr

Paul Moore (1):
lsm: framework for BPF integrity verification

 Documentation/admin-guide/LSM/Hornet.rst     | 323 +++++++++++++++
 Documentation/admin-guide/LSM/index.rst      |   1 +
 Documentation/admin-guide/LSM/ipe.rst        | 162 +++++++-
 Documentation/security/ipe.rst               |  68 ++++
 MAINTAINERS                                  |   9 +
 certs/system_keyring.c                       |   1 +
 crypto/asymmetric_keys/Makefile              |   4 +-
 crypto/asymmetric_keys/pkcs7_aa.asn1         |  18 +
 crypto/asymmetric_keys/pkcs7_key_type.c      |  44 +-
 crypto/asymmetric_keys/pkcs7_parser.c        |  81 ++++
 crypto/asymmetric_keys/pkcs7_parser.h        |   1 +
 crypto/asymmetric_keys/pkcs7_trust.c         |   1 +
 include/crypto/pkcs7.h                       |   4 +
 include/linux/lsm_hook_defs.h                |   5 +
 include/linux/oid_registry.h                 |   3 +
 include/linux/security.h                     |  28 ++
 include/uapi/linux/lsm.h                     |   1 +
 scripts/Makefile                             |   1 +
 scripts/hornet/Makefile                      |   5 +
 scripts/hornet/extract-insn.sh               |  27 ++
 scripts/hornet/extract-map.sh                |  27 ++
 scripts/hornet/extract-skel.sh               |  27 ++
 scripts/hornet/gen_sig.c                     | 401 +++++++++++++++++++
 scripts/hornet/write-sig.sh                  |  27 ++
 security/Kconfig                             |   3 +-
 security/Makefile                            |   1 +
 security/hornet/Kconfig                      |  13 +
 security/hornet/Makefile                     |   7 +
 security/hornet/hornet.asn1                  |  12 +
 security/hornet/hornet_lsm.c                 | 352 ++++++++++++++++
 security/ipe/Kconfig                         |  15 +
 security/ipe/audit.c                         |  15 +
 security/ipe/eval.c                          |  93 ++++-
 security/ipe/eval.h                          |  11 +
 security/ipe/hooks.c                         |  63 +++
 security/ipe/hooks.h                         |  15 +
 security/ipe/ipe.c                           |  14 +
 security/ipe/ipe.h                           |   3 +
 security/ipe/policy.h                        |  14 +
 security/ipe/policy_parser.c                 |  27 ++
 security/security.c                          |  75 +++-
 tools/testing/selftests/Makefile             |   1 +
 tools/testing/selftests/hornet/Makefile      |  63 +++
 tools/testing/selftests/hornet/loader.c      |  21 +
 tools/testing/selftests/hornet/trivial.bpf.c |  33 ++
 45 files changed, 2112 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
 create mode 100644 crypto/asymmetric_keys/pkcs7_aa.asn1
 create mode 100644 scripts/hornet/Makefile
 create mode 100755 scripts/hornet/extract-insn.sh
 create mode 100755 scripts/hornet/extract-map.sh
 create mode 100755 scripts/hornet/extract-skel.sh
 create mode 100644 scripts/hornet/gen_sig.c
Quoted 8 lines
 create mode 100755 scripts/hornet/write-sig.sh
 create mode 100644 security/hornet/Kconfig
 create mode 100644 security/hornet/Makefile
 create mode 100644 security/hornet/hornet.asn1
 create mode 100644 security/hornet/hornet_lsm.c
 create mode 100644 tools/testing/selftests/hornet/Makefile
 create mode 100644 tools/testing/selftests/hornet/loader.c
 create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c

[NOTE: added the linux-crypto list to the To/CC lines]

Hi crypto folks,

You'll notice there are three patches from James Bottomley in this patchset that touch crypto code and I'd appreciate it if you could take a look and either ACK the patches or let James and Blaise know what you would like changed. James did send these patches to you for review some time ago, so they aren't necessarily new, but I wanted to make sure you saw them again.

Unfortunately, it doesn't look like the crypto list was CC'd on this patchset, so here is a lore link to the patchset as a whole:

https://lore.kernel.org/linux-security-module/[email protected]

... and here are lore links to the three crypto patches:

https://lore.kernel.org/linux-security-module/[email protected]

https://lore.kernel.org/linux-security-module/[email protected]

https://lore.kernel.org/linux-security-module/[email protected]

Thanks.

Signature · 1 line
paul-moore.com
#32 lore raw
Quoted 145 lines · Paul Moore
Quoted 42 lines · Blaise Boscaccy

This patch series introduces the next iteration of the Hornet LSM. Hornet’s goal is to provide a secure and extensible in-kernel signature verification mechanism for eBPF programs.

Hornet addresses concerns from users who require strict audit trails and verification guarantees for eBPF programs, especially in security-sensitive environments. Many production systems need assurance that only authorized, unmodified eBPF programs are loaded into the kernel. Hornet provides this assurance through cryptographic signature verification.

The currently accepted loader-plus-map signature verification scheme, mandated by Alexei and KP, is simple to implement and generally acceptable if users and administrators are satisfied with it. However, verifying both the loader and the maps offers additional benefits beyond verifying the loader alone:

1. Security and Audit Integrity

A key advantage is that the LSM hook for authorizing BPF program loads can operate after signature verification. This ensures:

* Access control decisions are based on verified signature status.
* Accurate system state measurement and logging.
* Log entries claiming a verified signature are truthful, avoiding misleading records where only the loader was verified while the actual BPF program verification occurs later without logging.

2. TOCTOU Attack Prevention

The current map hash implementation may be vulnerable to a TOCTOU attack because it allows unfrozen maps to cache a previously calculated hash. The accepted “trusted loader” scheme cannot detect this and may permit loading altered maps.

3. Supply Chain Integrity

Verify that eBPF programs and their associated map data have not been modified since they were built and signed, in the kernel proper, may aid in protecting against supply chain attacks.

Quoted 9 lines

This approach addresses concerns from users who require strict audit trails and verification guarantees, especially in security-sensitive environments. Map hashes for extended verification are passed via the existing PKCS#7 UAPI and verified by the crypto subsystem. Hornet then calculates the program’s verification state. Hornet itself does not enforce a policy on whether unsigned or partially signed programs should be rejected. It delegates that decision to downstream LSMs hook, making it a composable building block in a larger security architecture.

[NOTE: trimmed changelog for brevity]

Quoted 69 lines

Blaise Boscaccy (6):
lsm: security: Add additional enum values for bpf integrity checks
security: Hornet LSM
hornet: Introduce gen_sig
hornet: Add a light skeleton data extractor scripts selftests/hornet: Add a selftest for the Hornet LSM
ipe: Add BPF program load policy enforcement via Hornet integration

James Bottomley (3):
crypto: pkcs7: add flag for validated trust on a signed info block
crypto: pkcs7: add ability to extract signed attributes by OID
crypto: pkcs7: add tests for pkcs7_get_authattr

Paul Moore (1):
lsm: framework for BPF integrity verification

 Documentation/admin-guide/LSM/Hornet.rst     | 323 +++++++++++++++
 Documentation/admin-guide/LSM/index.rst      |   1 +
 Documentation/admin-guide/LSM/ipe.rst        | 162 +++++++-
 Documentation/security/ipe.rst               |  68 ++++
 MAINTAINERS                                  |   9 +
 certs/system_keyring.c                       |   1 +
 crypto/asymmetric_keys/Makefile              |   4 +-
 crypto/asymmetric_keys/pkcs7_aa.asn1         |  18 +
 crypto/asymmetric_keys/pkcs7_key_type.c      |  44 +-
 crypto/asymmetric_keys/pkcs7_parser.c        |  81 ++++
 crypto/asymmetric_keys/pkcs7_parser.h        |   1 +
 crypto/asymmetric_keys/pkcs7_trust.c         |   1 +
 include/crypto/pkcs7.h                       |   4 +
 include/linux/lsm_hook_defs.h                |   5 +
 include/linux/oid_registry.h                 |   3 +
 include/linux/security.h                     |  28 ++
 include/uapi/linux/lsm.h                     |   1 +
 scripts/Makefile                             |   1 +
 scripts/hornet/Makefile                      |   5 +
 scripts/hornet/extract-insn.sh               |  27 ++
 scripts/hornet/extract-map.sh                |  27 ++
 scripts/hornet/extract-skel.sh               |  27 ++
 scripts/hornet/gen_sig.c                     | 401 +++++++++++++++++++
 scripts/hornet/write-sig.sh                  |  27 ++
 security/Kconfig                             |   3 +-
 security/Makefile                            |   1 +
 security/hornet/Kconfig                      |  13 +
 security/hornet/Makefile                     |   7 +
 security/hornet/hornet.asn1                  |  12 +
 security/hornet/hornet_lsm.c                 | 352 ++++++++++++++++
 security/ipe/Kconfig                         |  15 +
 security/ipe/audit.c                         |  15 +
 security/ipe/eval.c                          |  93 ++++-
 security/ipe/eval.h                          |  11 +
 security/ipe/hooks.c                         |  63 +++
 security/ipe/hooks.h                         |  15 +
 security/ipe/ipe.c                           |  14 +
 security/ipe/ipe.h                           |   3 +
 security/ipe/policy.h                        |  14 +
 security/ipe/policy_parser.c                 |  27 ++
 security/security.c                          |  75 +++-
 tools/testing/selftests/Makefile             |   1 +
 tools/testing/selftests/hornet/Makefile      |  63 +++
 tools/testing/selftests/hornet/loader.c      |  21 +
 tools/testing/selftests/hornet/trivial.bpf.c |  33 ++
 45 files changed, 2112 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
 create mode 100644 crypto/asymmetric_keys/pkcs7_aa.asn1
 create mode 100644 scripts/hornet/Makefile
 create mode 100755 scripts/hornet/extract-insn.sh
 create mode 100755 scripts/hornet/extract-map.sh
 create mode 100755 scripts/hornet/extract-skel.sh
 create mode 100644 scripts/hornet/gen_sig.c
Quoted 8 lines
 create mode 100755 scripts/hornet/write-sig.sh
 create mode 100644 security/hornet/Kconfig
 create mode 100644 security/hornet/Makefile
 create mode 100644 security/hornet/hornet.asn1
 create mode 100644 security/hornet/hornet_lsm.c
 create mode 100644 tools/testing/selftests/hornet/Makefile
 create mode 100644 tools/testing/selftests/hornet/loader.c
 create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c

[NOTE: added the linux-crypto list to the To/CC lines]

Hi crypto folks,

You'll notice there are three patches from James Bottomley in this patchset that touch crypto code and I'd appreciate it if you could take a look and either ACK the patches or let James and Blaise know what you would like changed. James did send these patches to you for review some time ago, so they aren't necessarily new, but I wanted to make sure you saw them again.

Quoted 6 lines

Unfortunately, it doesn't look like the crypto list was CC'd on this patchset, so here is a lore link to the patchset as a whole:

https://lore.kernel.org/linux-security-module/[email protected]

... and here are lore links to the three crypto patches:

We discussed before how the actual signature check seemed to have been overlooked in some cases, due to the complexities of PKCS#7
(https://lore.kernel.org/r/20260305185016.GC2796@quark/). Looks like that was fixed. It is really hard to do any meaningful review of a PKCS#7 based system, though. And it sounds like this one is proceeding anyway due to some requirement to be compatible with an existing PKCS#7 based system. So I'm not sure what you're looking for.

- Eric

maintainer
#33 lore raw
Quoted 153 lines · Eric Biggers
Quoted 145 lines · Paul Moore
Quoted 42 lines · Blaise Boscaccy

This patch series introduces the next iteration of the Hornet LSM. Hornet’s goal is to provide a secure and extensible in-kernel signature verification mechanism for eBPF programs.

Hornet addresses concerns from users who require strict audit trails and verification guarantees for eBPF programs, especially in security-sensitive environments. Many production systems need assurance that only authorized, unmodified eBPF programs are loaded into the kernel. Hornet provides this assurance through cryptographic signature verification.

The currently accepted loader-plus-map signature verification scheme, mandated by Alexei and KP, is simple to implement and generally acceptable if users and administrators are satisfied with it. However, verifying both the loader and the maps offers additional benefits beyond verifying the loader alone:

1. Security and Audit Integrity

A key advantage is that the LSM hook for authorizing BPF program loads can operate after signature verification. This ensures:

* Access control decisions are based on verified signature status.
* Accurate system state measurement and logging.
* Log entries claiming a verified signature are truthful, avoiding misleading records where only the loader was verified while the actual BPF program verification occurs later without logging.

2. TOCTOU Attack Prevention

The current map hash implementation may be vulnerable to a TOCTOU attack because it allows unfrozen maps to cache a previously calculated hash. The accepted “trusted loader” scheme cannot detect this and may permit loading altered maps.

3. Supply Chain Integrity

Verify that eBPF programs and their associated map data have not been modified since they were built and signed, in the kernel proper, may aid in protecting against supply chain attacks.

Quoted 9 lines

This approach addresses concerns from users who require strict audit trails and verification guarantees, especially in security-sensitive environments. Map hashes for extended verification are passed via the existing PKCS#7 UAPI and verified by the crypto subsystem. Hornet then calculates the program’s verification state. Hornet itself does not enforce a policy on whether unsigned or partially signed programs should be rejected. It delegates that decision to downstream LSMs hook, making it a composable building block in a larger security architecture.

[NOTE: trimmed changelog for brevity]

Quoted 69 lines

Blaise Boscaccy (6):
lsm: security: Add additional enum values for bpf integrity checks
security: Hornet LSM
hornet: Introduce gen_sig
hornet: Add a light skeleton data extractor scripts selftests/hornet: Add a selftest for the Hornet LSM
ipe: Add BPF program load policy enforcement via Hornet integration

James Bottomley (3):
crypto: pkcs7: add flag for validated trust on a signed info block
crypto: pkcs7: add ability to extract signed attributes by OID
crypto: pkcs7: add tests for pkcs7_get_authattr

Paul Moore (1):
lsm: framework for BPF integrity verification

 Documentation/admin-guide/LSM/Hornet.rst     | 323 +++++++++++++++
 Documentation/admin-guide/LSM/index.rst      |   1 +
 Documentation/admin-guide/LSM/ipe.rst        | 162 +++++++-
 Documentation/security/ipe.rst               |  68 ++++
 MAINTAINERS                                  |   9 +
 certs/system_keyring.c                       |   1 +
 crypto/asymmetric_keys/Makefile              |   4 +-
 crypto/asymmetric_keys/pkcs7_aa.asn1         |  18 +
 crypto/asymmetric_keys/pkcs7_key_type.c      |  44 +-
 crypto/asymmetric_keys/pkcs7_parser.c        |  81 ++++
 crypto/asymmetric_keys/pkcs7_parser.h        |   1 +
 crypto/asymmetric_keys/pkcs7_trust.c         |   1 +
 include/crypto/pkcs7.h                       |   4 +
 include/linux/lsm_hook_defs.h                |   5 +
 include/linux/oid_registry.h                 |   3 +
 include/linux/security.h                     |  28 ++
 include/uapi/linux/lsm.h                     |   1 +
 scripts/Makefile                             |   1 +
 scripts/hornet/Makefile                      |   5 +
 scripts/hornet/extract-insn.sh               |  27 ++
 scripts/hornet/extract-map.sh                |  27 ++
 scripts/hornet/extract-skel.sh               |  27 ++
 scripts/hornet/gen_sig.c                     | 401 +++++++++++++++++++
 scripts/hornet/write-sig.sh                  |  27 ++
 security/Kconfig                             |   3 +-
 security/Makefile                            |   1 +
 security/hornet/Kconfig                      |  13 +
 security/hornet/Makefile                     |   7 +
 security/hornet/hornet.asn1                  |  12 +
 security/hornet/hornet_lsm.c                 | 352 ++++++++++++++++
 security/ipe/Kconfig                         |  15 +
 security/ipe/audit.c                         |  15 +
 security/ipe/eval.c                          |  93 ++++-
 security/ipe/eval.h                          |  11 +
 security/ipe/hooks.c                         |  63 +++
 security/ipe/hooks.h                         |  15 +
 security/ipe/ipe.c                           |  14 +
 security/ipe/ipe.h                           |   3 +
 security/ipe/policy.h                        |  14 +
 security/ipe/policy_parser.c                 |  27 ++
 security/security.c                          |  75 +++-
 tools/testing/selftests/Makefile             |   1 +
 tools/testing/selftests/hornet/Makefile      |  63 +++
 tools/testing/selftests/hornet/loader.c      |  21 +
 tools/testing/selftests/hornet/trivial.bpf.c |  33 ++
 45 files changed, 2112 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
 create mode 100644 crypto/asymmetric_keys/pkcs7_aa.asn1
 create mode 100644 scripts/hornet/Makefile
 create mode 100755 scripts/hornet/extract-insn.sh
 create mode 100755 scripts/hornet/extract-map.sh
 create mode 100755 scripts/hornet/extract-skel.sh
 create mode 100644 scripts/hornet/gen_sig.c
Quoted 8 lines
 create mode 100755 scripts/hornet/write-sig.sh
 create mode 100644 security/hornet/Kconfig
 create mode 100644 security/hornet/Makefile
 create mode 100644 security/hornet/hornet.asn1
 create mode 100644 security/hornet/hornet_lsm.c
 create mode 100644 tools/testing/selftests/hornet/Makefile
 create mode 100644 tools/testing/selftests/hornet/loader.c
 create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c

[NOTE: added the linux-crypto list to the To/CC lines]

Hi crypto folks,

You'll notice there are three patches from James Bottomley in this patchset that touch crypto code and I'd appreciate it if you could take a look and either ACK the patches or let James and Blaise know what you would like changed. James did send these patches to you for review some time ago, so they aren't necessarily new, but I wanted to make sure you saw them again.

Quoted 6 lines

Unfortunately, it doesn't look like the crypto list was CC'd on this patchset, so here is a lore link to the patchset as a whole:

https://lore.kernel.org/linux-security-module/[email protected]

... and here are lore links to the three crypto patches:

Quoted 7 lines

We discussed before how the actual signature check seemed to have been overlooked in some cases, due to the complexities of PKCS#7
(https://lore.kernel.org/r/20260305185016.GC2796@quark/). Looks like that was fixed. It is really hard to do any meaningful review of a PKCS#7 based system, though. And it sounds like this one is proceeding anyway due to some requirement to be compatible with an existing PKCS#7 based system. So I'm not sure what you're looking for.

Ideally an ACK that you approve of merging those three crypto patches via the LSM tree, or a quick comment if you happen to see anything that needs changing.

Signature · 1 line
paul-moore.com