* [PATCH 00/11] hornet: security, tooling and selftest fixes
@ 2026-05-28 3:08 Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 01/11] hornet: fix TOCTOU in signed program verification Blaise Boscaccy
` (10 more replies)
0 siblings, 11 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
Patch 1 closes a TOCTOU race in signature verification. Map
contents were hashed at the program-load hook and re-hashed at
the program-run hook, leaving a window in which a sufficiently
privileged attacker could mutate a map between the two checks
and run a program whose maps no longer matched what was signed.
The fix records the verified hashes on the prog at load time
and, in security_bpf_prog, checks them against
prog->aux->used_maps — the same map set the verifier and
runtime resolve against — so the verified and executed sets
cannot diverge. The per-map index in the signature format is no
longer needed and is dropped; the check becomes a subset test.
Reported by Eric Biggers.
Patches 2-3 fix two counting bugs in the same area: duplicate maps
could satisfy the required hash count, and an off-by-one capped
accepted maps at MAX_USED_MAPS.
Patches 4-11 are in response to sashiko feedback found here:
https://sashiko.dev/#/patchset/20260507191416.2984054-1-bboscaccy%40linux.microsoft.com
They provide some correctness fixes in the hornet tooling along with
making the selftest behave under cross-compilation and skip cleanly
when signing keys / bpftool / vmlinux BTF are unavailable, instead of
breaking the global selftest build.
Blaise Boscaccy (11):
hornet: fix TOCTOU in signed program verification
hornet: invert map set check logic
hornet: fix off-by-one bug in max used maps check
selftests: hornet: handle cross compilation and test skipping
hornet: gen_sig: fix off-by-one check for used maps
hornet: gen_sig: fix error string allocations
hornet: gen_sig: check for bad allocations
hornet: gen_sig: fix missing command line switches
hornet: scripts: set a non-zero error code for usage
hornet: scripts: harden scripts to handle trailing whitespace
hornet: scripts: Improve argument handling and error messages
Documentation/admin-guide/LSM/Hornet.rst | 39 +++---
scripts/hornet/extract-insn.sh | 24 ++--
scripts/hornet/extract-map.sh | 25 ++--
scripts/hornet/extract-skel.sh | 35 ++++--
scripts/hornet/gen_sig.c | 61 ++++++----
scripts/hornet/write-sig.sh | 10 +-
security/hornet/hornet.asn1 | 1 -
security/hornet/hornet_lsm.c | 148 ++++-------------------
tools/testing/selftests/hornet/Makefile | 114 +++++++++++++----
9 files changed, 235 insertions(+), 222 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 01/11] hornet: fix TOCTOU in signed program verification
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 02/11] hornet: invert map set check logic Blaise Boscaccy
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
The signature verification path was vulnerable to a time-of-check vs
time-of-use race at both the program load and program run hook sites:
between the moment a map's contents were hashed for signature
verification and the moment the program run hook re-verified them, an
attacker with sufficient privileges could swap or mutate the map
contents.
Close the race by snapshotting the map hashes during program load,
attaching them to the program, and re-verifying them from the
security_bpf_prog hook against prog->aux->used_maps. Because used_maps
is the same map set the verifier and runtime resolve against, there is
no longer a window in which the verified set and the executed set can
diverge.
Since we are no longer targeting the fd_array passed in, drop the map
index data entirely and check for whether or not the set of requested
map hashes is a subset of prog->aux->used_maps.
Reported-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
Documentation/admin-guide/LSM/Hornet.rst | 39 +++-----
scripts/hornet/gen_sig.c | 17 +---
security/hornet/hornet.asn1 | 1 -
security/hornet/hornet_lsm.c | 121 +++--------------------
tools/testing/selftests/hornet/Makefile | 2 +-
5 files changed, 35 insertions(+), 145 deletions(-)
diff --git a/Documentation/admin-guide/LSM/Hornet.rst b/Documentation/admin-guide/LSM/Hornet.rst
index 0ade4c17374c6..a369bc11408f4 100644
--- a/Documentation/admin-guide/LSM/Hornet.rst
+++ b/Documentation/admin-guide/LSM/Hornet.rst
@@ -86,15 +86,14 @@ Hornet protects against the following threats:
- **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``.
+ SHA-256 hashes at load time after the program is publically exposed.
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).
+ the map check).
Known Limitations
=================
@@ -117,6 +116,10 @@ Known Limitations
data. It does not guarantee positional binding of maps to specific
fd_array slots.
+- Map hash verification does not enforce any ordering. It simply asserts
+ that the set of map hashes requested to be verified exist in the used
+ array.
+
- BPF_MAP_TYPE_PROG_ARRAY maps must be frozen for Hornet to verify
them. Unfrozen prog array maps are not covered by verification.
@@ -159,24 +162,19 @@ The following describes what happens when a userspace program calls
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.
+ of map hash hashes
-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.
+6. For each map hash entry, Hornet retrieves stores the target map hash in
+ the program's LSM blob.
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.
+8. After the verifier processes the program, once it's ready to be published,
+ Hornet intercepts the ``bpf_prog`` hook, and verifies that the set of
+ required hashes exist in the programs used maps. If the map hashes are
+ unable to be found, the command is denied.
Userspace Interface
-------------------
@@ -199,14 +197,10 @@ 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.
+Each ``Map`` entry contains an expected SHA-256 hash.
Tooling
=======
@@ -229,7 +223,7 @@ Usage::
--key <signer.key> \
[--pass <passphrase>] \
--out <signature.p7b> \
- [--add <mapfile.bin>:<index> ...]
+ [--add <mapfile.bin> ...]
``--data``
Path to the binary file containing eBPF program instructions to sign.
@@ -248,8 +242,7 @@ Usage::
``--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.
+ binary map file. This option may be specified multiple times.
extract-skel.sh
---------------
diff --git a/scripts/hornet/gen_sig.c b/scripts/hornet/gen_sig.c
index 8dd9ed66346a2..b4f983ab24bcd 100644
--- a/scripts/hornet/gen_sig.c
+++ b/scripts/hornet/gen_sig.c
@@ -55,7 +55,6 @@
struct hash_spec {
char *file;
- int index;
};
typedef struct {
@@ -66,7 +65,6 @@ typedef struct {
DECLARE_ASN1_FUNCTIONS(HORNET_MAP)
ASN1_SEQUENCE(HORNET_MAP) = {
- ASN1_SIMPLE(HORNET_MAP, index, ASN1_INTEGER),
ASN1_SIMPLE(HORNET_MAP, hash, ASN1_OCTET_STRING)
} ASN1_SEQUENCE_END(HORNET_MAP);
@@ -253,12 +251,11 @@ static int sha256(const char *path, unsigned char out[SHA256_LEN], unsigned int
return rc;
}
-static void add_hash(MAP_SET *set, unsigned char *buffer, int buffer_len, int index)
+static void add_hash(MAP_SET *set, unsigned char *buffer, int buffer_len)
{
HORNET_MAP *map = NULL;
map = HORNET_MAP_new();
- ASN1_INTEGER_set(map->index, index);
ASN1_OCTET_STRING_set(map->hash, buffer, buffer_len);
sk_HORNET_MAP_push(set->maps, map);
}
@@ -320,14 +317,8 @@ int main(int argc, char **argv)
data_path = optarg;
break;
case 'A':
- if (strchr(optarg, ':')) {
- hashes[hash_count].file = strsep(&optarg, ":");
- hashes[hash_count].index = atoi(optarg);
- if (++hash_count >= MAX_HASHES) {
- usage(argv[0]);
- return EXIT_FAILURE;
- }
- } else {
+ hashes[hash_count].file = optarg;
+ if (++hash_count >= MAX_HASHES) {
usage(argv[0]);
return EXIT_FAILURE;
}
@@ -371,7 +362,7 @@ int main(int argc, char **argv)
if (sha256(hashes[i].file, hash_buffer, &hash_len) != 0) {
DIE("failed to hash input");
}
- add_hash(set, hash_buffer, hash_len, hashes[i].index);
+ add_hash(set, hash_buffer, hash_len);
}
oid = OBJ_txt2obj("2.25.316487325684022475439036912669789383960", 1);
diff --git a/security/hornet/hornet.asn1 b/security/hornet/hornet.asn1
index e60abf451ae23..3cf50379f5e7c 100644
--- a/security/hornet/hornet.asn1
+++ b/security/hornet/hornet.asn1
@@ -7,6 +7,5 @@
HornetData ::= SET OF Map
Map ::= SEQUENCE {
- index INTEGER ({ hornet_map_index }),
sha OCTET STRING ({ hornet_map_hash })
} ({ hornet_next_map })
diff --git a/security/hornet/hornet_lsm.c b/security/hornet/hornet_lsm.c
index a4d11fa5b0889..516038413f321 100644
--- a/security/hornet/hornet_lsm.c
+++ b/security/hornet/hornet_lsm.c
@@ -21,26 +21,18 @@
#define MAX_USED_MAPS 64
-struct hornet_maps {
- bpfptr_t fd_array;
-};
-
/* The only hashing algorithm available is SHA256 due to it be hardcoded
* in the bpf subsystem.
*/
-
-struct hornet_parse_context {
- int indexes[MAX_USED_MAPS];
- bool skips[MAX_USED_MAPS];
- unsigned char hashes[SHA256_DIGEST_SIZE * MAX_USED_MAPS];
- int hash_count;
-};
-
struct hornet_prog_security_struct {
int signed_hash_count;
unsigned char signed_hashes[SHA256_DIGEST_SIZE * MAX_USED_MAPS];
};
+struct hornet_parse_context {
+ struct hornet_prog_security_struct *security;
+};
+
struct lsm_blob_sizes hornet_blob_sizes __ro_after_init = {
.lbs_bpf_prog = sizeof(struct hornet_prog_security_struct),
};
@@ -51,79 +43,17 @@ hornet_bpf_prog_security(struct bpf_prog *prog)
return prog->aux->security + hornet_blob_sizes.lbs_bpf_prog;
}
-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;
- 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));
- if (err != 0)
- return LSM_INT_VERDICT_FAULT;
-
- CLASS(fd, f)(map_fd);
- if (fd_empty(f))
- return LSM_INT_VERDICT_FAULT;
- if (unlikely(fd_file(f)->f_op != &bpf_map_fops))
- return LSM_INT_VERDICT_FAULT;
-
- map = fd_file(f)->private_data;
- if (!READ_ONCE(map->frozen))
- return LSM_INT_VERDICT_FAULT;
-
- if (!map->ops->map_get_hash)
- return LSM_INT_VERDICT_FAULT;
-
- if (map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash))
- return LSM_INT_VERDICT_FAULT;
-
- err = memcmp(hash, &ctx->hashes[i * SHA256_DIGEST_SIZE],
- SHA256_DIGEST_SIZE);
- if (err)
- return LSM_INT_VERDICT_UNEXPECTED;
-
- memcpy(&security->signed_hashes[security->signed_hash_count * SHA256_DIGEST_SIZE],
- &ctx->hashes[i * SHA256_DIGEST_SIZE], SHA256_DIGEST_SIZE);
- security->signed_hash_count++;
- }
- return LSM_INT_VERDICT_OK;
-}
-
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)
+ if (++ctx->security->signed_hash_count >= MAX_USED_MAPS)
return -EINVAL;
return 0;
}
-int hornet_map_index(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 (vlen != 1)
- return -EINVAL;
-
- ctx->indexes[ctx->hash_count] = *(u8 *)value;
- return 0;
-}
-
int hornet_map_hash(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
@@ -134,11 +64,8 @@ int hornet_map_hash(void *context, size_t hdrlen,
if (vlen != SHA256_DIGEST_SIZE && vlen != 0)
return -EINVAL;
- if (vlen) {
- ctx->skips[ctx->hash_count] = false;
- memcpy(&ctx->hashes[ctx->hash_count * SHA256_DIGEST_SIZE], value, vlen);
- } else
- ctx->skips[ctx->hash_count] = true;
+ memcpy(&ctx->security->signed_hashes[ctx->security->signed_hash_count * SHA256_DIGEST_SIZE],
+ value, vlen);
return 0;
}
@@ -147,7 +74,6 @@ 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)
{
- struct hornet_maps maps = {0};
bpfptr_t usig = make_bpfptr(attr->signature, is_kernel);
struct pkcs7_message *msg;
struct hornet_parse_context *ctx;
@@ -172,7 +98,8 @@ static int hornet_check_program(struct bpf_prog *prog, union bpf_attr *attr,
if (!ctx)
return -ENOMEM;
- maps.fd_array = make_bpfptr(attr->fd_array, is_kernel);
+ ctx->security = hornet_bpf_prog_security(prog);
+
sig = kzalloc(attr->signature_size, GFP_KERNEL);
if (!sig) {
err = -ENOMEM;
@@ -225,7 +152,7 @@ static int hornet_check_program(struct bpf_prog *prog, union bpf_attr *attr,
goto cleanup_msg;
}
- *verdict = hornet_verify_hashes(&maps, ctx, prog);
+ *verdict = LSM_INT_VERDICT_OK;
err = 0;
cleanup_msg:
@@ -257,10 +184,8 @@ static int hornet_bpf_prog_load_integrity(struct bpf_prog *prog, union bpf_attr
&hornet_lsmid, verdict);
}
-static int hornet_check_prog_maps(u32 ufd)
+static int hornet_check_prog_maps(struct bpf_prog *prog)
{
- CLASS(fd, f)(ufd);
- struct bpf_prog *prog;
struct hornet_prog_security_struct *security;
unsigned char hash[SHA256_DIGEST_SIZE];
struct bpf_map *map;
@@ -268,12 +193,6 @@ static int hornet_check_prog_maps(u32 ufd)
bool found;
int covered_count = 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)
@@ -316,26 +235,14 @@ static int hornet_check_prog_maps(u32 ufd)
return 0;
}
-static int hornet_bpf(int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
+static int hornet_bpf_prog(struct bpf_prog *prog)
{
- /* 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
- * policy enforcement.
- */
-
- if (cmd != BPF_PROG_RUN)
- return 0;
- if (kernel)
- return 0;
-
- return hornet_check_prog_maps(attr->test.prog_fd);
+ return hornet_check_prog_maps(prog);
}
static struct security_hook_list hornet_hooks[] __ro_after_init = {
LSM_HOOK_INIT(bpf_prog_load_integrity, hornet_bpf_prog_load_integrity),
- LSM_HOOK_INIT(bpf, hornet_bpf),
+ LSM_HOOK_INIT(bpf_prog, hornet_bpf_prog),
};
static int __init hornet_init(void)
diff --git a/tools/testing/selftests/hornet/Makefile b/tools/testing/selftests/hornet/Makefile
index 432bce59f54e7..316364f95f28c 100644
--- a/tools/testing/selftests/hornet/Makefile
+++ b/tools/testing/selftests/hornet/Makefile
@@ -51,7 +51,7 @@ $(OUTPUT)/gen_sig: ../../../../scripts/hornet/gen_sig.c
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
+ --data insn.bin --add map.bin --out sig.bin
signed_loader.h: sig.bin
$(SCRIPTSDIR)/write-sig.sh loader.h sig.bin > $@
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 02/11] hornet: invert map set check logic
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 01/11] hornet: fix TOCTOU in signed program verification Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 03/11] hornet: fix off-by-one bug in max used maps check Blaise Boscaccy
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
In a multi-map hash verification scenario, a logic bug may have
allowed an attacker to provide duplicate maps to satisfy the hash
check count. Instead, invert the logic to verify each map discretely
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
security/hornet/hornet_lsm.c | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/security/hornet/hornet_lsm.c b/security/hornet/hornet_lsm.c
index 516038413f321..35d9522d6bc72 100644
--- a/security/hornet/hornet_lsm.c
+++ b/security/hornet/hornet_lsm.c
@@ -191,7 +191,6 @@ static int hornet_check_prog_maps(struct bpf_prog *prog)
struct bpf_map *map;
int i, j;
bool found;
- int covered_count = 0;
security = hornet_bpf_prog_security(prog);
@@ -200,18 +199,18 @@ static int hornet_check_prog_maps(struct bpf_prog *prog)
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];
+ /* Verify every signed map exists in used_maps */
+ for (i = 0; i < security->signed_hash_count; i++) {
+ found = false;
+ 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 (!READ_ONCE(map->frozen) || !map->ops->map_get_hash)
+ continue;
- if (map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash))
- continue;
+ if (map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash))
+ continue;
- 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) {
@@ -223,15 +222,10 @@ static int hornet_check_prog_maps(struct bpf_prog *prog)
mutex_unlock(&prog->aux->used_maps_mutex);
return -EPERM;
}
- covered_count++;
}
mutex_unlock(&prog->aux->used_maps_mutex);
- /* Ensure all signed hashes were accounted for */
- if (covered_count != security->signed_hash_count)
- return -EPERM;
-
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 03/11] hornet: fix off-by-one bug in max used maps check
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 01/11] hornet: fix TOCTOU in signed program verification Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 02/11] hornet: invert map set check logic Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 04/11] selftests: hornet: handle cross compilation and test skipping Blaise Boscaccy
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
Sashiko correctly reported an off-by-one logic error checking against
the maximum number of used maps. Removing the index constraint allows
us to simplify the check logic.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
security/hornet/hornet_lsm.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/security/hornet/hornet_lsm.c b/security/hornet/hornet_lsm.c
index 35d9522d6bc72..eeb422db1092d 100644
--- a/security/hornet/hornet_lsm.c
+++ b/security/hornet/hornet_lsm.c
@@ -49,8 +49,7 @@ int hornet_next_map(void *context, size_t hdrlen,
{
struct hornet_parse_context *ctx = (struct hornet_parse_context *)context;
- if (++ctx->security->signed_hash_count >= MAX_USED_MAPS)
- return -EINVAL;
+ ctx->security->signed_hash_count++;
return 0;
}
@@ -63,6 +62,8 @@ int hornet_map_hash(void *context, size_t hdrlen,
if (vlen != SHA256_DIGEST_SIZE && vlen != 0)
return -EINVAL;
+ if (ctx->security->signed_hash_count >= MAX_USED_MAPS)
+ return -EINVAL;
memcpy(&ctx->security->signed_hashes[ctx->security->signed_hash_count * SHA256_DIGEST_SIZE],
value, vlen);
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 04/11] selftests: hornet: handle cross compilation and test skipping
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
` (2 preceding siblings ...)
2026-05-28 3:08 ` [PATCH 03/11] hornet: fix off-by-one bug in max used maps check Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 05/11] hornet: gen_sig: fix off-by-one check for used maps Blaise Boscaccy
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
There were a few spots in the hornet selftest makefile where some host
resources were assumed to be used. Additionally add proper skip
detection for scenarios where the autogenerated signing keys don't
exist.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
tools/testing/selftests/hornet/Makefile | 114 ++++++++++++++++++------
1 file changed, 89 insertions(+), 25 deletions(-)
diff --git a/tools/testing/selftests/hornet/Makefile b/tools/testing/selftests/hornet/Makefile
index 316364f95f28c..460adab35e238 100644
--- a/tools/testing/selftests/hornet/Makefile
+++ b/tools/testing/selftests/hornet/Makefile
@@ -5,59 +5,123 @@ include ../../../scripts/Makefile.include
CLANG ?= clang
CFLAGS := -g -O2 -Wall
+TOOLSDIR := $(abspath ../../..)
BPFTOOL ?= $(TOOLSDIR)/bpf/bpftool/bpftool
SCRIPTSDIR := $(abspath ../../../../scripts/hornet)
-TOOLSDIR := $(abspath ../../..)
LIBDIR := $(TOOLSDIR)/lib
BPFDIR := $(LIBDIR)/bpf
TOOLSINCDIR := $(TOOLSDIR)/include
APIDIR := $(TOOLSINCDIR)/uapi
CERTDIR := $(abspath ../../../../certs)
-PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config
+HOSTPKG_CONFIG ?= pkg-config
+
+SIGNING_KEY := $(CERTDIR)/signing_key.pem
+SIGNING_CERT := $(CERTDIR)/signing_key.x509
+
+VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux) \
+ $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \
+ ../../../../vmlinux \
+ /sys/kernel/btf/vmlinux \
+ /boot/vmlinux-$(shell uname -r)
+VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
+
+# The hornet selftest needs the kernel module signing key/cert (generated when
+# the kernel is built with CONFIG_MODULE_SIG=y), a bpftool binary, and a
+# vmlinux with BTF for trivial.bpf.o. If any of those are missing (cross-build
+# without artifacts, container CI, CONFIG_MODULE_SIG disabled, etc.) skip the
+# targets rather than failing the global selftests build.
+hornet_skip_reason :=
+ifeq ($(wildcard $(SIGNING_KEY)),)
+hornet_skip_reason := module signing key not found at $(SIGNING_KEY) (build the kernel with CONFIG_MODULE_SIG=y first)
+else ifeq ($(wildcard $(SIGNING_CERT)),)
+hornet_skip_reason := module signing cert not found at $(SIGNING_CERT)
+else ifeq ($(wildcard $(BPFTOOL)),)
+hornet_skip_reason := bpftool not found at $(BPFTOOL) (build it under tools/bpf/bpftool first)
+else ifeq ($(VMLINUX_BTF),)
+hornet_skip_reason := no vmlinux with BTF found; tried $(VMLINUX_BTF_PATHS) (build the kernel with CONFIG_DEBUG_INFO_BTF=y or set VMLINUX_BTF=)
+endif
+
+ifneq ($(hornet_skip_reason),)
+$(warning Skipping hornet selftests: $(hornet_skip_reason))
+TEST_GEN_PROGS :=
+TEST_GEN_FILES :=
+
+include ../lib.mk
+
+else
TEST_GEN_PROGS := loader
TEST_GEN_FILES := vmlinux.h loader.h trivial.bpf.o map.bin sig.bin insn.bin signed_loader.h
-$(TEST_GEN_PROGS): LDLIBS += -lbpf
-$(TEST_GEN_PROGS): $(TEST_GEN_FILES)
include ../lib.mk
-BPF_CFLAGS := -target bpf \
- -D__TARGET_ARCH_$(ARCH) \
- -I/usr/include/$(shell uname -m)-linux-gnu \
+define get_sys_includes
+$(shell $(1) $(2) -v -E - </dev/null 2>&1 \
+ | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \
+$(shell $(1) $(2) -dM -E - </dev/null | grep '__riscv_xlen ' | awk '{printf("-D__riscv_xlen=%d -D__BITS_PER_LONG=%d", $$3, $$3)}') \
+$(shell $(1) $(2) -dM -E - </dev/null | grep '__loongarch_grlen ' | awk '{printf("-D__BITS_PER_LONG=%d", $$3)}') \
+$(shell $(1) $(2) -dM -E - </dev/null | grep -E 'MIPS(EL|EB)|_MIPS_SZ(PTR|LONG) |_MIPS_SIM |_ABI(O32|N32|64) ' | awk '{printf("-D%s=%s ", $$2, $$3)}')
+endef
+
+ifneq ($(CROSS_COMPILE),)
+CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%))
+endif
+CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG),$(CLANG_TARGET_ARCH))
+
+IS_LITTLE_ENDIAN := $(shell $(CC) -dM -E - </dev/null | \
+ grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
+BPF_TARGET_ENDIAN := $(if $(IS_LITTLE_ENDIAN),--target=bpfel,--target=bpfeb)
+
+BPF_CFLAGS := $(BPF_TARGET_ENDIAN) \
+ -D__TARGET_ARCH_$(SRCARCH) \
+ $(CLANG_SYS_INCLUDES) \
$(KHDR_INCLUDES)
-vmlinux.h:
- $(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
+$(OUTPUT)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL)
+ $(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
-trivial.bpf.o: trivial.bpf.c vmlinux.h
- $(CLANG) $(CFLAGS) $(BPF_CFLAGS) -c $< -o $@
+$(OUTPUT)/trivial.bpf.o: trivial.bpf.c $(OUTPUT)/vmlinux.h
+ $(CLANG) $(CFLAGS) $(BPF_CFLAGS) -I$(OUTPUT) -c $< -o $@
-loader.h: trivial.bpf.o
- $(BPFTOOL) gen skeleton -S -k $(CERTDIR)/signing_key.pem -i $(CERTDIR)/signing_key.x509 \
+$(OUTPUT)/loader.h: $(OUTPUT)/trivial.bpf.o
+ $(BPFTOOL) gen skeleton -S -k $(SIGNING_KEY) -i $(SIGNING_CERT) \
-L $< name trivial > $@
-insn.bin: loader.h
+$(OUTPUT)/insn.bin: $(OUTPUT)/loader.h
$(SCRIPTSDIR)/extract-insn.sh $< > $@
-map.bin: loader.h
+$(OUTPUT)/map.bin: $(OUTPUT)/loader.h
$(SCRIPTSDIR)/extract-map.sh $< > $@
$(OUTPUT)/gen_sig: ../../../../scripts/hornet/gen_sig.c
$(call msg,GEN_SIG,,$@)
- $(Q)$(CC) $(shell $(PKG_CONFIG) --cflags libcrypto 2> /dev/null) \
+ $(Q)$(HOSTCC) $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null) \
$< -o $@ \
- $(shell $(PKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
+ $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
+
+$(OUTPUT)/sig.bin: $(OUTPUT)/insn.bin $(OUTPUT)/map.bin $(OUTPUT)/gen_sig
+ $(OUTPUT)/gen_sig --key $(SIGNING_KEY) --cert $(SIGNING_CERT) \
+ --data $(OUTPUT)/insn.bin --add $(OUTPUT)/map.bin --out $@
+
+$(OUTPUT)/signed_loader.h: $(OUTPUT)/sig.bin $(OUTPUT)/loader.h
+ $(SCRIPTSDIR)/write-sig.sh $(OUTPUT)/loader.h $(OUTPUT)/sig.bin > $@
+
+BPFOBJ := $(OUTPUT)/libbpf/libbpf.a
+
+$(OUTPUT)/libbpf:
+ $(Q)mkdir -p $@
-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 --out sig.bin
+$(BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile) \
+ $(APIDIR)/linux/bpf.h | $(OUTPUT)/libbpf
+ $(Q)$(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/libbpf/ \
+ DESTDIR=$(OUTPUT) prefix= \
+ $(BPFOBJ) install_headers
-signed_loader.h: sig.bin
- $(SCRIPTSDIR)/write-sig.sh loader.h sig.bin > $@
+$(OUTPUT)/loader: loader.c $(OUTPUT)/signed_loader.h $(BPFOBJ)
+ $(CC) $(CFLAGS) -I$(LIBDIR) -I$(APIDIR) -I$(OUTPUT) \
+ $< $(BPFOBJ) -o $@ -lelf -lz
-loader: loader.c signed_loader.h
- $(CC) $(CFLAGS) -I$(LIBDIR) -I$(APIDIR) $< -o $@ -lbpf
+EXTRA_CLEAN = $(OUTPUT)/gen_sig $(OUTPUT)/libbpf
-EXTRA_CLEAN = $(OUTPUT)/gen_sig
+endif
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 05/11] hornet: gen_sig: fix off-by-one check for used maps
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
` (3 preceding siblings ...)
2026-05-28 3:08 ` [PATCH 04/11] selftests: hornet: handle cross compilation and test skipping Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 06/11] hornet: gen_sig: fix error string allocations Blaise Boscaccy
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
A logic bug limited the maximum number of used maps to
MAX_USED_MAPS-1.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
scripts/hornet/gen_sig.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/scripts/hornet/gen_sig.c b/scripts/hornet/gen_sig.c
index b4f983ab24bcd..4e8caad22f381 100644
--- a/scripts/hornet/gen_sig.c
+++ b/scripts/hornet/gen_sig.c
@@ -317,11 +317,11 @@ int main(int argc, char **argv)
data_path = optarg;
break;
case 'A':
- hashes[hash_count].file = optarg;
- if (++hash_count >= MAX_HASHES) {
+ if (hash_count >= MAX_HASHES) {
usage(argv[0]);
return EXIT_FAILURE;
}
+ hashes[hash_count++].file = optarg;
break;
default:
usage(argv[0]);
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 06/11] hornet: gen_sig: fix error string allocations
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
` (4 preceding siblings ...)
2026-05-28 3:08 ` [PATCH 05/11] hornet: gen_sig: fix off-by-one check for used maps Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 07/11] hornet: gen_sig: check for bad allocations Blaise Boscaccy
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
The sha256 function was allocating/freeing it's own error strings,
which could case further errors to only return their error number.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
scripts/hornet/gen_sig.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/scripts/hornet/gen_sig.c b/scripts/hornet/gen_sig.c
index 4e8caad22f381..647bc3a257dd0 100644
--- a/scripts/hornet/gen_sig.c
+++ b/scripts/hornet/gen_sig.c
@@ -200,8 +200,6 @@ static int sha256(const char *path, unsigned char out[SHA256_LEN], unsigned int
return -2;
}
- ERR_load_crypto_strings();
-
rc = -3;
ctx = EVP_MD_CTX_new();
if (!ctx) {
@@ -247,7 +245,6 @@ static int sha256(const char *path, unsigned char out[SHA256_LEN], unsigned int
done:
EVP_MD_CTX_free(ctx);
fclose(f);
- ERR_free_strings();
return rc;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 07/11] hornet: gen_sig: check for bad allocations
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
` (5 preceding siblings ...)
2026-05-28 3:08 ` [PATCH 06/11] hornet: gen_sig: fix error string allocations Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 08/11] hornet: gen_sig: fix missing command line switches Blaise Boscaccy
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
There were a few sites where gen_sig failed to check for bad return
values after allocations. Error out appropriately as needed.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
scripts/hornet/gen_sig.c | 40 ++++++++++++++++++++++++++++++++++------
1 file changed, 34 insertions(+), 6 deletions(-)
diff --git a/scripts/hornet/gen_sig.c b/scripts/hornet/gen_sig.c
index 647bc3a257dd0..fb9ae1934206a 100644
--- a/scripts/hornet/gen_sig.c
+++ b/scripts/hornet/gen_sig.c
@@ -248,13 +248,25 @@ static int sha256(const char *path, unsigned char out[SHA256_LEN], unsigned int
return rc;
}
-static void add_hash(MAP_SET *set, unsigned char *buffer, int buffer_len)
+static int add_hash(MAP_SET *set, unsigned char *buffer, int buffer_len)
{
- HORNET_MAP *map = NULL;
+ HORNET_MAP *map;
map = HORNET_MAP_new();
- ASN1_OCTET_STRING_set(map->hash, buffer, buffer_len);
- sk_HORNET_MAP_push(set->maps, map);
+ if (!map)
+ return -1;
+
+ if (ASN1_OCTET_STRING_set(map->hash, buffer, buffer_len) != 1) {
+ HORNET_MAP_free(map);
+ return -1;
+ }
+
+ if (sk_HORNET_MAP_push(set->maps, map) <= 0) {
+ HORNET_MAP_free(map);
+ return -1;
+ }
+
+ return 0;
}
int main(int argc, char **argv)
@@ -353,13 +365,18 @@ int main(int argc, char **argv)
ERR(!si, "add signer failed");
set = MAP_SET_new();
+ ERR(!set, "alloc MAP_SET failed");
set->maps = sk_HORNET_MAP_new_null();
+ ERR(!set->maps, "alloc HORNET_MAP stack failed");
for (i = 0; i < hash_count; i++) {
if (sha256(hashes[i].file, hash_buffer, &hash_len) != 0) {
DIE("failed to hash input");
}
- add_hash(set, hash_buffer, hash_len);
+ if (add_hash(set, hash_buffer, hash_len) != 0) {
+ ERR_print_errors_fp(stderr);
+ DIE("failed to add hash to map set");
+ }
}
oid = OBJ_txt2obj("2.25.316487325684022475439036912669789383960", 1);
@@ -380,7 +397,18 @@ int main(int argc, char **argv)
b_out = bio_open_wr(out_path);
ERR(!b_out, "opening output path failed");
- i2d_CMS_bio_stream(b_out, cms_out, NULL, 0);
+ err = i2d_CMS_bio_stream(b_out, cms_out, NULL, 0);
+ ERR(!err, "writing CMS signature to %s failed", out_path);
+
+ /*
+ * File BIOs wrap stdio, which buffers writes; small payloads will
+ * report success from BIO_write even when the underlying file is
+ * full or otherwise un-writable. Force a flush and check it before
+ * the BIO is freed, otherwise gen_sig could exit successfully with
+ * a truncated or empty signature file (e.g. ENOSPC on /dev/full).
+ */
+ err = BIO_flush(b_out);
+ ERR(err <= 0, "flushing %s failed", out_path);
BIO_free(data_in);
BIO_free(b_out);
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 08/11] hornet: gen_sig: fix missing command line switches
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
` (6 preceding siblings ...)
2026-05-28 3:08 ` [PATCH 07/11] hornet: gen_sig: check for bad allocations Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 09/11] hornet: scripts: set a non-zero error code for usage Blaise Boscaccy
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
D was missing from the getopt list. Additionally, we were missing the
help option handler.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
scripts/hornet/gen_sig.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/scripts/hornet/gen_sig.c b/scripts/hornet/gen_sig.c
index fb9ae1934206a..19ae9af006853 100644
--- a/scripts/hornet/gen_sig.c
+++ b/scripts/hornet/gen_sig.c
@@ -295,7 +295,7 @@ int main(int argc, char **argv)
int i;
int opt;
- const char *short_opts = "C:K:P:O:A:Sh";
+ const char *short_opts = "C:K:P:O:D:A:Sh";
static const struct option long_opts[] = {
{"cert", required_argument, 0, 'C'},
@@ -332,6 +332,9 @@ int main(int argc, char **argv)
}
hashes[hash_count++].file = optarg;
break;
+ case 'h':
+ usage(argv[0]);
+ return EXIT_SUCCESS;
default:
usage(argv[0]);
return EXIT_FAILURE;
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 09/11] hornet: scripts: set a non-zero error code for usage
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
` (7 preceding siblings ...)
2026-05-28 3:08 ` [PATCH 08/11] hornet: gen_sig: fix missing command line switches Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 10/11] hornet: scripts: harden scripts to handle trailing whitespace Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 11/11] hornet: scripts: Improve argument handling and error messages Blaise Boscaccy
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
It was possible that build scripts may continue if arguments were
missing.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
| 10 +++++-----
| 10 +++++-----
| 10 +++++-----
scripts/hornet/write-sig.sh | 10 +++++-----
4 files changed, 20 insertions(+), 20 deletions(-)
--git a/scripts/hornet/extract-insn.sh b/scripts/hornet/extract-insn.sh
index 52338f057ff6b..e136932275aa5 100755
--- a/scripts/hornet/extract-insn.sh
+++ b/scripts/hornet/extract-insn.sh
@@ -8,11 +8,11 @@
# 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
+ echo "Sample script for extracting instructions" >&2
+ echo "autogenerated eBPF lskel headers" >&2
+ echo "" >&2
+ echo "USAGE: header_file" >&2
+ exit 1
}
ARGC=$#
--git a/scripts/hornet/extract-map.sh b/scripts/hornet/extract-map.sh
index c309f505c6238..058ac1b32d743 100755
--- a/scripts/hornet/extract-map.sh
+++ b/scripts/hornet/extract-map.sh
@@ -8,11 +8,11 @@
# 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
+ echo "Sample script for extracting instructions" >&2
+ echo "autogenerated eBPF lskel headers" >&2
+ echo "" >&2
+ echo "USAGE: header_file" >&2
+ exit 1
}
ARGC=$#
--git a/scripts/hornet/extract-skel.sh b/scripts/hornet/extract-skel.sh
index 6550a86b89917..abc435e2bcd4e 100755
--- a/scripts/hornet/extract-skel.sh
+++ b/scripts/hornet/extract-skel.sh
@@ -8,11 +8,11 @@
# 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
+ echo "Sample script for extracting instructions and map data out of" >&2
+ echo "autogenerated eBPF lskel headers" >&2
+ echo "" >&2
+ echo "USAGE: header_file field" >&2
+ exit 1
}
ARGC=$#
diff --git a/scripts/hornet/write-sig.sh b/scripts/hornet/write-sig.sh
index 7eaabe3bab9aa..ad2b65761c282 100755
--- a/scripts/hornet/write-sig.sh
+++ b/scripts/hornet/write-sig.sh
@@ -8,11 +8,11 @@
# 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
+ echo "Sample for rewriting an autogenerated eBPF lskel headers" >&2
+ echo "with a new signature" >&2
+ echo "" >&2
+ echo "USAGE: header_file sig" >&2
+ exit 1
}
ARGC=$#
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 10/11] hornet: scripts: harden scripts to handle trailing whitespace
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
` (8 preceding siblings ...)
2026-05-28 3:08 ` [PATCH 09/11] hornet: scripts: set a non-zero error code for usage Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 11/11] hornet: scripts: Improve argument handling and error messages Blaise Boscaccy
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
Trailing whitespace after the semicolon in the header files may have
caused the binary extracted payload to be corrupted due to a missing
anchor.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
| 2 +-
| 2 +-
| 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
--git a/scripts/hornet/extract-insn.sh b/scripts/hornet/extract-insn.sh
index e136932275aa5..3e7bed049acb6 100755
--- a/scripts/hornet/extract-insn.sh
+++ b/scripts/hornet/extract-insn.sh
@@ -23,5 +23,5 @@ if [ $ARGC -ne $EXPECTED_ARGS ] ; then
usage
else
printf $(gcc -E $1 | grep "opts_insn" | \
- awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+ awk -F"=" '{print $2}' | sed 's/[[:space:];]*$//' | sed 's/\"//g')
fi
--git a/scripts/hornet/extract-map.sh b/scripts/hornet/extract-map.sh
index 058ac1b32d743..1d92ebe1a04b5 100755
--- a/scripts/hornet/extract-map.sh
+++ b/scripts/hornet/extract-map.sh
@@ -23,5 +23,5 @@ if [ $ARGC -ne $EXPECTED_ARGS ] ; then
usage
else
printf $(gcc -E $1 | grep "opts_data" | \
- awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+ awk -F"=" '{print $2}' | sed 's/[[:space:];]*$//' | sed 's/\"//g')
fi
--git a/scripts/hornet/extract-skel.sh b/scripts/hornet/extract-skel.sh
index abc435e2bcd4e..e115f4b7fdf74 100755
--- a/scripts/hornet/extract-skel.sh
+++ b/scripts/hornet/extract-skel.sh
@@ -23,5 +23,5 @@ 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')
+ awk -F"=" '{print $2}' | sed 's/[[:space:];]*$//' | sed 's/\"//g')
fi
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 11/11] hornet: scripts: Improve argument handling and error messages
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
` (9 preceding siblings ...)
2026-05-28 3:08 ` [PATCH 10/11] hornet: scripts: harden scripts to handle trailing whitespace Blaise Boscaccy
@ 2026-05-28 3:08 ` Blaise Boscaccy
10 siblings, 0 replies; 12+ messages in thread
From: Blaise Boscaccy @ 2026-05-28 3:08 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
Blaise Boscaccy, linux-security-module
Spaces in file names may have caused invocation errors. Additionally
add helpful error messages if we are unable to extract the requested
payload.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
| 14 +++++++++++---
| 15 ++++++++++++---
| 25 ++++++++++++++++++++++---
3 files changed, 45 insertions(+), 9 deletions(-)
--git a/scripts/hornet/extract-insn.sh b/scripts/hornet/extract-insn.sh
index 3e7bed049acb6..e0c7a4ae967f3 100755
--- a/scripts/hornet/extract-insn.sh
+++ b/scripts/hornet/extract-insn.sh
@@ -21,7 +21,15 @@ EXPECTED_ARGS=1
if [ $ARGC -ne $EXPECTED_ARGS ] ; then
usage
-else
- printf $(gcc -E $1 | grep "opts_insn" | \
- awk -F"=" '{print $2}' | sed 's/[[:space:];]*$//' | sed 's/\"//g')
fi
+
+HEADER="$1"
+STR=$(gcc -E "$HEADER" | \
+ sed -n 's/.*char opts_insn[[:space:]]*\[\][^=]*=[[:space:]]*"\(.*\)"[[:space:]]*;.*/\1/p')
+
+if [ -z "$STR" ]; then
+ echo "$(basename "$0"): no opts_insn[] declaration found in $HEADER" >&2
+ exit 1
+fi
+
+printf '%b' "$STR"
--git a/scripts/hornet/extract-map.sh b/scripts/hornet/extract-map.sh
index 1d92ebe1a04b5..2d68bb473d889 100755
--- a/scripts/hornet/extract-map.sh
+++ b/scripts/hornet/extract-map.sh
@@ -21,7 +21,16 @@ EXPECTED_ARGS=1
if [ $ARGC -ne $EXPECTED_ARGS ] ; then
usage
-else
- printf $(gcc -E $1 | grep "opts_data" | \
- awk -F"=" '{print $2}' | sed 's/[[:space:];]*$//' | sed 's/\"//g')
fi
+
+# See extract-insn.sh for the rationale behind the sed/printf '%b' shape.
+HEADER="$1"
+STR=$(gcc -E "$HEADER" | \
+ sed -n 's/.*char opts_data[[:space:]]*\[\][^=]*=[[:space:]]*"\(.*\)"[[:space:]]*;.*/\1/p')
+
+if [ -z "$STR" ]; then
+ echo "$(basename "$0"): no opts_data[] declaration found in $HEADER" >&2
+ exit 1
+fi
+
+printf '%b' "$STR"
--git a/scripts/hornet/extract-skel.sh b/scripts/hornet/extract-skel.sh
index e115f4b7fdf74..c980f245f4a39 100755
--- a/scripts/hornet/extract-skel.sh
+++ b/scripts/hornet/extract-skel.sh
@@ -21,7 +21,26 @@ 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/[[:space:];]*$//' | sed 's/\"//g')
fi
+
+# See extract-insn.sh for the rationale behind the sed/printf '%b' shape.
+HEADER="$1"
+FIELD="$2"
+
+# Reject anything that could escape into the sed pattern below.
+case "$FIELD" in
+ *[!A-Za-z0-9_]*|"")
+ echo "$(basename "$0"): invalid field name '$FIELD'" >&2
+ exit 1
+ ;;
+esac
+
+STR=$(gcc -E "$HEADER" | \
+ sed -n "s/.*char opts_${FIELD}[[:space:]]*\[\][^=]*=[[:space:]]*\"\(.*\)\"[[:space:]]*;.*/\1/p")
+
+if [ -z "$STR" ]; then
+ echo "$(basename "$0"): no opts_${FIELD}[] declaration found in $HEADER" >&2
+ exit 1
+fi
+
+printf '%b' "$STR"
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-05-28 3:10 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-28 3:08 [PATCH 00/11] hornet: security, tooling and selftest fixes Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 01/11] hornet: fix TOCTOU in signed program verification Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 02/11] hornet: invert map set check logic Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 03/11] hornet: fix off-by-one bug in max used maps check Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 04/11] selftests: hornet: handle cross compilation and test skipping Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 05/11] hornet: gen_sig: fix off-by-one check for used maps Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 06/11] hornet: gen_sig: fix error string allocations Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 07/11] hornet: gen_sig: check for bad allocations Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 08/11] hornet: gen_sig: fix missing command line switches Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 09/11] hornet: scripts: set a non-zero error code for usage Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 10/11] hornet: scripts: harden scripts to handle trailing whitespace Blaise Boscaccy
2026-05-28 3:08 ` [PATCH 11/11] hornet: scripts: Improve argument handling and error messages Blaise Boscaccy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox