The eBPF verifier is failing because your program violates a fundamental safety rule, preventing it from being loaded into the kernel.

Common Verifier Errors and Their Fixes

1. invalid dereference in load / invalid read from stack / invalid read from map

  • What broke: Your eBPF program is attempting to access memory (stack or map) that it hasn’t properly initialized or that is out of bounds. The kernel verifier sees this as a potential crash for the entire system.

  • Cause 1: Uninitialized Stack Variable: You declared a local variable on the stack but didn’t assign it a value before reading from it.

    • Diagnosis: Use bpftool prog dump xlated <prog_fd> to see the BPF instructions. Look for ld_imm or ld_abs instructions reading from a stack pointer offset that’s immediately followed by a add or mov using that stack pointer before an assignment.
    • Fix: Initialize the variable before use.
      // Before:
      int my_var;
      // ...
      if (my_var > 10) { ... } // Verifier error!
      
      // After:
      int my_var = 0; // Initialize
      // ...
      if (my_var > 10) { ... }
      
    • Why it works: Explicitly assigning a value ensures the memory location contains predictable data, satisfying the verifier’s requirement for safe memory access.
  • Cause 2: Out-of-Bounds Map Access: You’re trying to read from or write to a map at an index or key that is outside the map’s defined range or for which no entry exists.

    • Diagnosis: Examine the eBPF instructions (bpftool prog dump xlated <prog_fd>) that perform ld_map or st_map operations. Check if the register used for the key/index is being calculated in a way that could go beyond the map’s bounds (e.g., using size - 1 where size is a variable that could be 0).
    • Fix: Add bounds checking before accessing the map.
      // Assuming map is a bpf_map_type_array with 10 elements
      u32 index = get_some_index(); // Could return 10 or more
      
      if (index >= 10) { // Check bounds
          return 0; // Or handle error
      }
      value = bpf_map_lookup_elem(&my_map, &index);
      
    • Why it works: The explicit check prevents the program from attempting to access memory beyond the allocated map storage, which the verifier deems unsafe.
  • Cause 3: Incorrect Map Access with bpf_map_lookup_elem: You’re passing a pointer to a stack variable as the key to bpf_map_lookup_elem, but the verifier can’t guarantee that the stack variable will always be valid for the entire duration of the lookup.

    • Diagnosis: Look for call bpf_map_lookup_elem and check the register used for the key argument. If it points to the stack (fp register), the verifier might flag it.
    • Fix: Copy the key value into a separate, kernel-allocated map value if possible, or ensure the stack variable’s lifetime is clearly managed. For simple types, copying to a register is often sufficient if the verifier can track it.
      // If key is a simple integer:
      u32 key_val = get_key_value();
      // ...
      // Verifier might complain if key_val is on stack and used directly
      // Instead, ensure it's in a register or a stable memory location.
      // Often, the compiler handles this if key_val is in a register.
      
      // A more robust fix for complex keys or stack issues:
      // Allocate a temporary map entry if possible, or use a fixed register copy.
      // For simple integers, ensure it's in a register:
      u32 index = get_some_index();
      value = bpf_map_lookup_elem(&my_map, &index); // If index is in a register, usually fine.
      
    • Why it works: The verifier needs to be certain that the pointer passed to kernel helpers remains valid. By ensuring the key is in a register or a known-valid memory location, you satisfy this requirement.

