ebpf

How can I dereference a pointer in an eBPF program


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


Solution

  • 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;