Introduction
Intro to eBPF
eBPF is a Linux mechanism that allows you to hook custom code directly into kernel events.
When an event triggers, the kernel pauses its normal execution, runs your eBPF program, and then continues. This happens at the kernel level, giving you visibility into system behavior that would be impossible from user space alone.
Examples of events you can hook:
- A process opens a file
- A program is executed
- A network packet arrives
This makes eBPF powerful for observability, security monitoring, and network filtering without modifying the kernel itself or loading kernel modules.
Program types
eBPF programs are categorized by where they attach in the kernel. These are the most common:
Tracepoints
Tracepoints are hook points defined throughout the kernel code. They expose almost all kernel events, such as process scheduling, file operations, network operations and system calls.
Kprobes
Kprobes attach to any kernel function by name. Unlike tracepoints, they’re not part of the kernel’s stable API - function names and signatures can change between versions.
Kprobes provide access to kernel internals that aren’t exposed through tracepoints, allowing deeper inspection of kernel behavior.
XDP
XDP programs attach at the network driver level, processing packets before they reach the kernel’s network stack.
XDP programs run so early that they can drop or redirect packets with minimal overhead.
The verifier
If you could run arbitrary code in the kernel, you could crash the system, leak sensitive data, or create infinite loops that hang the machine.
The eBPF verifier prevents this by analyzing your program before it runs and enforcing strict safety rules.
As you work through the exercises, you’ll encounter verifier errors. While these errors can be cryptic, they always point to something the verifier couldn’t prove was safe. Learning to read these errors is part of learning eBPF.
Guarantee of termination
Programs must be guaranteed to terminate. To enforce this, the verifier imposes limits:
- Programs have a maximum complexity (counted as “instructions processed” by the verifier)
- Loops must have a fixed upper bound that the verifier can verify at load time
- To prevent infinite recursion, helper functions can’t call back into your program
Memory safety
All memory access must be safe:
- You can’t do pointer arithmetic or dereference arbitrary addresses
- Reading kernel or user memory requires special helpers (
bpf_probe_read_kernel,bpf_probe_read_user) - Array access must be bounds-checked before use
- Pointers from map lookups must be checked for NULL
The verifier analyzes all branches in your code and will reject programs where any branch path can’t be proven safe.