Here is a very basic eBPF program. I am trying to hook file deletion.
#define __TARGET_ARCH_arm64
#include <linux/ptrace.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
char LICENSE[] SEC("license") = "Dual BSD/GPL";
typedef struct {
int counter;
} atomic_t;
struct filename {
const char *name;
const char *uptr;
atomic_t refcnt;
struct audit_names *aname;
const char iname[0];
};
SEC("kprobe/do_unlinkat")
int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name)
{
const char *filename = BPF_CORE_READ(name, name);
// This line causes the crash
if (filename!=NULL && *filename=='t') return 1;
bpf_printk("Deleting this file:%s", filename);
return 0;
}
Everything works fine, except when I am trying do dereference filename pointer.
I get this error, at compilation time:
arg#0 reference type('FWD pt_regs') size cannot be determined: -22
0: R1=ctx() R10=fp0
; int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name) @ test_ebpf.c:30
...
R3 invalid mem access 'scalar'
processed 11 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
Can anyone explain me how I can access to filename content ? bpf_printk manages to display this value...
Thanks
TL;DR. BPF_CORE_READ
reads the value of the name->name
pointer with CO-RE, but you need still need to copy the string from the kernel memory to the the BPF stack with bpf_probe_read_kernel_str
.
Explanation
Your program retrieves a value from kernel memory with BPF_CORE_READ
(syntactic sugar for bpf_probe_read_kernel
) and attempts to dereference it. The verifier however rejects your program because it doesn't know anything about the value returned by BPF_CORE_READ
and therefore treats it as a scalar (hence the R3 invalid mem access 'scalar'
error) and not a pointer to a string.
The call to bpf_printk
passes the verifier because this helper (bpf_trace_printk
) includes runtime checks to handle such pointers (cf. bpf_bprintf_prepare
).
Solution
The BPF CO-RE reference guide provides the solution. You need to call bpf_probe_read_kernel_str
after BPF_CORE_READ
to copy the string to the BPF stack. The example below passes the verifier on my system.
SEC("kprobe/do_unlinkat")
int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name)
{
char filename[40];
const char *ptr;
int ret;
ptr = BPF_CORE_READ(name, name);
ret = bpf_probe_read_kernel_str(filename, sizeof(filename), ptr);
if (ret < 0 && *filename == 't')
return 1;