]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
libbpf: Reject non-exclusive metadata maps in the signed loader
authorKP Singh <kpsingh@kernel.org>
Mon, 1 Jun 2026 15:02:44 +0000 (17:02 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 2 Jun 2026 01:36:40 +0000 (18:36 -0700)
The loader verifies map->sha against the metadata hash in its
instructions. map->sha is calculated when BPF_OBJ_GET_INFO_BY_FD is
called on the frozen map.

While the map is frozen, the /signed loader/ must also ensure the map
is exclusive, as, without exclusivity (which a hostile host could just
omit when loading the loader), another BPF program with map access can
mutate the contents afterwards, so the check passes on stale data.

With the extra check as part of the signed loader, it now refuses to
move on with map->sha validation if the host set it up wrongly.

Fixes: fb2b0e290147 ("libbpf: Update light skeleton for signing")
Signed-off-by: KP Singh <kpsingh@kernel.org>
Co-developed-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/r/20260601150248.394863-4-daniel@iogearbox.net
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/bpf.h
kernel/bpf/syscall.c
tools/lib/bpf/gen_loader.c

index c0510d2236854edc6655b010941f701de96d7226..8599b451dd7a29c68d4a5fc60669364b61a1d61a 100644 (file)
@@ -296,6 +296,7 @@ struct bpf_map_owner {
 
 struct bpf_map {
        u8 sha[SHA256_DIGEST_SIZE];
+       u32 excl;
        const struct bpf_map_ops *ops;
        struct bpf_map *inner_map_meta;
 #ifdef CONFIG_SECURITY
index a27fa2b9b40562b3e402c8fa0b0c20585d86a1ee..625a4366fe6d7be7078344b4818c75ad371b9c6d 100644 (file)
@@ -1588,6 +1588,13 @@ static int map_create_alloc(union bpf_attr *attr, bpfptr_t uattr, struct bpf_ver
                        err = -EFAULT;
                        goto free_map;
                }
+
+               /* See libbpf: emit_signature_match() */
+               BUILD_BUG_ON(offsetof(struct bpf_map, excl) != SHA256_DIGEST_SIZE);
+               BUILD_BUG_ON(!__same_type(map->excl, u32));
+               BUILD_BUG_ON(offsetof(struct bpf_map, sha)  != 0);
+               BUILD_BUG_ON(!__same_type(map->sha, u8[SHA256_DIGEST_SIZE]));
+               map->excl = 1;
        } else if (attr->excl_prog_hash_size) {
                bpf_log(log, "Invalid excl_prog_hash_size.\n");
                err = -EINVAL;
index 3702c5944bc02f91ee7374bee435f65c5d205f78..66a02039da8ce65fab70b2887ab703c99ed27811 100644 (file)
@@ -586,6 +586,23 @@ static void emit_signature_match(struct bpf_gen *gen)
        __s64 off;
        int i;
 
+       /*
+        * Reject if the metadata map is not exclusive. Without exclusivity
+        * the cached map->sha[] verified above can be stale: another BPF
+        * program with map access could have mutated the contents between
+        * BPF_OBJ_GET_INFO_BY_FD and loader execution.
+        */
+       emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
+                                        0, 0, 0, 0));
+       emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, SHA256_DIGEST_LENGTH));
+       off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2;
+       if (is_simm16(off)) {
+               emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL));
+               emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, off));
+       } else {
+               gen->error = -ERANGE;
+       }
+
        for (i = 0; i < SHA256_DWORD_SIZE; i++) {
                emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
                                                 0, 0, 0, 0));