On June 3rd, 2024, a subtle but critical vulnerability was patched in the Linux kernel BPF (Berkeley Packet Filter) verifier. This bug (now tracked as CVE-2024-53125) affects the kernel's ability to correctly verify BPF program safety, specifically around how sub-registers are tracked and copied between registers in JITed (just-in-time) compiled code. If unpatched, this could lead to unpredictable behavior in BPF programs, exposing the system to security or stability risks if malicious or buggy code executes.
In this post, we'll explain what happened—with code snippets, how the bug could be exploited, and why the fix matters. We'll also provide links to source references for further reading.
The Bug: Mis-handled Sub-Register Definition
BPF programs run inside the Linux kernel with strict verification: every register access must be tracked for size, sign-extension, etc., to avoid user-supplied code from breaking kernel security.
Under certain conditions—specifically when the BPF_F_TEST_RND_HI32 flag is set—the kernel's eBPF verifier was not correctly copying the "subreg_def" flag in register state during instruction rewriting. That meant *zero-extension* was skipped for some 32-bit writes, leading to potential leakage of 32 high-bits from uninitialized memory or attackers having partial control over random 32 high-bits.
The Problematic Code Path
The sequence below shows how the verifier might transform a BPF program *incorrectly* because of this bug:
: call bpf_ktime_get_ns
1: r &= x7fffffff
2: w1 = w
3: if w < 10 goto +
4: r1 >>= 32
5: r = r1
6: exit
With BPF_F_TEST_RND_HI32 set, the verifier *should* ensure that any 32-bit (w1) register assignment zeroes the upper 32 bits of its 64-bit parent register (r1). Instead, because the flag tracking (subreg_def) was *lost*, this zero-extension was missing.
The result? r1's high 32 bits become random (filled by the kernel purposely for fuzzing). Any subsequent 64-bit operation or read on r1 could operate on a misleading value.
Exploit Possibilities
While this isn't directly a privileged escalation bug, attackers could craft BPF programs that depend on register content, causing them to misbehave or leak random data.
For example, if a BPF program reads kernel memory directly *or* leaks the high 32 bits (which the verifier thinks are always zero), it could:
Crash the host due to undefined behavior.
The bug becomes especially severe if the BPF program's purpose is to manipulate low-level kernel state (as often in observability, performance, or security monitoring context).
Here is pseudo-code illustrating the effect
int vulnerable_func(void *ctx) {
// r = bpf_ktime_get_ns(); // Kernel helper
register unsigned long long t = bpf_ktime_get_ns();
t &= x7fffffff; // Mask to 31 bits
unsigned int w1 = (unsigned int)t;
if (w1 < 10)
; // no-op
w1 >>= 32; // Should always be zero
return w1; // But with bug, could be nonzero due to random hi bits!
}
If the zero extension isn’t enforced, w1's upper bits may contain garbage (here: random values). On some architectures, this could be observable.
The Fix: Preserve subreg_def During Register State Copy
The patch (original reference: 2024 BPF Patch for subreg_def) amends the sync_linked_regs() code:
Before:
copy_register_state() would overwrite the destination's subreg_def field—losing the necessary tracking for zero-extension logic.
After:
The patch ensures the subreg_def property is *also* copied, so the verifier can apply zero extension correctly to any sub-register assignment.
Key Diff (Pseudo-patch)
- copy_register_state(dst_reg, src_reg);
+ copy_register_state(dst_reg, src_reg);
+ dst_reg->subreg_def = src_reg->subreg_def; // <<< Fix: preserve subreg_def!
This single line keeps the vital tracking info intact throughout instruction rewrites and range propagations.
Impact and Who’s Affected
- All Linux kernels with eBPF verifier (as shipped since kernel 4.x) are potentially affected if BPF_F_TEST_RND_HI32 behavior or similar register propagations are used.
- Custom BPF code in tracing, monitoring, or filtering tools may (rarely) trip over this bug, creating hard-to-reproduce misbehaviors.
- Public clouds or environments allowing unprivileged BPF JIT have latent risk, as a rogue user might try to craft code reading high 32 bits.
Patch commit:
bpf: sync_linked_regs() must preserve subreg_def
BPF Verifier Internals:
CVE Record:
CVE-2024-53125 at Mitre (should populate soon)
Linux commit discussion:
Conclusion
CVE-2024-53125 is a textbook example of how subtle, deep-kernel register bookkeeping errors can break important security or stability guarantees. While it’s not a direct escalation vector, it weakens assumptions about BPF verifier safety and (in rare cases) could be chained with other logic.
If you're running custom or third-party eBPF code, patch your kernel!
If you care about kernel fuzzing quality or reliability, this fix is crucial.
Stay up to date by following LKML or modern BPF development (Cilium blog) for more such findings.
Timeline
Published on: 12/04/2024 14:15:20 UTC
Last modified on: 11/03/2025 21:17:28 UTC