From: sashiko-bot@kernel.org
To: "Blaise Boscaccy" <bboscaccy@linux.microsoft.com>
Cc: bpf@vger.kernel.org
Subject: Re: [PATCH v5 06/10] security: Hornet LSM
Date: Tue, 21 Apr 2026 04:29:32 +0000 [thread overview]
Message-ID: <20260421042933.4805DC2BCB3@smtp.kernel.org> (raw)
In-Reply-To: <20260420212653.438685-7-bboscaccy@linux.microsoft.com>
Sashiko AI review found 10 potential issue(s):
- [High] Invalid Pointer Dereference due to missing IS_ERR check on lookup_user_key().
- [High] Resource Leak due to missing key_put() on user keyring reference.
- [High] NULL Pointer Dereference when verifying map types that do not implement map_get_hash.
- [High] Security Bypass due to uninitialized verdict variable on copy_from_bpfptr() failure.
- [High] Logic Flaw allowing verification of arbitrary file descriptors on copy_from_bpfptr_offset() failure.
- [High] State Corruption and Data Race in globally shared map_security object.
- [High] Logic Error bypassing runtime map verification due to incorrect loop index.
- [High] False Positives and Logic Error due to matching unverified maps to signature index 0.
- [Medium] Out-of-bounds Read in hornet_map_index() due to missing length check.
- [Medium] Off-by-One Error limits tracked maps to 63 instead of the intended 64.
--
commit 59054f5223757f6e86fcfcc7b0a3aa6b97918bab
Author: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
security: Hornet LSM
This commit adds the Hornet Linux Security Module. It verifies PKCS#7
signatures attached to eBPF programs and checks SHA-256 hashes of BPF maps
at load and run time.
> diff --git a/security/hornet/hornet_lsm.c b/security/hornet/hornet_lsm.c
> new file mode 100644
> index 0000000000000..f7d62fe6229c9
> --- /dev/null
> +++ b/security/hornet/hornet_lsm.c
[ ... ]
> +static int hornet_verify_hashes(struct hornet_maps *maps,
> + struct hornet_parse_context *ctx,
> + struct bpf_prog *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)
Does this correctly handle copy_from_bpfptr_offset() failures?
The copy_from_bpfptr_offset() function returns the positive number of
uncopied bytes on failure, so err < 0 will not catch the error.
Since the destination buffer is zero-filled on failure, could this allow
inspecting file descriptor 0 instead of the intended map?
> + 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 (!map->frozen)
> + return LSM_INT_VERDICT_FAULT;
> +
> + map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash);
Are we sure map_get_hash is implemented for all map types being checked?
Many standard BPF maps (such as hash maps) do not implement this callback.
Could calling it unconditionally trigger a NULL pointer dereference if an
unsupported map type is encountered?
> +
> + err = memcmp(hash, &ctx->hashes[i * SHA256_DIGEST_SIZE],
> + SHA256_DIGEST_SIZE);
> + if (err)
> + return LSM_INT_VERDICT_UNEXPECTED;
> +
> + security->checked[i] = true;
> + memcpy(&security->hashes[i * SHA256_DIGEST_SIZE], hash, SHA256_DIGEST_SIZE);
> + map_security = hornet_bpf_map_security(map);
> + map_security->checked = true;
> + map_security->index = i;
Is it safe to write the program-specific signature index into the globally
shared map_security object?
If multiple programs are concurrently loaded and attach the same map at
different signature indices, this could result in a data race. When the
first program runs, hornet_verify_map() expects the original index, which
might cause legitimate executions to fail.
> + }
> + 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)
Should this condition be > instead of >=?
Since MAX_USED_MAPS is 64, returning an error when the count reaches 64
artificially restricts the maximum number of tracked maps to 63.
> + 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;
Is it possible for vlen to be 0 here?
If a crafted ASN.1 signature provides a zero-length integer, this might
result in an out-of-bounds read when accessing *(u8 *)value.
> + return 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)
> +{
[ ... ]
> + err = copy_from_bpfptr(sig, usig, attr->signature_size);
> + if (err != 0)
> + goto cleanup_sig;
Does this properly handle a copy_from_bpfptr() failure?
The copy_from_bpfptr() function returns the positive number of uncopied
bytes on failure, which is then propagated and returned by
hornet_check_program().
In hornet_bpf_prog_load_integrity(), the failure check is if (result < 0).
That check will pass on a positive return value, which could lead to
security_bpf_prog_load_post_integrity() being called with an uninitialized
verdict variable.
> +
> + msg = pkcs7_parse_message(sig, attr->signature_size);
> + if (IS_ERR(msg)) {
> + *verdict = LSM_INT_VERDICT_BADSIG;
> + err = 0;
> + goto cleanup_sig;
> + }
> +
> + if (system_keyring_id_check(attr->keyring_id) == 0)
> + key = (struct key*)(unsigned long)attr->keyring_id;
> + else
> + key = key_ref_to_ptr(lookup_user_key(attr->keyring_id, 0, KEY_DEFER_PERM_CHECK));
> +
> + if (verify_pkcs7_message_sig(prog->insnsi, prog->len * sizeof(struct bpf_insn), msg,
> + key,
Can lookup_user_key() return an error pointer here?
If lookup_user_key() fails (for instance, due to an invalid keyring ID),
key_ref_to_ptr() masks the reference but does not check for IS_ERR(). This
could lead to an invalid pointer dereference when key is evaluated later.
Additionally, lookup_user_key() increments the reference count of the
returned key. Is there a missing key_put() to release this reference? It
appears the reference is never dropped on either the success or error
paths, which could cause a memory leak.
[ ... ]
> +static int hornet_verify_map(struct bpf_prog *prog, int index)
> +{
> + unsigned char hash[SHA256_DIGEST_SIZE];
> + int i;
> + struct bpf_map *map;
> + struct hornet_prog_security_struct *security = hornet_bpf_prog_security(prog);
> + struct hornet_map_security_struct *map_security;
> +
> + if (!security->checked[index])
> + return 0;
> +
> + for (i = 0; i < prog->aux->used_map_cnt; i++) {
> + map = prog->aux->used_maps[i];
> + map_security = hornet_bpf_map_security(map);
> + if (map_security->index != index)
If a map has not been verified, its map_security structure is
zero-initialized, making map_security->index evaluate to 0.
When verifying signature index 0, could this wrongly match an unverified
map and attempt to compute its hash, leading to false positives?
> + continue;
> +
> + if (!map->frozen)
> + return -EPERM;
> +
> + map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash);
Similar to hornet_verify_hashes(), does this safely handle map types that
do not implement map_get_hash?
Calling it unconditionally on unsupported maps might trigger a NULL pointer
dereference.
> + if (memcmp(hash, &security->hashes[index * SHA256_DIGEST_SIZE],
> + SHA256_DIGEST_SIZE) != 0)
> + return -EPERM;
> + else
> + return 0;
> + }
> + return -EINVAL;
> +}
> +
> +static int hornet_check_prog_maps(u32 ufd)
> +{
[ ... ]
> + for (i = 0; i < prog->aux->used_map_cnt; i++) {
> + result = hornet_verify_map(prog, i);
Is i the correct value to pass as the signature index here?
The loop counter i iterates up to used_map_cnt. If a program uses maps
assigned to higher signature indices (e.g., indices 5 and 6) but only uses
2 maps overall, it appears this loop will only check indices 0 and 1,
skipping verification for the expected indices.
> + if (result)
> + goto out;
> + }
> +out:
> + mutex_unlock(&prog->aux->used_maps_mutex);
> + return result;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260420212653.438685-1-bboscaccy@linux.microsoft.com?part=6
next prev parent reply other threads:[~2026-04-21 4:29 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-20 21:26 [PATCH v5 00/10] Reintroduce Hornet LSM Blaise Boscaccy
2026-04-20 21:26 ` [PATCH v5 01/10] crypto: pkcs7: add flag for validated trust on a signed info block Blaise Boscaccy
2026-04-21 1:17 ` sashiko-bot
2026-04-20 21:26 ` [PATCH v5 02/10] crypto: pkcs7: add ability to extract signed attributes by OID Blaise Boscaccy
2026-04-21 1:49 ` sashiko-bot
2026-04-20 21:26 ` [PATCH v5 03/10] crypto: pkcs7: add tests for pkcs7_get_authattr Blaise Boscaccy
2026-04-21 2:06 ` sashiko-bot
2026-04-20 21:26 ` [PATCH v5 04/10] lsm: framework for BPF integrity verification Blaise Boscaccy
2026-04-20 21:26 ` [PATCH v5 05/10] lsm: security: Add additional enum values for bpf integrity checks Blaise Boscaccy
2026-04-20 21:26 ` [PATCH v5 06/10] security: Hornet LSM Blaise Boscaccy
2026-04-21 0:08 ` Fan Wu
2026-04-21 4:29 ` sashiko-bot [this message]
2026-04-20 21:26 ` [PATCH v5 07/10] hornet: Introduce gen_sig Blaise Boscaccy
2026-04-21 0:18 ` Fan Wu
2026-04-21 3:03 ` sashiko-bot
2026-04-20 21:26 ` [PATCH v5 08/10] hornet: Add a light skeleton data extractor scripts Blaise Boscaccy
2026-04-21 3:06 ` sashiko-bot
2026-04-20 21:26 ` [PATCH v5 09/10] selftests/hornet: Add a selftest for the Hornet LSM Blaise Boscaccy
2026-04-21 3:04 ` sashiko-bot
2026-04-20 21:26 ` [PATCH v5 10/10] ipe: Add BPF program load policy enforcement via Hornet integration Blaise Boscaccy
2026-04-21 0:27 ` Fan Wu
2026-04-21 3:23 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260421042933.4805DC2BCB3@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=bboscaccy@linux.microsoft.com \
--cc=bpf@vger.kernel.org \
--cc=sashiko@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox