Tech News
← Back to articles

Bouncing on trampolines to run eBPF programs

read original related products more articles

This blog post is the second installment in our eBPF blog post series, following our blog post about eBPF selftests.

As eBP F is more and more used in the industry, eBPF kernel developers give considerable attention to eBPF performance: some standard use cases like system monitoring involve hundreds of eBPF programs attached to events triggered at high frequencies. It is then paramount to keep eBPF programs execution overhead as low as possible. This blog post aims to shed some light on an internal eBPF mechanism perfectly showcasing those efforts: the eBPF trampoline.

eBPF tracing programs

eBPF tracing programs regroup a whole variety of eBPF programs aiming to allow users to monitor the kernel execution and internals, making it a very important category for monitoring and security tools. There are multiple program types falling in this category, defining the events triggering the programs execution: kprobe programs (allowing to hook virtually anywhere in the kernel), tracepoint programs and raw tracepoint programs (targeting defined tracepoint instrumented in the kernel source code), perf event programs (targeting software and hardware perf events), pure tracing programs, etc. This last category can be hooked to different attach types:

fentry programs allow to hook a program at the beginning of a kernel function, allowing for example to record the function arguments

programs allow to hook a program at the beginning of a kernel function, allowing for example to record the function arguments fexit programs allow to hook a program at the end of a kernel function, allowing for example to record the function return value

programs allow to hook a program at the end of a kernel function, allowing for example to record the function return value modify return programs go even further and allow to “bypass” kernel functions and replace those with a custom return value.

programs go even further and allow to “bypass” kernel functions and replace those with a custom return value. iterator programs allow to quickly iterate over a family of kernel objects (eg: all the task_structs), skipping the cost of crossing the kernel-userspace boundary to get data about each of those items.

To get a basic example of such a program, let’s assume that we want to know about any attempt to open a file on our system. We can develop a small monitoring tool fulfilling this need by hooking an eBPF program to the openat2 system call entry, and retrieve the passed path argument each time the syscall is executed:

#include #include #include SEC("fentry/do_sys_openat2") int BPF_PROG(trace_files, int dfd, const char *filename, void *how) { int pid = bpf_get_current_pid_tgid() & 0xFFFFFFFF; char fmt[] = "Process %d tried to open file %s"; bpf_trace_printk(fmt, sizeof(fmt), pid, filename); return 0; } char _license[] SEC("license") = "GPL";

... continue reading