]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
libbpf: Skip initial_value override on signed loaders
authorDaniel Borkmann <daniel@iogearbox.net>
Mon, 1 Jun 2026 15:02:45 +0000 (17:02 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 2 Jun 2026 01:36:40 +0000 (18:36 -0700)
bpf_gen__map_update_elem() emits code that, when the host-supplied
loader ctx provides a non-NULL map_desc[idx].initial_value, overwrites
the blob value with bytes read from the host (bpf_copy_from_user /
bpf_probe_read_kernel) before the BPF_MAP_UPDATE_ELEM that populates
the program's .data/.rodata/.bss maps.

This override runs after emit_signature_match() has validated map->sha[],
and initial_value is part of neither the signed loader instructions nor
the hashed data blob. For a signed loader this lets an untrusted host
substitute global-variable contents into a program whose code carries
a valid signature, thus weakening what the signature attests to.

The blob already contains the signer-provided value (added via add_data()
and covered by the embedded, signed hash), so simply skip emitting the
override for signed loaders (gen_hash). Runtime initialization stays
available for the unsigned light-skeleton path as before. The jump
offsets within the override block are internal to it, so guarding the
whole block leaves them unchanged.

Fixes: ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/r/20260601150248.394863-5-daniel@iogearbox.net
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/lib/bpf/gen_loader.c

index 66a02039da8ce65fab70b2887ab703c99ed27811..a5d9c7a5261b07cb22e15ef89a163f43587cef75 100644 (file)
@@ -1187,27 +1187,36 @@ void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue,
        value = add_data(gen, pvalue, value_size);
        key = add_data(gen, &zero, sizeof(zero));
 
-       /* if (map_desc[map_idx].initial_value) {
+       /*
+        * if (map_desc[map_idx].initial_value) {
         *    if (ctx->flags & BPF_SKEL_KERNEL)
         *        bpf_probe_read_kernel(value, value_size, initial_value);
         *    else
         *        bpf_copy_from_user(value, value_size, initial_value);
         * }
+        *
+        * The runtime initial_value comes from the host-supplied loader
+        * ctx and would overwrite the blob value after emit_signature_match()
+        * has already validated map->sha[]. For a signed loader (gen_hash)
+        * the attested blob value must be authoritative, so skip the override
+        * and leave the hashed value in place.
         */
-       emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6,
-                             sizeof(struct bpf_loader_ctx) +
-                             sizeof(struct bpf_map_desc) * map_idx +
-                             offsetof(struct bpf_map_desc, initial_value)));
-       emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8));
-       emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
-                                        0, 0, 0, value));
-       emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size));
-       emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
-                             offsetof(struct bpf_loader_ctx, flags)));
-       emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2));
-       emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user));
-       emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1));
-       emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
+       if (!OPTS_GET(gen->opts, gen_hash, false)) {
+               emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6,
+                                     sizeof(struct bpf_loader_ctx) +
+                                     sizeof(struct bpf_map_desc) * map_idx +
+                                     offsetof(struct bpf_map_desc, initial_value)));
+               emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8));
+               emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
+                                                0, 0, 0, value));
+               emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size));
+               emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
+                                     offsetof(struct bpf_loader_ctx, flags)));
+               emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2));
+               emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user));
+               emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1));
+               emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
+       }
 
        map_update_attr = add_data(gen, &attr, attr_size);
        pr_debug("gen: map_update_elem: idx %d, value: off %d size %d, attr: off %d size %d\n",