1

I have a BPF_MAP_TYPE_ARRAY map that stores instances of this struct:

    struct target_d_name {
        unsigned long int len;
        char name[PID_LEN_MAX]; //PID_LEN_MAX = 8, it is a macro
    };

Here is the map definition:

    struct {
        __uint(type, BPF_MAP_TYPE_ARRAY);
        __uint(max_entries, 8);
        __type(key, u32);
        __type(value, sizeof(struct target_d_name));
    } map_d_name_tgts SEC(".maps");

What I am doing can be summarised as the following:

  1. Hook exit of getents64
  2. Loop through all dirent64 structures being returned
  3. See if any of dirent64->d_name strings match anything in target_d_name->name

Here is the code that does the actual string comparison between the dirent64 strings and the strings in my map:

    //iterator_limit is either len(filename) or PID_LEN_MAX, whichever is smallest
    for (k = 0; k < iterator_limit; ++k) {
      if (filename[k] != target_d_name_instance->name[k]) break;
    }

As you can see, everything is in bounds. However at load time the eBPF verifier errors out with:

    invalid access to map value, value_size=8 off=8 size=1
    R5 min value is outside of the allowed memory range

Where R5 attempts to dereference the address of target_d_name_instance->name[k].

At first I expected this to work since everything is in bounds. Upon further investigation however I found numerous other people who have come across this problem. Namely:

From these links I gathered that the eBPF verifier fails to keep track of the size of the character buffers in my struct. As such, when I iterate with 0 <= k <= PID_LEN_MAX, at some point k may fall outside the allowed memory range. While this is impossible, the eBPF verifier isn't aware of this.

However I still don't know how what my new approach should be. How else can I pass strings to my eBPF programs from userspace? What if I need to carry out string comparisons on them and hence iterate over them? I noticed the existence of the bpf_strncmp() helper, but it only works on constants and is therefore not very useful if the strings are being dynamically generated.

UPDATE:

Here is a minimal example that produces an identical error:

    u32 index = 0;
    struct target_d_name * current_d_name 
        = bpf_map_lookup_elem(&map_d_name_tgts, &index);
    if (current_d_name == 0) return -1;

    for (int i = 0; i < PID_LEN_MAX; ++i) {
        current_d_name->name[i] = 'u';
    }
    return 0;

1 Answer 1

1

how about change

    struct {
        __uint(type, BPF_MAP_TYPE_ARRAY);
        __uint(max_entries, 8);
        __type(key, u32);
        __type(value, sizeof(struct target_d_name));
    } map_d_name_tgts SEC(".maps");

to

    struct {
        __uint(type, BPF_MAP_TYPE_ARRAY);
        __uint(max_entries, 8);
        __type(key, u32);
        __type(value, struct target_d_name);  // changed
    } map_d_name_tgts SEC(".maps");
Sign up to request clarification or add additional context in comments.

1 Comment

Fantastic, thank you so much. I see I was actually allocating size_t worth of bytes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.