From 1a6824a3fcab674eecba7e1a2f199d85c431e136 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 26 Jan 2024 07:41:57 +0000 Subject: [PATCH] tetragon: Harden loader sensor Making the loader sensor to send data for non excutable mmaps as well to increase the chance that we might be able to read build id for the binary at least from one of them. As we can't sleep at the current loader probed function, we depend on the page with build id being swapped-in, which might not be always the case. By allowing to send build id data for mmap non executable mmap events we increase the chance of getting the build id data for binary. Signed-off-by: Jiri Olsa --- bpf/process/bpf_loader.c | 13 ++++------ pkg/sensors/tracing/loader.go | 46 ++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/bpf/process/bpf_loader.c b/bpf/process/bpf_loader.c index 2fcb9c08f2d..332fc289bfd 100644 --- a/bpf/process/bpf_loader.c +++ b/bpf/process/bpf_loader.c @@ -67,9 +67,7 @@ loader_kprobe(struct pt_regs *ctx) struct perf_mmap_event *mmap_event; struct execve_map_value *curr; struct task_struct *current; - struct vm_area_struct *vma; struct msg_loader *msg; - unsigned long vm_flags; struct perf_event *pe; __u64 *id_map, id_pe; const char *path; @@ -110,16 +108,15 @@ loader_kprobe(struct pt_regs *ctx) mmap_event = (struct perf_mmap_event *)PT_REGS_PARM2_CORE(ctx); - vma = BPF_CORE_READ(mmap_event, vma); - vm_flags = BPF_CORE_READ(vma, vm_flags); - - /* We are interested only in exec maps. */ - if (!(vm_flags & VM_EXEC)) + /* Send all events with valid build id, user space will sort + * out duplicates. + */ + msg->buildid_size = BPF_CORE_READ(mmap_event, build_id_size); + if (!msg->buildid_size) return 0; probe_read(&msg->buildid[0], sizeof(msg->buildid), _(&mmap_event->build_id[0])); - msg->buildid_size = BPF_CORE_READ(mmap_event, build_id_size); path = BPF_CORE_READ(mmap_event, file_name); len = probe_read_str(&msg->path, sizeof(msg->path), path); diff --git a/pkg/sensors/tracing/loader.go b/pkg/sensors/tracing/loader.go index eee6b7370a7..1bd0aada7ea 100644 --- a/pkg/sensors/tracing/loader.go +++ b/pkg/sensors/tracing/loader.go @@ -29,6 +29,7 @@ import ( "bytes" "encoding/binary" "fmt" + "log" "syscall" "unsafe" @@ -44,9 +45,19 @@ import ( "github.com/cilium/tetragon/pkg/sensors/program" "github.com/cilium/tetragon/pkg/strutils" "github.com/cilium/tetragon/pkg/tracingpolicy" + lru "github.com/hashicorp/golang-lru/v2" "golang.org/x/sys/unix" ) +type cacheKey struct { + Pid uint32 + Path string +} + +const ( + cacheSize = 1024 +) + var ( loader = program.Builder( "bpf_loader.o", @@ -59,6 +70,8 @@ var ( idsMap = program.MapBuilder("ids_map", loader) loaderEnabled bool + + cache *lru.Cache[cacheKey, bool] ) type loaderSensor struct { @@ -73,6 +86,18 @@ func init() { sensors.RegisterPolicyHandlerAtInit(loader.name, loader) observer.RegisterEventHandlerAtInit(ops.MSG_OP_LOADER, handleLoader) + + var err error + cache, err = lru.NewWithEvict( + cacheSize, + func(_ cacheKey, _ bool) { + // not used at the moment, let's see if we want to add metric for this + }, + ) + if err != nil { + log.Fatalf("loader init failed with %v", err) + } + } func GetLoaderSensor() *sensors.Sensor { @@ -110,7 +135,11 @@ func createLoaderEvents() error { Type: unix.PERF_TYPE_SOFTWARE, Config: unix.PERF_COUNT_SW_BPF_OUTPUT, Sample_type: unix.PERF_SAMPLE_RAW, - Bits: unix.PerfBitMmap | unix.PerfBitMmap2 | bpf.PerfBitBuildId, + + // Enable all possible perf mmap events to increase the possibility + // we get valid build id data for the binary. + Bits: unix.PerfBitMmap | unix.PerfBitMmap2 | bpf.PerfBitBuildId | + unix.PerfBitMmapData, } nCpus := bpf.GetNumPossibleCPUs() @@ -156,6 +185,15 @@ func (k *loaderSensor) LoadProbe(args sensors.LoadProbeArgs) error { return nil } +func inCache(pid uint32, path string) bool { + key := cacheKey{pid, path} + _, ok := cache.Get(key) + if !ok { + cache.Add(key, true) + } + return ok +} + func handleLoader(r *bytes.Reader) ([]observer.Event, error) { m := tracingapi.MsgLoader{} err := binary.Read(r, binary.LittleEndian, &m) @@ -166,6 +204,12 @@ func handleLoader(r *bytes.Reader) ([]observer.Event, error) { path := m.Path[:m.PathSize-1] + // We can get multiple entries for given pid/path, + // check if we already processed it + if inCache(m.Pid, string(path[:])) { + return nil, nil + } + msg := &tracing.MsgProcessLoaderUnix{ Msg: &m, Path: strutils.UTF8FromBPFBytes(path),