Description
cilium/ebpf
can detect if a symbol is part of a kmod and load it automatically. This is done here by checking the function name that the user wants to attach in /proc/kallsyms
.
This does not work as expected when we want to relocate a symbol that is part of a kmod but the attach functions is not.
As an example, in my system, overlay
is build as a module, and its BTF resides in /sys/kernel/btf/overlay
. The following program attaches to ovl_inode_lower
, a function from overlay
kmod. cilium/ebpf
will check that and load overlay
's BTF. This will also be used to evaluate bpf_core_type_exists(struct ovl_entry)
, the verifier will remove the infinite loop, and the program will load as expected.
SEC("fentry/ovl_inode_lower")
int BPF_PROG(ovl_inode_lower, struct inode *inode)
{
if (!bpf_core_type_exists(struct ovl_entry))
while(1) {}
return 0;
}
On the other hand, the following program attaches to a function that is not part of any kmod. Thus cilium/ebpf
will not load overlay
's BTF. bpf_core_type_exists(struct ovl_entry)
will evaluate to 0, the infinite loop will not be removed by the verifier and the program fails to load (even the kmod BTF exists).
SEC("fentry/security_file_open")
int BPF_PROG(security_file_open, struct file *file)
{
if (!bpf_core_type_exists(struct ovl_entry))
while(1) {}
return 0;
}
These are tested with these programs with cilium/ebpf v0.15.0 and the output is:
$ sudo ./main
Program ovl_inode_lower loaded successfully
Failed to load program security_file_open with error:
ebpf.NewProgram: load program: invalid argument:
0: R1=ctx() R10=fp0
; int BPF_PROG(security_file_open, struct file *file)
0: (b7) r1 = 0 ; R1_w=0
; if (!bpf_core_type_exists(struct ovl_entry))
1: (55) if r1 != 0x0 goto pc+1 ; R1_w=0
; while(1) {}
2: (05) goto pc-1
2: (05) goto pc-1
2: (05) goto pc-1
2: (05) goto pc-1
2: (05) goto pc-1
2: (05) goto pc-1
infinite loop detected at insn 2
cur state: R1=0 R10=fp0
old state: R1_w=0 R10=fp0
processed 9 insns (limit 1000000) max_states_per_insn 0 total_states 1 peak_states 1 mark_read 0
A way to overcome this limitation is to manually load the overlay BTF with a function similar to this one and provide its output to ProgramOptions.KernelTypes. This makes the second program to load as well.