2. invalid indirect read / invalid indirect write

  • What broke: Your eBPF program is trying to access memory via a pointer, but the verifier cannot determine the type or bounds of the memory pointed to. This is common when dealing with data passed from the kernel (e.g., struct sk_buff).

  • Cause 1: Dereferencing a NULL Pointer: The pointer you’re using is NULL (or effectively 0 in BPF’s view), and you’re trying to read from it.

    • Diagnosis: Look for instructions that use a register as a base address (ld_ind, st_ind) and check if that register can possibly hold 0. This often happens after a map lookup that returned NULL.
    • Fix: Add a NULL check before dereferencing.
      void *ptr = bpf_map_lookup_elem(&my_map, &key);
      if (!ptr) { // NULL check
          return 0;
      }
      // Now it's safe to dereference ptr
      struct my_struct *data = ptr;
      // ... use data->field ...
      
    • Why it works: Explicitly checking for NULL ensures that you only attempt to read from valid memory addresses, preventing potential kernel panics.
  • Cause 2: Using bpf_probe_read Incorrectly: You’re using bpf_probe_read (or bpf_probe_read_kernel) to read from memory, but the size argument is too large or the destination buffer is too small, or the source pointer is invalid.

    • Diagnosis: Examine the call bpf_probe_read instructions. Check the arguments: destination pointer, source pointer, and size. Ensure the source pointer is valid and the size is appropriate for the destination buffer and the memory being read.
    • Fix: Ensure the destination buffer is large enough and the source pointer is valid.
      char buffer[64];
      void *src_ptr = get_source_pointer(); // Could be invalid
      
      if (bpf_probe_read(buffer, sizeof(buffer), src_ptr) < 0) {
          // Handle read error, e.g., src_ptr was invalid
          return 0;
      }
      // Now buffer contains data from src_ptr
      
    • Why it works: bpf_probe_read is designed to safely read from potentially unsafe memory regions. Providing correct arguments ensures it can perform the read without causing memory corruption or accessing invalid kernel memory.
  • Cause 3: Type Mismatch with bpf_map_push_elem/pop_elem: When using stack-like maps (e.g., BPF_MAP_TYPE_QUEUE or BPF_MAP_TYPE_STACK), you might be pushing or popping data of an incorrect size, leading the verifier to believe the memory layout is inconsistent.

    • Diagnosis: Look for call bpf_map_push_elem or call bpf_map_pop_elem. Check the size argument passed to these helpers.
    • Fix: Ensure the size argument exactly matches the size of the data being pushed or popped.
      struct event_data { int id; char name[32]; };
      struct event_data data = { .id = 1, .name = "test" };
      
      // Correctly push the entire struct
      bpf_map_push_elem(&my_stack, &data, BPF_ANY);
      
      // Incorrectly: bpf_map_push_elem(&my_stack, &data.id, sizeof(data.id));
      
    • Why it works: These map types rely on consistent data sizes for their internal structure. Mismatched sizes violate the verifier’s assumptions about memory coherence.

3. call_imm_unreachable

  • What broke: Your eBPF program is trying to call a BPF helper function that doesn’t exist or is not available in the current kernel version/configuration.
  • Cause 1: Using Newer Helper Functions on Older Kernels: You’re compiling your eBPF program with CLANG_BPF_SYSCALL_SUPPORT or similar flags that enable newer helpers, but you’re trying to load it on a kernel that doesn’t support them.
    • Diagnosis: The error message itself usually indicates the helper function name. Check bpftool btf dump id <btf_id> or bpftool prog dump xlated <prog_fd> for the specific call instruction.
    • Fix:
      1. Check Kernel Version: Determine your kernel version (uname -r).
      2. Check Available Helpers: Consult the kernel documentation for your specific version or use bpftool prog show <prog_fd> and look for the "helpers" section.
      3. Update Kernel or Program: Either upgrade your kernel to a version that supports the helper, or rewrite your eBPF program to use older, compatible helpers.
      // Example: If using bpf_redirect_map which might be newer
      // Check kernel support. If not available, use alternative.
      
    • Why it works: BPF helper functions are kernel features. Using a helper that the kernel doesn’t recognize leads to an immediate rejection by the verifier.

The Next Error You’ll Hit:

After resolving verifier errors, you’ll likely encounter issues with your eBPF map data not being populated as expected, or your trace logs showing unexpected values, leading you to investigate map contents and program output more closely.

Want structured learning?

Take the full Ebpf course →