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 forld_immorld_absinstructions reading from a stack pointer offset that’s immediately followed by aaddormovusing 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.
- Diagnosis: Use
-
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 performld_maporst_mapoperations. 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., usingsize - 1wheresizeis 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.
- Diagnosis: Examine the eBPF instructions (
-
Cause 3: Incorrect Map Access with
bpf_map_lookup_elem: You’re passing a pointer to a stack variable as the key tobpf_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_elemand check the register used for the key argument. If it points to the stack (fpregister), 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.
- Diagnosis: Look for
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 effectively0in 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 hold0. This often happens after a map lookup that returnedNULL. - Fix: Add a
NULLcheck 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
NULLensures that you only attempt to read from valid memory addresses, preventing potential kernel panics.
- Diagnosis: Look for instructions that use a register as a base address (
-
Cause 2: Using
bpf_probe_readIncorrectly: You’re usingbpf_probe_read(orbpf_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_readinstructions. 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_readis 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.
- Diagnosis: Examine the
-
Cause 3: Type Mismatch with
bpf_map_push_elem/pop_elem: When using stack-like maps (e.g.,BPF_MAP_TYPE_QUEUEorBPF_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_elemorcall 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.
- Diagnosis: Look for
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_SUPPORTor 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>orbpftool prog dump xlated <prog_fd>for the specificcallinstruction. - Fix:
- Check Kernel Version: Determine your kernel version (
uname -r). - Check Available Helpers: Consult the kernel documentation for your specific version or use
bpftool prog show <prog_fd>and look for the "helpers" section. - 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. - Check Kernel Version: Determine your kernel version (
- 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.
- Diagnosis: The error message itself usually indicates the helper function name. Check
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.