Skip to content

Commit

Permalink
tetragon: Harden loader sensor
Browse files Browse the repository at this point in the history
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 <jolsa@kernel.org>
  • Loading branch information
olsajiri committed Jan 26, 2024
1 parent b12bd74 commit 1a6824a
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 9 deletions.
13 changes: 5 additions & 8 deletions bpf/process/bpf_loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
46 changes: 45 additions & 1 deletion pkg/sensors/tracing/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"bytes"
"encoding/binary"
"fmt"
"log"
"syscall"
"unsafe"

Expand All @@ -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",
Expand All @@ -59,6 +70,8 @@ var (
idsMap = program.MapBuilder("ids_map", loader)

loaderEnabled bool

cache *lru.Cache[cacheKey, bool]
)

type loaderSensor struct {
Expand All @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand All @@ -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),
Expand Down

0 comments on commit 1a6824a

Please sign in to comment.