eBPF Verifier Security: Understanding Kernel Bypass Attacks and Defense
eBPF now runs inside the Linux kernel of nearly every cloud server, container host, and modern security tool โ powering networking, observability, and even the defenses meant to catch attackers. The one thing standing between an untrusted program and the kernel is a piece of code called the verifier, and 2026 has already produced two significant CVEs showing what happens when it can be tricked.
๐ฐ Beginner’s Guide โ the short version
eBPF lets programs run inside the Linux kernel for things like monitoring and security tooling, but only after a gatekeeper called “the verifier” checks the code is safe. Recently, security researchers found real bugs (CVE-2026-64036 and CVE-2026-31525) where the verifier’s safety checks didn’t match what actually happens when the code runs, letting a local user with special permissions crash or take over the kernel. This mainly affects servers and cloud systems running recent Linux kernels where eBPF is enabled for non-root users. If you manage Linux systems, the single most important thing to do is make sure kernel.unprivileged_bpf_disabled is set to 1 and keep your kernel patched โ that setting alone blocks most of these attacks from being reachable at all.
What Is the eBPF Verifier?
eBPF (extended Berkeley Packet Filter) lets user-supplied programs run directly inside the Linux kernel, which is powerful but dangerous: a buggy or malicious kernel-mode program can crash the machine or read/write memory it shouldn’t touch. Before any eBPF program is allowed to load, the kernel’s verifier performs static analysis on it.
The verifier walks the program’s control-flow graph, rejecting anything with loops or unreachable code so it can’t run forever or hide malicious paths. It then simulates every possible execution path, tracking what type each register holds โ a context pointer (PTR_TO_CTX), a pointer into a BPF map (PTR_TO_MAP_VALUE), a pointer into the stack (PTR_TO_STACK), or an ordinary scalar value โ and enforces bounds and alignment checks on every memory access. It also rejects any read of a register that hasn’t been initialized.
Because fully re-simulating every path through a large program would be too slow, the verifier uses state pruning: if it has already verified a state that is a strict superset of the current one, it skips re-analyzing that path. This shortcut is efficient, but it’s also where much of the verifier’s historical bug surface lives โ subtle mismatches between the abstract “safe” state the verifier records and what the CPU actually does at runtime have repeatedly produced serious vulnerabilities.
How Verifier Bypass Attacks Work (Conceptually)
An eBPF verifier bypass doesn’t look like a traditional exploit with shellcode. Instead, an attacker crafts a BPF program that the verifier believes is safe, but that behaves differently once it’s actually running. Historically, these bugs fall into a few recurring patterns:
- Arithmetic/bounds-tracking errors โ the verifier’s model of what values a register can hold after an operation (like a bitwise AND, OR, or XOR) doesn’t match the real 32-bit truncation semantics of the hardware, so the verifier “proves” an access is in-bounds when it isn’t.
- Verifier/runtime semantic mismatches โ the verifier reasons about the possible outputs of an instruction using one model, while the actual interpreter or JIT-compiled code computes something different, especially in edge cases like signed integer overflow.
- Kfunc input-validation gaps โ as the kernel exposes more specialized “kfunctions” for BPF programs to call, some of these functions fail to validate caller-supplied inputs (like an array index), creating an out-of-bounds access that has nothing to do with the verifier’s core arithmetic logic at all.
In each case, once the flawed program is loaded, it can be used to read or write kernel memory it should never touch, corrupting kernel state or escalating a local user’s privileges to root.
Real-World Cases
CVE-2026-64036 (CVSS 7.8, published July 2026) sits in the css_rstat_updated() cgroup-rstat kfunc, which is reachable from BPF programs. It never validated a caller-supplied CPU index, allowing a local attacker with CAP_BPF and CAP_PERFMON to trigger an out-of-bounds per-CPU memory access. It affects kernels 6.1 and later and is fixed in 6.18.34, 7.0.11, and 7.1. This is a kfunc validation bug rather than a classic verifier-math bug, reflecting a newer and growing category of risk as the BPF kfunc surface expands.
CVE-2026-31525 (CVSS 7.8, NVD-published April 2026) is a verifier/runtime mismatch in the BPF interpreter’s signed 32-bit divide and modulo handling (sdiv32/smod32). The kernel’s internal abs() macro produces undefined behavior when an operand equals S32_MIN, so the verifier’s model of the operation’s possible outputs diverges from what actually executes at runtime. Notably, this one was caught during kernel release candidates (7.0-rc1 through rc4) rather than shipping in a stable release.
CVE-2021-3490 is an older but instructive example: ALU32 bounds tracking for bitwise AND/OR/XOR failed to correctly update 32-bit value ranges, a bug introduced across two separate kernel changes (5.7-rc1 and 5.10-rc1) and eventually fixed in 5.13-rc4 with backports to earlier stable branches. When unprivileged BPF loading was still commonly allowed, this bug gave an unprivileged local user a path to arbitrary kernel read/write and full privilege escalation. CVE-2017-16995, an earlier pointer-mismanagement bug, is generally cited as the founding example of this “verifier logic bug leads to privilege escalation” class.
Who and What Is Affected
These vulnerabilities require the ability to load a BPF program in the first place โ meaning CAP_BPF, CAP_PERFMON, or CAP_SYS_ADMIN on the local system. That capability requirement is the single biggest factor shaping real-world risk: on systems where unprivileged BPF loading is disabled, none of the CVEs above are reachable by an unprivileged user. Affected surface spans a wide range of kernel versions (6.1+ for the kfunc bug, various 5.x and early 7.0-rc kernels for the others), which matters for any organization running self-managed Linux servers, container hosts, or cloud instances with custom kernels.
Detection
Because verifier bugs are subtle mismatches between a static model and runtime behavior, they are difficult to catch through code review alone. The most effective detection approach is fuzzing the BPF interpreter against the verifier’s own bounds model โ continuously feeding it edge-case programs and comparing predicted safety against actual execution, an approach used by BRF-style eBPF runtime fuzzers. Favor kernels and distributions that run this kind of continuous fuzzing upstream.
At runtime, eBPF-based observability tools can help spot exploitation attempts: Falco can alert on unusual bpf() syscall usage or unexpected kprobe/fentry/fexit attachments, while Tetragon or KubeArmor can enforce blocking policies using BPF_PROG_TYPE_LSM programs. It’s also worth periodically auditing loaded BPF programs and maps with bpftool prog list and bpftool map list against an expected allowlist โ a malicious BPF program can hook the very same kernel functions your security tooling depends on and filter events before they reach userspace, so don’t rely on a single eBPF-based sensor for critical detections.
Mitigation
The most impactful and broadly applicable mitigation is disabling unprivileged BPF loading entirely. Since around December 2021, kernel.unprivileged_bpf_disabled=1 has been the default on SUSE, Ubuntu (5.13+, 20.04 HWE, 21.10+), and other major distributions, which blocks non-root or non-CAP_BPF users from loading any BPF program โ closing off both the verifier bugs above and JIT heap-spray-style attacks. Confirm this setting with sysctl kernel.unprivileged_bpf_disabled; note that once set to 1 at runtime, it cannot be cleared without a reboot, which is a useful property for hardened systems.
Beyond that baseline, keep capabilities tightly scoped โ CAP_BPF, CAP_PERFMON, and CAP_SYS_ADMIN should only be granted to processes and users that genuinely need to load BPF programs, since every recent CVE discussed here required one of these capabilities to reach the vulnerable code path. Patch promptly, and track kfunc-specific advisories in addition to classic verifier CVEs, since the kfunc surface is a newer and expanding area of risk that doesn’t get the same scrutiny as the core verifier logic.
Finally, be skeptical of secondary sources when researching eBPF vulnerabilities. During research for this article, a widely-circulated claim about a “CVE-2025-0009 eBPF type-confusion” bug attributed to Project Zero turned out to be fabricated โ the real CVE-2025-0009 is an unrelated AMD advisory โ alongside a set of invented “zero-day” IDs that appear to be AI-generated content. Always cross-check CVE identifiers against NVD or kernel.org before acting on them.
Takeaway
The eBPF verifier is a powerful but imperfect gatekeeper, and its historical bugs โ from bounds-tracking errors to verifier/runtime mismatches to kfunc validation gaps โ consistently require local privileged access to exploit. Disabling unprivileged BPF, tightly scoping BPF-related capabilities, staying current on patches, and layering runtime observability are the practical defenses that neutralize nearly every publicly known verifier bypass to date.
Sources
- CVE-2026-64036: Fix Linux eBPF Kernel Out-of-Bounds Flaw โ Windows Forum
- Linux Kernel BPF Flaw CVE-2026-64036 Exposes Local Attack Paths โ windowsnews.ai
- NVD: CVE-2026-64036
- SentinelOne Vulnerability DB: CVE-2026-31525
- SentinelOne Vulnerability DB: CVE-2021-3490 and CVEdetails: CVE-2021-3490
- eBPF verifier โ The Linux Kernel documentation
- Unprivileged bpf() โ LWN.net, SUSE Security Hardening KB, Ubuntu Discourse announcement
- ARMO: Best eBPF Security Solutions for Kubernetes & Runtime Protection
- systemshardening.com: Detecting and Containing eBPF-Based Rootkits
- NVD: CVE-2025-0009 โ cited only to debunk a fabricated eBPF claim circulating under this ID
Leave a Reply