Search code examples
csystem-callsebpfexecvetracepoint

How to bpf_probe_read_str all ctx->argv elements in kernel space and forward it to the userspace?


I am new to eBPF and currently trying to send all the executed commands with arguments in the userspace using a perf map.

I manage to send most of my data, but not the one accessed with bpf_probe_read_str. Print the output in console works, but i don't know where to look for to forward ctx->argv to the userspace after reading it from kernel.

Maybe am i supposed to this solution but copy all argv item manually ?

My code is based on liz's chapter 7 tuto

struct sys_enter_execve
{
    // ...
    char **argv;
};

struct data_t
{
    // ...
    char **argv;
};
SEC("tp/syscalls/sys_enter_execve")
int tp_sys_enter_execve(struct sys_enter_execve *ctx)
{
    struct data_t data = {};

    // ...

    for (int i = 0; i < sizeof(ctx->argv); i++)
    {
        char *arg = NULL;
        bpf_probe_read_user_str(&arg, sizeof(arg),  &ctx->argv[i]);

        data.argv[i] = arg; // Lead to the below error
        // bpf_map_update_elem(&argv, &i, &arg, BPF_ANY); // Another solution that lead to infinite loop for the verifier
        bpf_printk("arg%d: %s ", i, arg); // Works and print the correct sting
    }

    bpf_perf_event_output(ctx, &output, BPF_F_CURRENT_CPU, &data, sizeof(data));
    return 0;
}

Below is the error from data.argv[i] = arg;

42: (85) call bpf_probe_read_user_str#114     ; R0=scalar(smin=-4095,smax=8) fp-72=mmmmmmmm
; data.argv[i] = arg;
43: (79) r1 = *(u64 *)(r10 -72)       ; R1_w=scalar() R10=fp0 fp-72=mmmmmmmm
; data.argv[i] = arg;
44: (79) r2 = *(u64 *)(r10 -8)        ; R2_w=P0 R10=fp0 fp-8=00000000
; data.argv[i] = arg;
45: (7b) *(u64 *)(r2 +0) = r1
R2 invalid mem access 'scalar'
processed 43 insns (limit 1000000) max_states_per_insn 0 total_states 3 peak_states 3 mark_read 3
Failed to load BPF object

I tried the alternantive solution to manually copy all args into an hashmap with bpf_map_update_elem(&argv, &i, &arg, BPF_ANY); but the verifier can't load the program and send the below error

62: (85) call bpf_trace_printk#6
; for (int i = 0; i < sizeof(ctx->argv); i++)
infinite loop detected at insn 63
processed 97 insns (limit 1000000) max_states_per_insn 1 total_states 6 peak_states 6 mark_read 4

What did i miss in the documentation ? Thanks ✌️


Solution

  • So there were few confusion i had and it explain why i could not fix this problem. I'll give more details below.

    I think the error invalid mem access 'scalar' means that the verifier can't determine the size of a char pointer to check that the size of all variables stay in 512 bytes. Basically to fix this was just to fix the size of my variable from char **argv; to char[8][50]. I have noticed that the length of ctx->argv was always 8 so i took this value. Then the second mistake was to make sure the value was safe using bpf_probe_read before bpf_probe_read_str

    So the final code look like this.

    struct sys_enter_execve
    {
        // ...
        char **argv;
    };
    
    struct data_t
    {
        // ...
        char argv[8][50]; // Fix the size here is critical for the verifier
    };
    SEC("tp/syscalls/sys_enter_execve")
    int tp_sys_enter_execve(struct sys_enter_execve *ctx)
    {
        struct data_t data = {};
    
        // ...
    
        for (int i = 0; i < sizeof(ctx->argv); i++)
        {
            char *tmp;
            bpf_probe_read(&tmp, sizeof(tmp), &ctx->argv[i]);
            bpf_probe_read_str(data.argv[i], sizeof(data.argv[i]), tmp);
        }
    
        bpf_perf_event_output(ctx, &output, BPF_F_CURRENT_CPU, &data, sizeof(data));
        return 0;
    }
    

    I hope it could help for your code.

    Happy coding ✌️