bpf_program__set_load_strategy()'s MANUAL case rejects setting MANUAL strategy while a gen_loader is already attached, but a program marked MANUAL declaratively (SEC("!...")) gets that strategy during bpf_object__open(), before bpf_object__gen_loader() can ever be called, so the existing guard can never observe it. Left unchecked, bpf_object_load_progs() skips such programs, so gen->nr_progs undercounts relative to the object's real program count. bpf_gen__finish() only rejects the opposite mismatch direction (nr_progs < gen->nr_progs), so this passes silently, and every generated skeleton program slot after the manual one ends up wired to the wrong prog_fd. Catch it at the one point guaranteed to run after any MANUAL marking has already happened: reject in bpf_object__gen_loader() itself if any program already has load_strategy == BPF_PROG_LOAD_STRATEGY_MANUAL. Assisted-by: Claude:claude-sonnet-5 Suggested-by: Andrii Nakryiko Signed-off-by: Andrey Grodzovsky --- tools/lib/bpf/libbpf.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index c9a184d9626d..3ac7319ed0f4 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -9752,11 +9752,29 @@ int bpf_object__set_kversion(struct bpf_object *obj, __u32 kern_version) int bpf_object__gen_loader(struct bpf_object *obj, struct gen_loader_opts *opts) { struct bpf_gen *gen; + size_t i; if (!opts) return libbpf_err(-EFAULT); if (!OPTS_VALID(opts, gen_loader_opts)) return libbpf_err(-EINVAL); + + /* + * Manually-loaded programs are not visible to gen_loader (see + * bpf_program__set_load_strategy()'s MANUAL case), and marking a + * program MANUAL happens during bpf_object__open(), before this + * function can ever run, so that guard can never catch it here. + * Reject any pre-existing MANUAL program now, since this is the + * earliest point where both are known. + */ + for (i = 0; i < obj->nr_programs; i++) { + if (obj->programs[i].load_strategy == BPF_PROG_LOAD_STRATEGY_MANUAL) { + pr_warn("prog '%s': gen_loader does not support manually-loaded programs\n", + obj->programs[i].name); + return libbpf_err(-EOPNOTSUPP); + } + } + gen = calloc(1, sizeof(*gen)); if (!gen) return libbpf_err(-ENOMEM); -- 2.34.1