Stateful eBPF
Reading syscall buffers
In the previous exercise, we learned how to use maps to share data between multiple eBPF programs.
Now let’s apply this to capturing data from syscall buffers. Syscalls have two tracepoint events: enter (before it starts) and exit (after it finishes). We’ll need to correlate data between both.
The read syscall
Looking at the read syscall:
ssize_t read(int fd, void *buf, size_t count);
If we hook into sys_enter_read and sys_exit_read:
- At entry: we have access to the arguments, but the buffer is still empty.
- At exit: the buffer is filled with data, but we only have access to the return value.
To correlate both, we capture the buffer pointer at entry using a map, then read the filled buffer at exit.
Storing the buffer address at entry
First, we need to save the buffer pointer when the syscall starts. The buffer address is in ctx->args[1].
We’ll store this address in a map, using the PID as the key so we can look it up later:
u64 buf_addr = ctx->args[1];
bpf_map_update_elem(&read_buffers, &pid, &buf_addr, BPF_ANY);
Note that we’re storing the address of the buffer as a u64 value.
Reading the buffer at exit
Now in the exit program, we need to retrieve and read the buffer that was filled by the syscall.
First, check if the read succeeded:
size_t count = ctx->ret;
if (count <= 0) return 0;
The return value ctx->ret tells you how many bytes were read.
Next, look up the saved buffer address using the PID as the key:
u64 *buf_ptr = bpf_map_lookup_elem(&read_buffers, &pid);
if (!buf_ptr) return 0;
This returns a pointer to the stored value (u64*).
To read the buffer, create a local buffer and use bpf_probe_read_user. You’ll need to dereference buf_ptr to get the actual buffer address:
char local_buf[32];
if (count > 32) count = 32;
bpf_probe_read_user(local_buf, count, (void *)*buf_ptr);
Notice the *buf_ptr - we’re dereferencing the pointer to get the buffer address we stored earlier.
The challenge
A program reads a file containing a password. Submit the contents of the entire file with SUBMIT_STR_LEN.
Your task
Complete both the entry and exit programs to capture and submit the buffer contents.
Compare two strings for equality
- Args:
char* bufdynamic buffer to compareu32 buf_szlength of dynamic bufferconst char* buf2literal string to compare against
Read string from user space into kernel buffer
- Args:
void* dstkernel buffer to read intou32 sizemaximum bytes to readconst void* srcuser space pointer to string
Read bytes from user space into kernel buffer
- Args:
void* dstkernel buffer to read intou32 sizebytes to readconst void* srcuser space pointer