[PATCH bpf-next 07/13] libbpf: generate prog BTF for loader programs

KP Singh kpsingh at kernel.org
Fri May 22 02:32:27 UTC 2026


For signed loaders, build a minimal prog BTF containing one FUNC entry
for bpf_loader_verify_metadata with a FUNC_PROTO of primitives (int,
void *) so the bytes are reproducible across build hosts.

In emit_verify_metadata, spill four signed ld_imm64 immediates into
a 32-byte stack buffer and invoke the kfunc once with (map, &buf, 32),
replacing ~24 inline dword comparisons. Expose the serialized BTF on
gen_loader_opts from bpf_gen__finish so bpftool gen can embed it in
the lskel.

Signed-off-by: KP Singh <kpsingh at kernel.org>
---
 tools/lib/bpf/bpf_gen_internal.h |   2 +
 tools/lib/bpf/gen_loader.c       | 127 ++++++++++++++++++++++++-------
 tools/lib/bpf/libbpf.h           |   4 +-
 3 files changed, 105 insertions(+), 28 deletions(-)

diff --git a/tools/lib/bpf/bpf_gen_internal.h b/tools/lib/bpf/bpf_gen_internal.h
index 49af4260b8e6..e4aed9e99b56 100644
--- a/tools/lib/bpf/bpf_gen_internal.h
+++ b/tools/lib/bpf/bpf_gen_internal.h
@@ -52,6 +52,8 @@ struct bpf_gen {
 	int fd_array;
 	int nr_fd_array;
 	int hash_insn_offset[SHA256_DWORD_SIZE];
+	struct btf *loader_btf;
+	int loader_btf_func_id;
 };
 
 void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps);
diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index fee35c26deb8..48ac25c058e3 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -35,6 +35,7 @@ struct loader_stack {
 	__u32 btf_fd;
 	__u32 inner_map_fd;
 	__u32 prog_fd[MAX_USED_PROGS];
+	__u64 metadata_hash[SHA256_DWORD_SIZE];
 };
 
 #define stack_off(field) \
@@ -109,7 +110,7 @@ static void emit2(struct bpf_gen *gen, struct bpf_insn insn1, struct bpf_insn in
 
 static int add_data(struct bpf_gen *gen, const void *data, __u32 size);
 static void emit_sys_close_blob(struct bpf_gen *gen, int blob_off);
-static void emit_signature_match(struct bpf_gen *gen);
+static void emit_verify_metadata(struct bpf_gen *gen);
 
 void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps)
 {
@@ -152,8 +153,68 @@ void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps
 	/* R7 contains the error code from sys_bpf. Copy it into R0 and exit. */
 	emit(gen, BPF_MOV64_REG(BPF_REG_0, BPF_REG_7));
 	emit(gen, BPF_EXIT_INSN());
-	if (OPTS_GET(gen->opts, gen_hash, false))
-		emit_signature_match(gen);
+	if (OPTS_GET(gen->opts, gen_hash, false)) {
+		int int_id, u32_id, ptr_id, proto_id, err;
+
+		/* Prog BTF built from primitives so the bytes are reproducible
+		 * across build hosts.
+		 */
+		gen->loader_btf = btf__new_empty();
+		if (libbpf_get_error(gen->loader_btf)) {
+			gen->error = -ENOMEM;
+			gen->loader_btf = NULL;
+			return;
+		}
+		if (gen->swapped_endian) {
+			enum btf_endianness target =
+				btf__endianness(gen->loader_btf) == BTF_LITTLE_ENDIAN
+				? BTF_BIG_ENDIAN : BTF_LITTLE_ENDIAN;
+
+			err = btf__set_endianness(gen->loader_btf, target);
+			if (err) {
+				gen->error = err;
+				return;
+			}
+		}
+		int_id = btf__add_int(gen->loader_btf, "int", 4, BTF_INT_SIGNED);
+		if (int_id < 0) {
+			gen->error = int_id;
+			return;
+		}
+		u32_id = btf__add_int(gen->loader_btf, "u32", 4, 0);
+		if (u32_id < 0) {
+			gen->error = u32_id;
+			return;
+		}
+		ptr_id = btf__add_ptr(gen->loader_btf, 0);
+		if (ptr_id < 0) {
+			gen->error = ptr_id;
+			return;
+		}
+		proto_id = btf__add_func_proto(gen->loader_btf, int_id);
+		if (proto_id < 0) {
+			gen->error = proto_id;
+			return;
+		}
+		err = btf__add_func_param(gen->loader_btf, "map", ptr_id);
+		if (!err)
+			err = btf__add_func_param(gen->loader_btf, "hash", ptr_id);
+		if (!err)
+			err = btf__add_func_param(gen->loader_btf, "hash__sz", u32_id);
+		if (err) {
+			gen->error = err;
+			return;
+		}
+		gen->loader_btf_func_id = btf__add_func(gen->loader_btf,
+							"bpf_loader_verify_metadata",
+							BTF_FUNC_GLOBAL, proto_id);
+		if (gen->loader_btf_func_id < 0) {
+			gen->error = gen->loader_btf_func_id;
+			gen->loader_btf_func_id = 0;
+			return;
+		}
+		emit_verify_metadata(gen);
+	}
 }
 
 static int add_data(struct bpf_gen *gen, const void *data, __u32 size)
