Search code examples
ebpfbpflibbpf

string matching in bpf programs


I am writing a bpf program in which i need to match prefix of filename in openat syscall.

Since we cannot link libc, and there is no such builtin function, i wrote one myself.

#define MAX_FILE_NAME_LENGTH 128
#define LOG_DIR "/my/prefix"
#define LEN_LOG_DIR sizeof(LOG_DIR)

int matchPrefix(char str[MAX_FILE_NAME_LENGTH]) {
  for (int i = 0; i < LEN_LOG_DIR; i++) {
    char ch1 = LOG_DIR[i];
    if (ch1 == '\0') {
      return 0;
    }
    char ch2 = str[i];
    if (ch2 == '\0') {
      return -1;
    }
    if (ch1 != ch2) {
      return -2;
    }
  }
  return (-3);
}

i am getting invalid mem access 'mem_or_null' error when i try to load this program.

libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
Validating matchPrefix() func#1...
38: R1=mem_or_null(id=2,off=0,imm=0) R10=fp0
; int matchPrefix(char str[MAX_FILE_NAME_LENGTH]) {
38: (18) r0 = 0xffffffff              ; R0_w=P4294967295
; char ch2 = str[i];
40: (71) r2 = *(u8 *)(r1 +0)
R1 invalid mem access 'mem_or_null'
processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

libbpf: -- END LOG --
libbpf: failed to load program 'syscall_enter_open'

R1 is the register for first argument. which is a char array on stack. Do i need to pass length of array separately?

the function is called this way

  char filename[MAX_FILE_NAME_LENGTH];    
  bpf_probe_read_user(filename, sizeof(filename), args->filename);    
  if (matchPrefix(filename) != 0) {    
    return 0;    
  }

Even if i change the function signature to accept a char * , there is some other error R1 invalid mem access 'scalar'.

Can someone help in understanding why am i getting this error in function verification?


Solution

  • TL;DR. Making your matchPrefix function a static inline one should work around the verifier issue.


    I believe this is happening because the BPF verifier recognizes your function as a global one (vs. inlined) and therefore verifies it independently. That means it won't assume anything for the arguments. Thus, the str argument is recognized as mem_or_null and verification fails because you didn't check that pointer isn't null.

    Inlining the function will work around this issue because the verifier won't see a function anymore. It will be able to preserve the inferred type of filename when verifying the code that corresponds to the body of matchPrefix.