Skip to content

CONCEPT Cited by 2 sources

eBPF verifier

The eBPF verifier is the static-analysis pass the Linux kernel runs on every eBPF program before it's allowed to load. It proves that the program is safe to execute in kernel context: it terminates (no unbounded loops), respects memory bounds, doesn't read uninitialised memory, and stays within instruction / stack / complexity limits.

The verifier is simultaneously:

  • eBPF's signature safety guarantee — the reason eBPF programs are deployable into production kernels where custom kernel modules are not.
  • The last line of defence against eBPF-based kernel exploits (see CVE-2023-2163, CVE-2024-41003).
  • The primary source of variability that makes eBPF operationally hard at fleet scale — what verifies on one kernel can fail to verify on another.

(Source: sources/2026-01-07-datadog-hardening-ebpf-for-runtime-security)

What the verifier checks

  • Termination. No unbounded loops (bounded loops supported from later kernels; pre-bounded-loop kernels require manual unrolling).
  • Memory bounds. Every pointer dereference must be provably within a known valid region. Stack variables occupy 8-byte-aligned "stack slots"; packing smaller variables can confuse the verifier.
  • Instruction count. From 4,096 initially to 1 million from kernel 5.2 — programs exceeding this are rejected.
  • Map operation legality. Some patterns restricted on older kernels (e.g. map-value as another map's key was unsupported before 4.18).
  • Context + tail-call consistency. From 6.11 tail-call targets must match context type, not just program type.
  • Helper availability. Context-dependent — Lockdown mode can disable bpf_probe_write_user, bpf_probe_read, etc.

Why it evolves

Every kernel release can add, tighten, or occasionally relax verifier rules. Datadog's Workload Protection supports kernels back to 4.15 (as of kernel 6.18 upstream) — verifier behaviour differs across that range in ways that routinely cause "works-in-dev, fails-in-prod" surprises.

Examples of subtle verifier drift (from the Datadog post):

  • Dead-code elimination at load time (allowing constant-patched program logic) from 4.15 — pre-4.15 kernels need alternative logic paths for the same product feature.
  • Instruction-count cap expansion (4k → 1M) reshapes what's expressible in-kernel.
  • Tighter memory-bounds checks on modern kernels may force extra explicit bounds checks that older kernels didn't require.
  • "Flaky" verifier failures when smaller-than-64-bit stack variables share an 8-byte slot.

Mitigation patterns

Datadog's Workload Protection handles verifier variability via:

  • Comprehensive CI matrix — a kernel version/distro is only "supported" if it's actively tested.
  • systems/ebpf-manager — centralised lifecycle library where verifier-quirk abstractions live (macros, wrappers).
  • systems/co-re — handles layout drift without recompilation.
  • Minimum-viable hook setebpf-manager fails the product loudly if a critical program won't verify on this kernel rather than serving reduced coverage silently.

Why it forces patterns/two-stage-evaluation

The verifier's complexity limit caps how rich in-kernel rule logic can be. Sophisticated expressions (wildcards, cross-field correlation, stateful patterns) often don't fit — so the architecture becomes "cheap kernel pass → rich user-space pass" with a data contract via eBPF maps (patterns/approver-discarder-filter is the canonical fill for the kernel side in systems/datadog-workload-protection-fim).

Seen in

Last updated · 200 distilled / 1,178 read