Concept familiarization
Reading event data
In the previous exercise, we used a helper to ask “Who is running?”.
Now let’s answer questions about the running process by querying the data available to the tracepoint.
For sched_process_exec, the kernel provides a structure with specific event data, like the Process ID (PID) and the filename.
You can find the format of the context that the kernel provides on any Linux computer, by running:
$ cat /sys/kernel/tracing/events/sched/sched_process_exec/format
format:
unsigned short common_type; offset:0; size:2;
unsigned char common_flags; offset:2; size:1;
unsigned char common_preempt_count; offset:3; size:1;
int common_pid; offset:4; size:4;
__data_loc char[] filename; offset:8; size:4;
pid_t pid; offset:12; size:4;
pid_t old_pid; offset:16; size:4;
Alternatively, Ctrl+Click on the trace_event_raw_sched_process_exec in the editor to see the definition.
The challenge
Extract the PID and the filename of the program being executed, directly from the event data.
Getting the PID is straightforward in this case, we can just read it:
int pid = ctx->pid;
DEBUG_NUM("PID", pid);
Reading the filename, however, is more involved. If you look at the type declaration above,
it is __data_loc char[].
This is not a regular pointer; the __data_loc prefix means the field just contains the encoded location of the string, not the string itself.
Basically, the filename field contains a 32-bit integer that packs two pieces of information:
- Lower 16 bits: The Offset (How far away the string is from the start of the structure)
- Upper 16 bits: The Length (How long the string is)
To get the actual string, we need to decode its location:
- Read the
__data_loc_filenamefield - Mask the lower 16 bits (
val & 0xFFFF) to get the offset - Right shift the upper bits (
val >> 16) to get the length - Add that offset to the
ctxpointer
However, we can’t really read the data directly from the calculated address:
char* fname = (void *)ctx + off;
DEBUG_STR("Filename", fname);
Why not?
As we saw in the intro, eBPF programs run in a sandbox. Calculated addresses like this one (ctx + off) point to Kernel memory (not the program’s stack).
When we call DEBUG_STR with a non-local address, the kernel helpers will refuse to read from that memory range, leaving the “DEBUG” memory uninitialized.
You can try it though! It will not crash the kernel, instead, it will return something like: �����.
To solve this, we need to copy that data to “local memory” (stack) using the bpf_probe_read_kernel_str helper.
char fname[32];
bpf_probe_read_kernel_str(fname, sizeof(fname), (void *)ctx + off);
After this, the fname buffer will be populated, and we can call DEBUG_STR on it.
Your task
If you added DEBUG_STR/DEBUG_NUM as explained above, you should see multiple standard programs, but there’s one that stands out. Use SUBMIT_STR_LEN(buff, len) to submit it.
Compare two strings for equality
- Args:
char* bufdynamic buffer to compareu32 buf_szlength of dynamic bufferconst char* buf2literal string to compare against