string matching in bpf programs

848 views Asked by At

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?

2

There are 2 answers

0
pchaigno On BEST ANSWER

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.

1
Devidas On

there is easier solution using strcmp. find in xdp-project/bpf-next

code from the same is

    int strcmp(const char *cs, const char *ct)
    {
        unsigned char c1, c2;
    
        while (1) {
            c1 = *cs++;
            c2 = *ct++;
            if (c1 != c2)
                return c1 < c2 ? -1 : 1;
            if (!c1)
                break;
        }
        return 0;
    }

Do let me know if you still have issue.

NOTE: you cannot use #define to define string. do reverify line


   char ch1 = LOG_DIR[i];