Bob5421
Bob5421

Reputation: 9163

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

Upvotes: 1

Views: 163

Answers (1)

pchaigno
pchaigno

Reputation: 13133

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;

Upvotes: 0

Related Questions