@@ -398,7 +459,7 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps)
 			      blob_fd_array_off(gen, i));
 	emit(gen, BPF_MOV64_IMM(BPF_REG_0, 0));
 	emit(gen, BPF_EXIT_INSN());
-	if (OPTS_GET(gen->opts, gen_hash, false))
+	if (!gen->error && OPTS_GET(gen->opts, gen_hash, false))
 		compute_sha_update_offsets(gen);
 
 	pr_debug("gen: finish %s\n", errstr(gen->error));
@@ -410,6 +471,19 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps)
 		opts->data = gen->data_start;
 		opts->data_sz = gen->data_cur - gen->data_start;
 
+		if (gen->loader_btf) {
+			__u32 btf_sz = 0;
+			const void *btf_data;
+
+			btf_data = btf__raw_data(gen->loader_btf, &btf_sz);
+			if (!btf_data) {
+				gen->error = -ENOMEM;
+				return gen->error;
+			}
+			OPTS_SET(opts, btf, btf_data);
+			OPTS_SET(opts, btf_sz, btf_sz);
+		}
+
 		/* use target endianness for embedded loader */
 		if (gen->swapped_endian) {
 			struct bpf_insn *insn = (struct bpf_insn *)opts->insns;
@@ -426,6 +500,7 @@ void bpf_gen__free(struct bpf_gen *gen)
 {
 	if (!gen)
 		return;
+	btf__free(gen->loader_btf);
 	free(gen->data_start);
 	free(gen->insn_start);
 	free(gen);
@@ -580,39 +655,37 @@ void bpf_gen__map_create(struct bpf_gen *gen,
 		emit_sys_close_stack(gen, stack_off(inner_map_fd));
 }
 
-static void emit_signature_match(struct bpf_gen *gen)
+static void emit_verify_metadata(struct bpf_gen *gen)
 {
 	__s64 off;
 	int i;
 
+	/* arg1: metadata struct bpf_map */
+	emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
+					 0, 0, 0, 0));
+
+	/* arg2: hash buffer on our BPF stack, populated from ld_imm64
+	 * immediates patched in by compute_sha_update_offsets() before signing.
+	 */
+	emit(gen, BPF_MOV64_REG(BPF_REG_2, BPF_REG_10));
+	emit(gen, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, stack_off(metadata_hash)));
 	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));
-		emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, i * sizeof(__u64)));
 		gen->hash_insn_offset[i] = gen->insn_cur - gen->insn_start;
 		emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_3, 0, 0, 0, 0, 0));
-
-		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_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, off));
-		} else {
-			gen->error = -ERANGE;
-		}
+		emit(gen, BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3,
+				      i * sizeof(__u64)));
 	}
 
-	/* 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_DW, BPF_REG_2, BPF_REG_1, SHA256_DWORD_SIZE * sizeof(__u64)));
-	off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2;
+	/* arg3: hash length */
+	emit(gen, BPF_MOV64_IMM(BPF_REG_3, SHA256_DWORD_SIZE * sizeof(__u64)));
+
+	emit(gen, BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0,
+			       BPF_PSEUDO_KFUNC_CALL_PROG_BTF, 0,
+			       gen->loader_btf_func_id));
+	emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_0));
+	off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 1;
 	if (is_simm16(off)) {
-		emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL));
-		emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_2, 0, off));
+		emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, off));
 	} else {
 		gen->error = -ERANGE;
 	}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index bba4e8464396..25906fb695f9 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -1899,9 +1899,11 @@ struct gen_loader_opts {
 	__u32 data_sz;
 	__u32 insns_sz;
 	bool gen_hash;
+	const void *btf;
+	__u32 btf_sz;
 };
 
-#define gen_loader_opts__last_field gen_hash
+#define gen_loader_opts__last_field btf_sz
 LIBBPF_API int bpf_object__gen_loader(struct bpf_object *obj,
 				      struct gen_loader_opts *opts);
 
-- 
2.53.0




More information about the Linux-security-module-archive mailing list