[PATCH bpf-next 04/13] bpf: add bpf_loader_verify_metadata kfunc
KP Singh
kpsingh at kernel.org
Fri May 22 02:32:24 UTC 2026
A signed loader reaches sig.verdict = BPF_SIG_OK after PKCS#7
verification, but the metadata map content the loader writes has not
been checked against its expected SHA256. The map carries that SHA256
in the signed instruction stream, spilled to the loader's stack at
runtime as four ld_imm64 immediates.
Assert the map is exclusive, compare its frozen content hash against
the spilled SHA256, and promote sig.verdict to
BPF_SIG_METADATA_VERIFIED. The verifier injects the calling
prog->aux as an implicit argument via KF_IMPLICIT_ARGS so the kfunc
can read the prog digest and signature verdict directly.
Drop skel_obj_get_info_by_fd from bpf_load_and_run since the kfunc
computes the map hash itself via map_get_hash; the prior
BPF_OBJ_GET_INFO_BY_FD call to populate map->sha is not
needed.
Signed-off-by: KP Singh <kpsingh at kernel.org>
---
kernel/bpf/helpers.c | 57 +++++++++++++++++++++++++++++++++++
tools/bpf/bpftool/sign.c | 17 ++++++++++-
tools/lib/bpf/skel_internal.h | 25 ---------------
3 files changed, 73 insertions(+), 26 deletions(-)
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index b5314c9fed3c..9afa71fbcac3 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4257,6 +4257,53 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p,
return -EOPNOTSUPP;
#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
}
+
+/**
+ * bpf_loader_verify_metadata - perform the signed loader's metadata-map check
+ * in a single kernel-side step.
+ *
+ * Asserts the metadata map is exclusive, compares its frozen content hash
+ * against the expected SHA256 carried in the loader's signed instruction
+ * stream, and promotes sig.verdict from BPF_SIG_OK to
+ * BPF_SIG_METADATA_VERIFIED.
+ *
+ * @map: metadata map bound to this loader via excl_prog_hash at sign time
+ * @hash: pointer to the expected SHA256, spilled to the loader's BPF stack
+ * from signed ld_imm64 immediates
+ * @hash__sz: byte length of @hash (must equal SHA256_DIGEST_SIZE)
+ * @aux__ign: verifier-supplied prog aux; BPF programs do not set it
+ *
+ * Return: 0 on success, -EPERM if not called from a signed loader,
+ * -EINVAL if the map is not exclusive or the hash buffer is the wrong size,
+ * -EBADMSG if the hash does not match.
+ */
+__bpf_kfunc int bpf_loader_verify_metadata(struct bpf_map *map,
+ const u64 *hash, u32 hash__sz,
+ struct bpf_prog_aux *aux__ign)
+{
+ u8 sha[SHA256_DIGEST_SIZE];
+ int err;
+
+ if (!aux__ign || aux__ign->sig.verdict != BPF_SIG_OK)
+ return -EPERM;
+ if (!map->excl_prog_sha || hash__sz != SHA256_DIGEST_SIZE)
+ return -EINVAL;
+ if (memcmp(map->excl_prog_sha, aux__ign->prog->digest, SHA256_DIGEST_SIZE))
+ return -EPERM;
+ if (!READ_ONCE(map->frozen))
+ return -EPERM;
+ if (!map->ops->map_get_hash)
+ return -EINVAL;
+ err = map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, sha);
+ if (err)
+ return err;
+ if (memcmp(sha, hash, SHA256_DIGEST_SIZE))
+ return -EBADMSG;
+
+ aux__ign->sig.verdict = BPF_SIG_METADATA_VERIFIED;
+ return 0;
+}
+
#endif /* CONFIG_KEYS */
typedef int (*bpf_task_work_callback_t)(struct bpf_map *map, void *key, void *value);
@@ -4764,6 +4811,15 @@ static const struct btf_kfunc_id_set generic_kfunc_set = {
.set = &generic_btf_ids,
};
+BTF_KFUNCS_START(syscall_btf_ids)
+BTF_ID_FLAGS(func, bpf_loader_verify_metadata, KF_SLEEPABLE | KF_IMPLICIT_ARGS)
+BTF_KFUNCS_END(syscall_btf_ids)
+
+static const struct btf_kfunc_id_set syscall_kfunc_set = {
+ .owner = THIS_MODULE,
+ .set = &syscall_btf_ids,
+};
+
BTF_ID_LIST(generic_dtor_ids)
BTF_ID(struct, task_struct)
@@ -4893,6 +4949,7 @@ static int __init kfunc_init(void)
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &generic_kfunc_set);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &generic_kfunc_set);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_CGROUP_SKB, &generic_kfunc_set);
+ ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &syscall_kfunc_set);
ret = ret ?: register_btf_id_dtor_kfuncs(generic_dtors,
ARRAY_SIZE(generic_dtors),
THIS_MODULE);
diff --git a/tools/bpf/bpftool/sign.c b/tools/bpf/bpftool/sign.c
index f9b742f4bb10..13f256546cf0 100644
--- a/tools/bpf/bpftool/sign.c
+++ b/tools/bpf/bpftool/sign.c
@@ -134,10 +134,24 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
EVP_PKEY *private_key = NULL;
CMS_ContentInfo *cms = NULL;
long actual_sig_len = 0;
+ void *signed_buf = NULL;
+ size_t signed_sz;
X509 *x509 = NULL;
int err = 0;
- bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
+ signed_sz = opts->insns_sz + opts->btf_sz;
+ if (opts->btf_sz) {
+ signed_buf = malloc(signed_sz);
+ if (!signed_buf) {
+ err = -ENOMEM;
+ goto cleanup;
+ }
+ memcpy(signed_buf, opts->insns, opts->insns_sz);
+ memcpy(signed_buf + opts->insns_sz, opts->btf, opts->btf_sz);
+ bd_in = BIO_new_mem_buf(signed_buf, signed_sz);
+ } else {
+ bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
+ }
if (!bd_in) {
err = -ENOMEM;
goto cleanup;
@@ -212,6 +226,7 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
X509_free(x509);
EVP_PKEY_free(private_key);
BIO_free(bd_in);
+ free(signed_buf);
DISPLAY_OSSL_ERR(err < 0);
return err;
}
diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h
index 0b6b1ecedd45..d194f4e23d12 100644
--- a/tools/lib/bpf/skel_internal.h
+++ b/tools/lib/bpf/skel_internal.h
@@ -335,25 +335,6 @@ static inline int skel_link_create(int prog_fd, int target_fd,
return skel_sys_bpf(BPF_LINK_CREATE, &attr, attr_sz);
}
-static inline int skel_obj_get_info_by_fd(int fd)
-{
- const size_t attr_sz = offsetofend(union bpf_attr, info);
- __u8 sha[SHA256_DIGEST_LENGTH];
- struct bpf_map_info info;
- __u32 info_len = sizeof(info);
- union bpf_attr attr;
-
- memset(&info, 0, sizeof(info));
- info.hash = (long) &sha;
- info.hash_size = SHA256_DIGEST_LENGTH;
-
- memset(&attr, 0, attr_sz);
- attr.info.bpf_fd = fd;
- attr.info.info = (long) &info;
- attr.info.info_len = info_len;
- return skel_sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, attr_sz);
-}
-
static inline int skel_map_freeze(int fd)
{
const size_t attr_sz = offsetofend(union bpf_attr, map_fd);
@@ -400,12 +381,6 @@ static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
set_err;
goto out;
}
- err = skel_obj_get_info_by_fd(map_fd);
- if (err < 0) {
- opts->errstr = "failed to fetch obj info";
- set_err;
- goto out;
- }
#endif
#ifndef __KERNEL__
--
2.53.0
More information about the Linux-security-module-archive
mailing list