CVE-2023-52920 relates to a vulnerability in the Linux kernel's eBPF (extended Berkeley Packet Filter) verifier, specifically around how the verifier tracks register spill/fill to and from the stack for precision tracking. This flaw could allow attackers to trick the verifier, potentially opening up avenues for denial of service or privilege escalation if exploited in the right context.
You can find the original patch and further details here:
Kernel Patch: support non-r10 register spill/fill to/from stack in precision tracking
Background
eBPF programs are loaded into the Linux kernel and get verified by the kernel verifier before execution. The verifier analyzes every possible state to guarantee these programs won't crash the kernel or read/write out of bounds. BPF programs often spill (save) register contents onto the stack and fill (restore) them back from the stack. Historically, precision tracking logic was somewhat limited around cases where non-r10 registers were spilled/filled after copying from r10 (the stack pointer).
Attackers could potentially exploit these limitations for manipulation or bypass of safety checks.
What Was the Problem?
- The BPF verifier historically tracked register spills/fills only when using r10 (the stack pointer).
- An attacker could copy r10 to another register and use it to spill/fill to/from the stack with an offset.
- Previous logic failed to properly account for this, resulting in incorrect state tracking (precision info).
- This could let attackers confuse the verifier into losing track of what’s stored where, breaking expected program safety.
The comments from the Linux patch summarize the fix
> Use instruction (jump) history to record instructions that performed register spill/fill to/from stack, regardless if this was done through read-only r10 register, or any other register after copying r10 into it *and* potentially adjusting offset.
The patch basically enhanced the instruction (jmp) history to track more precise information
- Stack slot index (spi) and stack frame number are now encoded in extra flags within instruction history.
- This allows backtracking logic to *always* know which spills/fills happened and where, for any register, not just r10.
- The data structure looks like this (pseudo-code)
struct bpf_jmp_history_entry {
u32 insn_idx; // Index of the instruction
u32 prev_idx; // Index of previous jump
u16 flags; // NEW: Stack slot info & frame tracking
// ... other fields
};
- The use of the flags field lets the verifier remember *exactly* which stack slot/frame was involved in the stack operation, even for complex cases.
Here’s a hypothetical simplified eBPF pseudo-code that could try to abuse the old logic
// Copy r10 (stack pointer) to r1, modify it, use it for spill/fill
r1 = r10 // Copy stack pointer
r1 -= x10 // Adjust to different stack frame offset
// Spill r2 onto stack, but using r1 now
*(u64 *)(r1) = r2
// ... later: restore from that stack slot
r3 = *(u64 *)(r1)
// If the verifier doesn't track this properly, it could lose precision info on r3!
With the Patch
Now, the verifier records that even this "indirect" stack access is tracked and backtracked correctly.
Security Impact
- Before the fix: Malicious BPF programs could confuse the precision tracking logic, potentially leading to verifier bypass.
- After the fix: All stack spills and fills are tracked, including when using non-r10 registers, closing the loophole.
The patch reduces the number of verified states and improves verification efficiency as evident from selftest results (see below).
Brief table from patch
| File | Program | Insns (A) | Insns (B) | Insns (Δ) | States (A) | States (B) | States (Δ) |
|-------------------------------------------|---------------|-----------|-----------|-----------|------------|------------|------------|
| test_cls_redirect_dynptr.bpf.linked3.o | cls_redirect | 2987 | 2864 | -123 | 240 | 231 | -9 |
| xdp_synproxy_kern.bpf.linked3.o | syncookie_tc | 82848 | 82661 | -187 | 5107 | 5073 | -34 |
| xdp_synproxy_kern.bpf.linked3.o | syncookie_xdp | 85116 | 84964 | -152 | 5162 | 513 | -32 |
This means not only better verification, but *faster* BPF program loads and less work done by the kernel verifier.
References
- Kernel patch: support non-r10 register spill/fill to/from stack in precision tracking
- BPF Verifier documentation
- CVE-2023-52920 at NVD *(link may become available once public)*
Summary
CVE-2023-52920 highlights the importance of strict and precise state tracking in the kernel eBPF verifier. The fix makes sure every stack operation—no matter how registers are manipulated—gets tracked, closing a subtle but dangerous loophole in the precision logic. Keeping your kernel updated ensures protection against such flaws, especially if you’re running BPF workloads or allowing user-supplied eBPF code.
Keep your kernels updated and stay tuned for further security improvements! If you want to dig in, review the patch details here.
Timeline
Published on: 11/05/2024 10:15:24 UTC
Last modified on: 11/07/2024 19:31:31 UTC