An out-of-process reader for the polar signals custom-labels Thread Local Storage Format, as well as our own TLS v2 proposal, as well as the OTEP-4719 Process Context Format.
When you launch the process pointed at a particular PID, it loads the processes maps from /proc/<pid>/maps, hunts
around for process context mappings, as well as custom-labels v1 and v2 TLS variables.
It then loops forever, printing out any v1 and v2 labels on the process threads periodically.
It needs:
- Linux
- ELF binaries
- ARM64 or X86-64
- Root or
CAP_SYS_PTRACE- required forprocess_vm_readvto read process context and TLS from the target process. On Ubuntu/Debian, the default Yama ptrace scope (kernel.yama.ptrace_scope=1) restricts this to child processes only.
The tool supports two reading modes:
context-reader <pid> --mode ptraceUses ptrace to attach to threads and read TLS. More compatible but stops threads briefly.
context-reader <pid> --mode ebpfUses eBPF perf events to read TLS on CPU samples. Lower overhead, doesn't stop threads.
Requirements for eBPF mode:
- Linux kernel 5.8+ with BTF support
CAP_BPFandCAP_PERFMONcapabilities (or root)- Build the eBPF program first (see below)
cargo build --releaseFirst, install the Rust BPF toolchain:
rustup target add bpfel-unknown-none
cargo install bpf-linkerThen build the eBPF program and userspace:
# Build eBPF program
cd ebpf && cargo build --release && cd ..
# Build userspace
cargo build --releaseAt startup, we hunt for configuration for the two label formats, and then having found at least one of them, read them out of the running process.
- We use procfs to read the current threads out of the process
- For each thread:
- Use
ptraceto attach to the thread, and wait for it to stop - Read the pointer to the current label set out of the TL location we discovered at startup
- If it's not
0, we've got thread labels!- Use
process_vm_readvvia the nix crate to read out our TL, and then chase the pointers back. - Print it to screen!
- Use
- Use
- Uses Aya to load an eBPF program
- The eBPF program attaches to
perf_event(CPU clock sampling) - On each sample, if the current process matches our target PID:
- Read the thread pointer from
task_struct - Compute TLS addresses using the same logic as ptrace mode
- Read labelset/record pointers using
bpf_probe_read_user() - Emit raw data to userspace via ring buffer
- Read the thread pointer from
- Userspace parses the raw data and chases remaining pointers with
process_vm_readv