On Fri, 2025-01-03 at 12:03 +0100, andrea terzolo wrote: > Hi folks! I would like to check with you if the verifier failure I'm > facing is expected. The verifier rejects the following eBPF program on > kernel 5.10.232. > > ``` > static long loop_fn(uint32_t index, void *ctx) { > bpf_printk("handle_exit\n"); > return 0; > } > > SEC("tp/raw_syscalls/sys_enter") > int test(void *ctx) { > if (bpf_core_enum_value_exists(enum bpf_func_id, BPF_FUNC_loop)) { > bpf_printk("loop\n"); > bpf_loop(12, loop_fn, NULL, 0); > } else { > bpf_printk("skip loop\n"); > } > return 0; > } > ``` > > With this error: > > ``` > libbpf: prog 'test': BPF program load failed: Invalid argument > libbpf: prog 'test': -- BEGIN PROG LOAD LOG -- > number of funcs in func_info doesn't match number of subprogs > processed 0 insns (limit 1000000) max_states_per_insn 0 total_states 0 > peak_states 0 mark_read 0 > -- END PROG LOAD LOG -- > libbpf: prog 'test': failed to load: -22 > ``` > > This sounds like a valid use case. I would like to use bpf_loop if > supported by the running kernel otherwise I can fall back to a simple > loop. This issue goes away on kernel 5.13 with the introduction of > PTR_TO_FUNC [0]. Is there a way I can use CO-RE features to avoid this > issue? I would expect the verifier to prune the dead code inside the > `if` but the error seems to be triggered before the control flow > analysis. > > [0]: https://github.com/torvalds/linux/commit/69c087ba6225b574afb6e505b72cb75242a3d844 bpf_loop was introduced by commit [1] and released as a part of 5.17. The error you see is indeed caused by the lack of PTR_TO_FUNC register type in an old kernel. In your program the call to bpf_loop would look like below in the assembly: ... r2 = loop_fn ;; here function pointer is taken ... call bpf_loop Before main verification pass verifier.c:add_subprog_and_kfunc() discovers subprogram entries by looking at function calls and function pointer assignments and compares it to function information provided via bpf_attr->func_info. The kernel that does not know about PTR_TO_FUNC would not find the loop_fn entry, hence the error message about mismatch. Additionally, verifier.c:check_cfg() looks for parts of the program that can't be reached by jump and call instructions. For this purpose pointers to functions are treated as function calls. The kernel that does not know about PTR_TO_FUNC it would seem that loop_fn is unreachable, this would cause another error message. Even if you add a dummy call to loop_fn verifier would most likely reject the program at 'r2 = loop_fn'. The approach libbpf uses to detect running kernel features is based on programs accept/reject status [2]. E.g. your program could be simplified to: static int loop_fn(int i, void *c) { return 0; } SEC("tp/raw_syscalls/sys_enter") int test(void *ctx) { bpf_loop(1, loop_fn, NULL, 0); return 0; } And checked if load is successful. [1] e6f2dd0f8067 ("bpf: Add bpf_loop helper") [2] see <kernel>/tools/lib/bpf/features.c