CVE-2025-21852 - Kernel NULL Pointer Dereference in BPF Tracepoint (`trace_kfree_skb`) – Explained & Mitigated

---

The Linux kernel is the backbone of countless systems. Its flexibility—especially via eBPF—brings power but also risk. One such risk was recently patched: CVE-2025-21852, a critical bug that could let an attacker trigger a NULL pointer dereference through a specially crafted BPF program using the trace_kfree_skb tracepoint.

This article explains the vulnerability, shows the affected code flow, how it could be abused, and how the fix works. It’s written for developers and security folks who want a plain-English, hands-on understanding.

📌 Quick Summary

- Bug: BPF program could access a NULL pointer (via rx_sk) when attaching to the kfree_skb tracepoint, causing kernel crashes (DoS).

Impact: Unprivileged user could crash the kernel, affecting system availability.

- Exploit: Write a BPF program that skips NULL checks on the third argument to the BPF tracepoint hook.
- Fix: Mark the tracepoint argument as "maybe NULL" so the BPF verifier blocks unsafe programs at load time.

🕳️ The Vulnerability: BPF, Tracepoints, and NULL Pointers

Background:
BPF (Berkeley Packet Filter) programs can hook into tracepoints in the kernel, giving deep observability and control. Each tracepoint passes several arguments—sometimes, those can be NULL.

What happened:
A 2023 commit (c53795d48ee8) introduced an argument called rx_sk (a pointer to struct sock) to the trace_kfree_skb tracepoint.
But sometimes, no socket exists for that packet—passing NULL to BPF handlers attached to this tracepoint.

The BPF program:
A BPF program that fails to check for NULL can end up dereferencing the pointer. When run in the kernel, this leads to:

🧨 Example Exploit: Crashing the kernel with BPF

To show how the bug worked before the fix, here’s a minimal example of a BPF program that can TRIGGER the NULL pointer dereference.

// kfree_skb_sk_null.bpf.c

SEC("tracepoint/kfree_skb")
int drop(struct sk_buff *skb, void *location, struct sock *sk)
{
    // No NULL check on sk!
    bpf_printk("sk: %d, %d\n", sk, sk->__sk_common.skc_family);
    return ;
}

*Explanation:*
The line sk->__sk_common.skc_family will crash if sk is NULL.

You can attach this BPF program (via libbpf, bcc, or similar), and trigger the tracepoint (e.g., by dropping packets). If the third argument is NULL (which happens in normal kernel operation), the dereference triggers an OOPS.

Kernel Crash Log Example

BUG: kernel NULL pointer dereference, address: 000000000000001
PF: supervisor read access in kernel mode
RIP: 001:bpf_prog_XXXXX_drop+x10/x2d
Call Trace:
 bpf_trace_run4+x68/xd
 sk_skb_reason_drop+x90/x120
 unix_stream_connect+x1f4/x6f
 __sys_connect+x7f/xb
 ...

The upshot:
Anyone able to load this BPF program (non-root, but with CAP_BPF—sometimes exposed in container environments) could crash the entire system.

🛡️ The Fix: BPF Type System to the Rescue

What’s new:
The kernel now tells the BPF verifier that struct sock *sk (the third argument) to this tracepoint can be NULL.

Technical fix:

// (Kernel commit message)
Let's add kfree_skb to raw_tp_null_args[] to let the BPF verifier
validate such a prog and prevent the issue.

That means:
- The BPF verifier will fail to load any program that dereferences the third argument without a NULL check.

Verifier Error Example

libbpf: prog 'drop': -- BEGIN PROG LOAD LOG --
: R1=ctx() R10=fp
; int BPF_PROG(drop, struct sk_buff *skb, void *location, @ kfree_skb_sk_null.bpf.c:21
: (79) r3 = *(u64 *)(r1 +24)
func 'kfree_skb' arg3 has btf_id 5253 type STRUCT 'sock'
1: R1=ctx() R3_w=trusted_ptr_or_null_sock(id=1)
; bpf_printk("sk: %d, %d\n", sk, sk->__sk_common.skc_family); @ kfree_skb_sk_null.bpf.c:24
1: (69) r4 = *(u16 *)(r3 +16)
R3 invalid mem access 'trusted_ptr_or_null_'
...
-- END PROG LOAD LOG --

In short: unsafe programs are now blocked before they can ever run.

Are you vulnerable?

- If you run kernel versions with commit c53795d48ee8 (rx_sk added to the tracepoint) without the recent fix,

What to do

- Upgrade to a kernel version with the patch for CVE-2025-21852.

Restrict loading of BPF programs to trusted users.

## ⭐ Exclusive Insights / Takeaways

- BPF Verifier is core to security: It prevents many classes of kernel bugs and privilege escalations.
- Tracepoint argument changes must be guarded by proper type annotations, especially with pointers that may be NULL.
- Just because a field can be present doesn’t mean it always is. BPF programs must diligently check for NULL.
- Container runtimes sometimes grant BPF privileges for observability—making kernel checks like these non-theoretical.

🔗 References

- [kernel.org Commit: net: Add rx_skb of kfree_skb to raw_tp_null_args[]](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f1bbb05bfa2323b1d086cb07d6ca4deffcc2eb)
- Original BPF Type Nullability Enhancements
- CVE-2025-21852 NVD Entry
- Linux BPF Documentation

🏁 Conclusion

CVE-2025-21852 is a classic example where a tiny type annotation has a huge security impact. The fix doesn’t only patch a crash—it prevents a class of BPF bugs involving optional pointers reaching into the deepest parts of the Linux kernel.

Timeline

Published on: 03/12/2025 10:15:17 UTC
Last modified on: 03/24/2025 15:41:23 UTC