I have written an example ebpf program to attach to the lsm/file_open
but the verifier rejects my program.
my program:
#include "vmlinux.h"
#include "commons.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
#define MAX_SIZE 255
SEC("lsm/file_open")
int BPF_PROG(test_prog, struct file *file, int ret)
{
unsigned int f_flags;
struct path file_path;
char path_buffer[MAX_SIZE];
BPF_CORE_READ_INTO(&file_path, file, f_path);
long err = bpf_d_path(&file_path, path_buffer, sizeof(path_buffer));
return 0;
}
char LICENSE[] SEC("license") = "GPL";
verifier produces this output:
libbpf: prog 'test_prog': BPF program load failed: Permission denied
libbpf: prog 'test_prog': -- BEGIN PROG LOAD LOG --
0: R1=ctx() R10=fp0
; int BPF_PROG(test_prog, struct file *file, int ret)
0: (b7) r2 = 152 ; R2_w=152
; int BPF_PROG(test_prog, struct file *file, int ret)
1: (79) r3 = *(u64 *)(r1 +0)
func 'bpf_lsm_file_open' arg0 has btf_id 601 type STRUCT 'file'
2: R1=ctx() R3_w=trusted_ptr_file()
2: (0f) r3 += r2 ; R2_w=152 R3_w=trusted_ptr_file(off=152)
3: (bf) r6 = r10 ; R6_w=fp0 R10=fp0
;
4: (07) r6 += -16 ; R6_w=fp-16
; BPF_CORE_READ_INTO(&file_path, file, f_path);
5: (bf) r1 = r6 ; R1_w=fp-16 R6_w=fp-16
6: (b7) r2 = 16 ; R2_w=16
7: (85) call bpf_probe_read_kernel#113 ; R0_w=scalar() fp-8=mmmmmmmm fp-16=mmmmmmmm
8: (bf) r2 = r10 ; R2_w=fp0 R10=fp0
;
9: (07) r2 += -271 ; R2_w=fp-271
; long err = bpf_d_path(&file_path, path_buffer, sizeof(path_buffer));
10: (bf) r1 = r6 ; R1_w=fp-16 R6_w=fp-16
11: (b7) r3 = 255 ; R3_w=255
12: (85) call bpf_d_path#147
R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_
processed 13 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
-- END PROG LOAD LOG --
libbpf: prog 'test_prog': failed to load: -13
libbpf: failed to load object 'lsm_file_open.o'
Failed to load BPF object into the kernel
Any help would be greatly appreciated. My kernel version is: 6.8.0-51-generic.
TL;DR. BPF_CORE_READ_INTO
is unnecessary and causes the verifier to lose track of the variable's type.
12: (85) call bpf_d_path#147
R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_
The verifier complains that the type for R1 when calling bpf_d_path
is not as it expects. Since it's R1, we're talking about the first argument of bpf_d_path
, file_path
.
And when it says type
, it means verifier types. So the verifier expected one of the listed types (ptr_
aka PTR_TO_BTF_ID
, with trusted and RCU variants, see reg_type_str
for the mappings). Instead it got a pointer to the stack (fp
aka PTR_TO_STACK
).
Reading up, we can see that R1 points to the offset -16 in the stack (R1_w=fp-16
). And before that, we can see that that offset was written by bpf_probe_read_kernel
. As expected and shown by the annotated verifier output, bpf_probe_read_kernel
comes from the compilation of BPF_CORE_READ_INTO
. So BPF_CORE_READ_INTO
is copying your file->f_path
pointer to the stack.
Looking at verifier examples upstream (ex., tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c
), we can see the call to BPF_CORE_READ_INTO
isn't needed. And the copy to the stack probably confuses the verifier here.
Your code can be rewritten to:
SEC("lsm/file_open")
int BPF_PROG(test_prog, struct file *file, int ret)
{
unsigned int f_flags;
char path_buffer[MAX_SIZE];
long err = bpf_d_path(&file->f_path, path_buffer, sizeof(path_buffer));
return 0;
}
and it loads!