public inbox for bpf@vger.kernel.org
 help / color / mirror / Atom feed
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

  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