ebpflinux-security-module

BPF verifier rejects the use of path pointer as argument to the bpf_d_path helper function


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.


Solution

  • TL;DR. BPF_CORE_READ_INTO is unnecessary and causes the verifier to lose track of the variable's type.


    Verifier Error Explanation

    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).


    Root Cause

    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.


    Solution

    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!