From 291db9aebc7d07e2b227e0c4f67a5e9a4adb4044 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 16 Aug 2022 12:45:01 +0200 Subject: [PATCH 1/6] tetragon: Add GetDefaultSensorsWithFile function Adding GetDefaultSensorsWithFile function to return sensors instead of observer object. It will be used in following tests. Signed-off-by: Jiri Olsa --- pkg/observer/observer_test_helper.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/pkg/observer/observer_test_helper.go b/pkg/observer/observer_test_helper.go index 58060ab2d80..8b1d3cbf187 100644 --- a/pkg/observer/observer_test_helper.go +++ b/pkg/observer/observer_test_helper.go @@ -252,8 +252,8 @@ func readConfig(file string) (*yaml.GenericTracingConf, error) { return cnf, nil } -func getDefaultObserver(t *testing.T, ctx context.Context, base *sensors.Sensor, opts ...TestOption) (*Observer, error) { - var sens []*sensors.Sensor +func getDefaultObserverSensors(t *testing.T, ctx context.Context, base *sensors.Sensor, opts ...TestOption) (*Observer, []*sensors.Sensor, error) { + var sens, ret []*sensors.Sensor testutils.CaptureLog(t, logger.GetLogger().(*logrus.Logger)) @@ -280,17 +280,17 @@ func getDefaultObserver(t *testing.T, ctx context.Context, base *sensors.Sensor, var err error sens, err = sensors.GetSensorsFromParserPolicy(&cnf.Spec) if err != nil { - return nil, err + return nil, ret, err } } if err := loadObserver(t, ctx, base, sens, o.observer.notestfail); err != nil { - return nil, err + return nil, ret, err } exportFname, err := testutils.GetExportFilename(t) if err != nil { - return nil, err + return nil, ret, err } saveInitInfo(o, exportFname) @@ -311,9 +311,16 @@ func getDefaultObserver(t *testing.T, ctx context.Context, base *sensors.Sensor, testDone(t, obs) }) + ret = append(sens, base) + obs.perfConfig = bpf.DefaultPerfEventConfig() obs.perfConfig.MapName = filepath.Join(bpf.MapPrefixPath(), "tcpmon_map") - return obs, nil + return obs, ret, nil +} + +func getDefaultObserver(t *testing.T, ctx context.Context, base *sensors.Sensor, opts ...TestOption) (*Observer, error) { + obs, _, err := getDefaultObserverSensors(t, ctx, base, opts...) + return obs, err } func GetDefaultObserverWithWatchers(t *testing.T, ctx context.Context, base *sensors.Sensor, opts ...TestOption) (*Observer, error) { @@ -339,6 +346,12 @@ func GetDefaultObserverWithFile(t *testing.T, ctx context.Context, file, lib str return GetDefaultObserverWithWatchers(t, ctx, b, WithConfig(file), withPretty(), WithLib(lib)) } +func GetDefaultSensorsWithFile(t *testing.T, ctx context.Context, file, lib string) ([]*sensors.Sensor, error) { + b := base.GetInitialSensor() + _, sens, err := getDefaultObserverSensors(t, ctx, b, WithConfig(file), withPretty(), WithLib(lib)) + return sens, err +} + func GetDefaultObserverWithFileNoTest(t *testing.T, ctx context.Context, file, lib string, fail bool) (*Observer, error) { b := base.GetInitialSensor() return GetDefaultObserverWithWatchers(t, ctx, b, WithConfig(file), withPretty(), WithLib(lib), withNotestfail(fail)) From 7311b56ab08d4091eeb2f928de04cace8eb2f2e1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 10 Aug 2022 12:37:19 +0200 Subject: [PATCH 2/6] tetragon: Add LoadedCollection object Adding LoadedCollection object to carry loaded programs/maps for program to be used later for TestLoad* tests to verify loaded programs and maps. Signed-off-by: Jiri Olsa --- pkg/sensors/program/coll.go | 170 +++++++++++++++++++++++++++++++ pkg/sensors/program/loader.go | 55 +++++++--- pkg/sensors/program/program.go | 3 + pkg/testutils/sensors/testrun.go | 4 + 4 files changed, 216 insertions(+), 16 deletions(-) create mode 100644 pkg/sensors/program/coll.go diff --git a/pkg/sensors/program/coll.go b/pkg/sensors/program/coll.go new file mode 100644 index 00000000000..ed53c68860b --- /dev/null +++ b/pkg/sensors/program/coll.go @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package program + +import ( + "errors" + "fmt" + "os" + + "github.com/cilium/ebpf" + "github.com/cilium/tetragon/pkg/logger" +) + +// The idea of LoadedCollection is to keep loaded programs and maps +// from ebpf.Collection to be used later for 'TestLoad*' tests to +// verify loaded programs and maps. +// +// We can't just iterate all existing programs and maps for 2 reasons: +// - we could race with others loading bpf programs +// - we get limited name (16 bytes), which we usually cross with +// our names for maps and programs +// +// The process of loading LoadedCollection is following: +// +// - ebpf collection is loaded +// +// coll := NewCollectionWithOptions +// +// - copy all programs/maps from ebpf collection +// +// copyLoadedCollection(lc, coll) +// +// - collection is removed and only 'used' programs and maps stay loaded +// so we filter out unused programs/maps with (based on their IDs) +// +// load.LC = filterLoadedCollection(lc) +// +// This way we have only programs/maps realted to our test and with +// original long names. +// +// All this is happening only when program.KeepCollection is set true, +// so it's enabled only for testing code. + +var ( + KeepCollection bool +) + +type LoadedMap struct { + ID ebpf.MapID +} + +type LoadedProgram struct { + ID ebpf.ProgramID + MapIDs []ebpf.MapID + Type ebpf.ProgramType +} + +type LoadedCollection struct { + Programs map[string]*LoadedProgram + Maps map[string]*LoadedMap +} + +func newLoadedCollection() *LoadedCollection { + lc := &LoadedCollection{} + lc.Maps = map[string]*LoadedMap{} + lc.Programs = map[string]*LoadedProgram{} + return lc +} + +func printLoadedCollection(str string, lc *LoadedCollection) { + logger.GetLogger().Infof("Programs (%s):", str) + for name, lp := range lc.Programs { + logger.GetLogger().Infof("%d: %s - %v", lp.ID, name, lp.MapIDs) + } + logger.GetLogger().Infof("Maps (%s):", str) + for name, lm := range lc.Maps { + logger.GetLogger().Infof("%d: %s", lm.ID, name) + } +} + +func copyLoadedCollection(coll *ebpf.Collection) (*LoadedCollection, error) { + if coll == nil { + return nil, fmt.Errorf("failed to get collection") + } + lc := newLoadedCollection() + // copy all loaded maps + for name, m := range coll.Maps { + info, err := m.Info() + if err != nil { + return nil, err + } + id, ok := info.ID() + if !ok { + return nil, fmt.Errorf("failed to get id") + } + lm := &LoadedMap{id} + lc.Maps[name] = lm + } + // copy all loaded programs with assigned map ids + for name, p := range coll.Programs { + info, err := p.Info() + if err != nil { + return nil, err + } + id, ok := info.ID() + if !ok { + return nil, fmt.Errorf("failed to get id") + } + mapIDs, ok := info.MapIDs() + if !ok { + return nil, fmt.Errorf("failed to get map ids") + } + lp := &LoadedProgram{ID: id} + lp.MapIDs = mapIDs + lp.Type = p.Type() + lc.Programs[name] = lp + } + return lc, nil +} + +// Gets all the programs/maps and removes any non existent ones +// from passed collection +func filterLoadedCollection(lc *LoadedCollection) *LoadedCollection { + ret := newLoadedCollection() + + // filter out non existing programs + lastProg := ebpf.ProgramID(0) + for { + next, err := ebpf.ProgramGetNextID(lastProg) + if errors.Is(err, os.ErrNotExist) { + break + } + for name, lp := range lc.Programs { + if lp.ID == next { + ret.Programs[name] = lp + } + } + lastProg = next + } + + // filter out non existing maps + lastMap := ebpf.MapID(0) + for { + next, err := ebpf.MapGetNextID(lastMap) + if errors.Is(err, os.ErrNotExist) { + break + } + for name, lm := range lc.Maps { + if lm.ID == next { + ret.Maps[name] = lm + } + } + lastMap = next + } + for _, lp := range lc.Programs { + var mapIDs []ebpf.MapID + + for _, mi := range lp.MapIDs { + for _, lm := range lc.Maps { + if lm.ID == mi { + mapIDs = append(mapIDs, mi) + break + } + } + } + lp.MapIDs = mapIDs + } + return ret +} diff --git a/pkg/sensors/program/loader.go b/pkg/sensors/program/loader.go index f1b92968058..dc163598f25 100644 --- a/pkg/sensors/program/loader.go +++ b/pkg/sensors/program/loader.go @@ -190,27 +190,27 @@ func installTailCalls(mapDir string, spec *ebpf.CollectionSpec, coll *ebpf.Colle return nil } -func loadProgram( +func doLoadProgram( bpfDir string, mapDirs []string, load *Program, withProgram AttachFunc, ci *customInstall, verbose int, -) error { +) (*LoadedCollection, error) { var btfSpec *btf.Spec if btfFilePath := cachedbtf.GetCachedBTFFile(); btfFilePath != "/sys/kernel/btf/vmlinux" { // Non-standard path to BTF, open it and provide it as 'KernelTypes'. var err error btfSpec, err = btf.LoadSpec(btfFilePath) if err != nil { - return fmt.Errorf("opening BTF file '%s' failed: %w", btfFilePath, err) + return nil, fmt.Errorf("opening BTF file '%s' failed: %w", btfFilePath, err) } } spec, err := ebpf.LoadCollectionSpec(load.Name) if err != nil { - return fmt.Errorf("loading collection spec failed: %w", err) + return nil, fmt.Errorf("loading collection spec failed: %w", err) } // Find all the maps referenced by the program, so we'll rewrite only @@ -230,7 +230,7 @@ func loadProgram( } if progSpec == nil { - return fmt.Errorf("program for section '%s' not found", load.Label) + return nil, fmt.Errorf("program for section '%s' not found", load.Label) } pinnedMaps := make(map[string]*ebpf.Map) @@ -287,23 +287,23 @@ func loadProgram( fmt.Println(slimVerifierError(err.Error())) } - return fmt.Errorf("opening collection '%s' failed", load.Name) + return nil, fmt.Errorf("opening collection '%s' failed", load.Name) } } defer coll.Close() err = installTailCalls(mapDirs[0], spec, coll, ci) if err != nil { - return fmt.Errorf("installing tail calls failed: %s", err) + return nil, fmt.Errorf("installing tail calls failed: %s", err) } for _, mapLoad := range load.MapLoad { if m, ok := coll.Maps[mapLoad.Name]; ok { if err := m.Update(uint32(0), mapLoad.Data, ebpf.UpdateAny); err != nil { - return err + return nil, err } } else { - return fmt.Errorf("populating map failed as map '%s' was not found from collection", mapLoad.Name) + return nil, fmt.Errorf("populating map failed as map '%s' was not found from collection", mapLoad.Name) } } @@ -315,18 +315,18 @@ func loadProgram( progOverride, ok := coll.Programs["generic_kprobe_override"] if !ok { - return fmt.Errorf("program for section '%s' not found", load.Label) + return nil, fmt.Errorf("program for section '%s' not found", load.Label) } progOverride, err = progOverride.Clone() if err != nil { - return fmt.Errorf("failed to clone program '%s': %w", load.Label, err) + return nil, fmt.Errorf("failed to clone program '%s': %w", load.Label, err) } pinPath := filepath.Join(bpfDir, fmt.Sprint(load.PinPath, "-override")) if err := progOverride.Pin(pinPath); err != nil { - return fmt.Errorf("pinning '%s' to '%s' failed: %w", load.Label, pinPath, err) + return nil, fmt.Errorf("pinning '%s' to '%s' failed: %w", load.Label, pinPath, err) } load.unloaderOverride, err = withProgram(progOverride, progOverrideSpec) @@ -337,7 +337,7 @@ func loadProgram( prog, ok := coll.Programs[progSpec.Name] if !ok { - return fmt.Errorf("program for section '%s' not found", load.Label) + return nil, fmt.Errorf("program for section '%s' not found", load.Label) } pinPath := filepath.Join(bpfDir, load.PinPath) @@ -353,11 +353,11 @@ func loadProgram( // we close the collection. prog, err = prog.Clone() if err != nil { - return fmt.Errorf("failed to clone program '%s': %w", load.Label, err) + return nil, fmt.Errorf("failed to clone program '%s': %w", load.Label, err) } if err := prog.Pin(pinPath); err != nil { - return fmt.Errorf("pinning '%s' to '%s' failed: %w", load.Label, pinPath, err) + return nil, fmt.Errorf("pinning '%s' to '%s' failed: %w", load.Label, pinPath, err) } load.unloader, err = withProgram(prog, progSpec) @@ -365,9 +365,32 @@ func loadProgram( if err := prog.Unpin(); err != nil { logger.GetLogger().Warnf("Unpinning '%s' failed: %w", pinPath, err) } - return err + return nil, err } + // Copy the loaded collection before it's destroyed + if KeepCollection { + return copyLoadedCollection(coll) + } + return nil, nil +} + +func loadProgram( + bpfDir string, + mapDirs []string, + load *Program, + withProgram AttachFunc, + ci *customInstall, + verbose int, +) error { + lc, err := doLoadProgram(bpfDir, mapDirs, load, withProgram, ci, verbose) + if err != nil { + return err + } + if KeepCollection { + load.LC = filterLoadedCollection(lc) + printLoadedCollection(load.Name, load.LC) + } return nil } diff --git a/pkg/sensors/program/program.go b/pkg/sensors/program/program.go index 849dc9b7dfe..f66c99080dc 100644 --- a/pkg/sensors/program/program.go +++ b/pkg/sensors/program/program.go @@ -76,6 +76,9 @@ type Program struct { unloaderOverride unloader.Unloader PinMap map[string]string + + // available when program.KeepCollection is true + LC *LoadedCollection } func (p *Program) SetRetProbe(ret bool) *Program { diff --git a/pkg/testutils/sensors/testrun.go b/pkg/testutils/sensors/testrun.go index 2ffaf625090..e9ed775c816 100644 --- a/pkg/testutils/sensors/testrun.go +++ b/pkg/testutils/sensors/testrun.go @@ -13,6 +13,7 @@ import ( "time" "github.com/cilium/tetragon/pkg/bpf" + "github.com/cilium/tetragon/pkg/sensors/program" ) var config *Config @@ -53,6 +54,9 @@ func TestSensorsRun(m *testing.M, sensorName string) int { c := ConfigDefaults config = &c + // instruct loader to keep the loaded collection for TestLoad* tests + program.KeepCollection = true + // some tests require the name of the current binary. config.SelfBinary = filepath.Base(os.Args[0]) From 98ddd8b4d5b385bd2dd2784a45f255d43fcb66e2 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 25 Jul 2022 09:47:17 +0200 Subject: [PATCH 3/6] tetragon: Add CheckSensorLoad function Adding CheckSensorLoad function that takes array of programs and maps and checks they are loaded and share specified maps. Signed-off-by: Jiri Olsa --- pkg/testutils/sensors/load.go | 149 ++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 pkg/testutils/sensors/load.go diff --git a/pkg/testutils/sensors/load.go b/pkg/testutils/sensors/load.go new file mode 100644 index 00000000000..9d5ee705588 --- /dev/null +++ b/pkg/testutils/sensors/load.go @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package sensors + +import ( + "testing" + + "github.com/cilium/ebpf" + "github.com/cilium/tetragon/pkg/sensors" + "github.com/cilium/tetragon/pkg/sensors/program" +) + +type SensorProg struct { + Name string + Type ebpf.ProgramType + NotIn bool +} + +type SensorMap struct { + Name string + Progs []uint +} + +func findMapForProg(coll *program.LoadedCollection, nam string, p *program.LoadedProgram, t *testing.T) *program.LoadedMap { + for name, m := range coll.Maps { + if nam != name { + continue + } + for _, id := range p.MapIDs { + if m.ID == id { + return m + } + } + } + return nil +} + +type prog struct { + name string + prog *program.LoadedProgram + coll *program.LoadedCollection + mark bool +} + +func findProgram(cache []*prog, name string, typ ebpf.ProgramType, t *testing.T) *prog { + for _, c := range cache { + if c.prog.Type != typ { + continue + } + if c.name == name { + return c + } + } + return nil +} + +func CheckSensorLoad(sensors []*sensors.Sensor, sensorMaps []SensorMap, sensorProgs []SensorProg, t *testing.T) { + + var cache []*prog + + // make programs cache 'name/type/coll' + for _, sensor := range sensors { + for _, load := range sensor.Progs { + c := load.LC + for n, p := range c.Programs { + c := &prog{name: n, prog: p, coll: c, mark: false} + cache = append(cache, c) + } + } + } + + // check that we loaded expected programs + for _, tp := range sensorProgs { + c := findProgram(cache, tp.Name, tp.Type, t) + if c == nil { + t.Fatalf("could not find program %v in sensor", tp.Name) + } + c.mark = true + t.Logf("Found prog %v type %s\n", c.name, c.prog.Type) + } + + var extra bool + + // check that we did not load anything else + for _, c := range cache { + if !c.mark { + t.Logf("found extra program loaded: %v type %s", c.name, c.prog.Type) + extra = true + } + } + + if extra { + t.Fatalf("found extra program loaded") + } + + // check user provided maps + for _, tm := range sensorMaps { + var sharedId ebpf.MapID + + t.Logf("Checking map %v\n", tm.Name) + + for _, c := range cache { + c.mark = false + } + + // check that tm.Progs programs DO share the map + for _, idx := range tm.Progs { + tp := sensorProgs[idx] + + c := findProgram(cache, tp.Name, tp.Type, t) + if c == nil { + t.Fatalf("could not find program %v in sensor\n", tp.Name) + } + + m := findMapForProg(c.coll, tm.Name, c.prog, t) + if m == nil { + t.Fatalf("could not find map %v in program %v\n", tm.Name, tp.Name) + } + + t.Logf("\tFound map %v id %v in prog %v\n", tm.Name, m.ID, tp.Name) + + if sharedId == 0 { + sharedId = m.ID + } + + if m.ID != sharedId { + t.Fatalf("map %v has wrong shared id %v != %v\n", tm.Name, m.ID, sharedId) + } + c.mark = true + } + + // check that rest of the loaded programs DO NOT share the map + for _, c := range cache { + if c.mark { + continue + } + + m := findMapForProg(c.coll, tm.Name, c.prog, t) + if m == nil { + continue + } + + if m.ID == sharedId { + t.Fatalf("Map %s[%d] is shared also with program %s", tm.Name, m.ID, c.name) + } + } + } +} From f99b7422df671740ce61955dcc928659af7ef0a9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 22 Jul 2022 15:43:34 +0200 Subject: [PATCH 4/6] tetragon: Add TestLoadInitialSensor test Adding load test for initial sensors. Signed-off-by: Jiri Olsa --- pkg/sensors/exec/exec_test.go | 40 ++++++++++++++++++++++++++++++++ pkg/testutils/sensors/testrun.go | 5 ++++ 2 files changed, 45 insertions(+) diff --git a/pkg/sensors/exec/exec_test.go b/pkg/sensors/exec/exec_test.go index 080a3913f3f..4d0462e8ea1 100644 --- a/pkg/sensors/exec/exec_test.go +++ b/pkg/sensors/exec/exec_test.go @@ -11,13 +11,18 @@ import ( "sync" "testing" + "github.com/cilium/ebpf" ec "github.com/cilium/tetragon/api/v1/tetragon/codegen/eventchecker" api "github.com/cilium/tetragon/pkg/api/processapi" + "github.com/cilium/tetragon/pkg/bpf" "github.com/cilium/tetragon/pkg/jsonchecker" "github.com/cilium/tetragon/pkg/kernels" sm "github.com/cilium/tetragon/pkg/matchers/stringmatcher" "github.com/cilium/tetragon/pkg/observer" + "github.com/cilium/tetragon/pkg/option" "github.com/cilium/tetragon/pkg/reader/namespace" + "github.com/cilium/tetragon/pkg/sensors" + "github.com/cilium/tetragon/pkg/sensors/base" "github.com/cilium/tetragon/pkg/sensors/exec/procevents" "github.com/cilium/tetragon/pkg/testutils" tus "github.com/cilium/tetragon/pkg/testutils/sensors" @@ -409,3 +414,38 @@ func TestEventExecveLongPathLongArgs(t *testing.T) { err = jsonchecker.JsonTestCheck(t, checker) assert.NoError(t, err) } + +func TestLoadInitialSensor(t *testing.T) { + + var sensorProgs = []tus.SensorProg{ + 0: tus.SensorProg{Name: "event_execve", Type: ebpf.TracePoint}, + 1: tus.SensorProg{Name: "event_exit", Type: ebpf.TracePoint}, + 2: tus.SensorProg{Name: "event_wake_up_new_task", Type: ebpf.Kprobe}, + } + + var sensorMaps = []tus.SensorMap{ + // all programs + tus.SensorMap{Name: "execve_map", Progs: []uint{0, 1, 2}}, + tus.SensorMap{Name: "execve_map_stats", Progs: []uint{0, 1, 2}}, + tus.SensorMap{Name: "tcpmon_map", Progs: []uint{0, 1, 2}}, + + // event_execve + tus.SensorMap{Name: "names_map", Progs: []uint{0}}, + + // event_wake_up_new_task + tus.SensorMap{Name: "execve_val", Progs: []uint{2}}, + } + + sensor := base.GetInitialSensor() + + option.Config.HubbleLib = tus.Conf().TetragonLib + + t.Logf("Loading sensor %v\n", sensor.Name) + if err := sensor.Load(context.TODO(), bpf.MapPrefixPath(), bpf.MapPrefixPath(), ""); err != nil { + t.Fatalf("sensor.Load failed: %v\n", err) + } + + tus.CheckSensorLoad([]*sensors.Sensor{sensor}, sensorMaps, sensorProgs, t) + + sensors.UnloadAll(tus.Conf().TetragonLib) +} diff --git a/pkg/testutils/sensors/testrun.go b/pkg/testutils/sensors/testrun.go index e9ed775c816..07872d9e1f4 100644 --- a/pkg/testutils/sensors/testrun.go +++ b/pkg/testutils/sensors/testrun.go @@ -4,6 +4,7 @@ package sensors import ( + "context" "flag" "fmt" "os" @@ -13,6 +14,7 @@ import ( "time" "github.com/cilium/tetragon/pkg/bpf" + "github.com/cilium/tetragon/pkg/btf" "github.com/cilium/tetragon/pkg/sensors/program" ) @@ -99,5 +101,8 @@ func TestSensorsRun(m *testing.M, sensorName string) int { fmt.Printf("map dir `%s` still exists after test. Removing it.\n", path) os.RemoveAll(path) }() + if err := btf.InitCachedBTF(context.Background(), config.TetragonLib, ""); err != nil { + fmt.Printf("InitCachedBTF failed: %v", err) + } return m.Run() } From a99f7a4772ce67bcf97f5d4dc0315bc39eaaf861 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 1 Aug 2022 13:51:15 +0200 Subject: [PATCH 5/6] tetragon: Add TestLoadKprobeSensor test Adding load test for generic kprobe sensors. Signed-off-by: Jiri Olsa --- pkg/sensors/tracing/kprobe_test.go | 100 +++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/pkg/sensors/tracing/kprobe_test.go b/pkg/sensors/tracing/kprobe_test.go index 9683bd57159..663f56f0f8f 100644 --- a/pkg/sensors/tracing/kprobe_test.go +++ b/pkg/sensors/tracing/kprobe_test.go @@ -16,6 +16,7 @@ import ( "testing" "unsafe" + "github.com/cilium/ebpf" "github.com/cilium/tetragon/api/v1/tetragon" ec "github.com/cilium/tetragon/api/v1/tetragon/codegen/eventchecker" "github.com/cilium/tetragon/pkg/bpf" @@ -2400,3 +2401,102 @@ spec: err = jsonchecker.JsonTestCheck(t, checker) assert.NoError(t, err) } + +func TestLoadKprobeSensor(t *testing.T) { + var sensorProgs = []tus.SensorProg{ + // kprobe + 0: tus.SensorProg{Name: "generic_kprobe_event", Type: ebpf.Kprobe}, + 1: tus.SensorProg{Name: "generic_kprobe_process_event0", Type: ebpf.Kprobe}, + 2: tus.SensorProg{Name: "generic_kprobe_process_event1", Type: ebpf.Kprobe}, + 3: tus.SensorProg{Name: "generic_kprobe_process_event2", Type: ebpf.Kprobe}, + 4: tus.SensorProg{Name: "generic_kprobe_process_event3", Type: ebpf.Kprobe}, + 5: tus.SensorProg{Name: "generic_kprobe_process_event4", Type: ebpf.Kprobe}, + 6: tus.SensorProg{Name: "generic_kprobe_filter_arg1", Type: ebpf.Kprobe}, + 7: tus.SensorProg{Name: "generic_kprobe_filter_arg2", Type: ebpf.Kprobe}, + 8: tus.SensorProg{Name: "generic_kprobe_filter_arg3", Type: ebpf.Kprobe}, + 9: tus.SensorProg{Name: "generic_kprobe_filter_arg4", Type: ebpf.Kprobe}, + 10: tus.SensorProg{Name: "generic_kprobe_filter_arg5", Type: ebpf.Kprobe}, + 11: tus.SensorProg{Name: "generic_kprobe_process_filter", Type: ebpf.Kprobe}, + // retkprobe + 12: tus.SensorProg{Name: "generic_retkprobe_event", Type: ebpf.Kprobe}, + + // base sensor + 13: tus.SensorProg{Name: "event_execve", Type: ebpf.TracePoint}, + 14: tus.SensorProg{Name: "event_exit", Type: ebpf.TracePoint}, + 15: tus.SensorProg{Name: "event_wake_up_new_task", Type: ebpf.Kprobe}, + } + + var sensorMaps = []tus.SensorMap{ + // all kprobe programs + tus.SensorMap{Name: "process_call_heap", Progs: []uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + tus.SensorMap{Name: "kprobe_calls", Progs: []uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + + // only retkprobe + tus.SensorMap{Name: "process_call_heap", Progs: []uint{12}}, + + // generic_kprobe_process_filter,generic_kprobe_filter_arg* + tus.SensorMap{Name: "filter_map", Progs: []uint{6, 7, 8, 9, 10, 11}}, + + // generic_kprobe_filter_arg* + tus.SensorMap{Name: "override_tasks", Progs: []uint{6, 7, 8, 9, 10}}, + + // generic_kprobe_filter_arg*,generic_retkprobe_event,base + tus.SensorMap{Name: "tcpmon_map", Progs: []uint{6, 7, 8, 9, 10, 12, 13, 14, 15}}, + + // only retkprobe + tus.SensorMap{Name: "config_map", Progs: []uint{12}}, + + // shared with base sensor + tus.SensorMap{Name: "execve_map", Progs: []uint{13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}}, + tus.SensorMap{Name: "execve_map_stats", Progs: []uint{13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}}, + + // generic_kprobe_process_event*,generic_kprobe_filter_arg*,retkprobe + tus.SensorMap{Name: "fdinstall_map", Progs: []uint{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12}}, + } + + if kernels.EnableLargeProgs() { + // all kprobe but generic_kprobe_process_filter,generic_kprobe_event + sensorMaps = append(sensorMaps, tus.SensorMap{Name: "config_map", Progs: []uint{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}) + } else { + // all kprobe but generic_kprobe_process_filter,generic_kprobe_event + sensorMaps = append(sensorMaps, tus.SensorMap{Name: "config_map", Progs: []uint{1, 2, 3, 4, 5}}) + } + + readHook := ` +apiVersion: cilium.io/v1alpha1 +metadata: + name: "sys_read" +spec: + kprobes: + - call: "__x64_sys_read" + syscall: true + return: true + args: + - index: 0 + type: "int" + - index: 1 + type: "char_buf" + returnCopy: true + - index: 2 + type: "size_t" + returnArg: + type: "size_t" +` + + var sens []*sensors.Sensor + var err error + + readConfigHook := []byte(readHook) + err = ioutil.WriteFile(testConfigFile, readConfigHook, 0644) + if err != nil { + t.Fatalf("writeFile(%s): err %s", testConfigFile, err) + } + sens, err = observer.GetDefaultSensorsWithFile(t, context.TODO(), testConfigFile, tus.Conf().TetragonLib) + if err != nil { + t.Fatalf("GetDefaultObserverWithFile error: %s", err) + } + + tus.CheckSensorLoad(sens, sensorMaps, sensorProgs, t) + + sensors.UnloadAll(tus.Conf().TetragonLib) +} From f6e44a2a54d656e61ef91977b827d6c98ffccc2b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 11 Aug 2022 08:23:49 +0200 Subject: [PATCH 6/6] tetragon: Add TestLoadTracepointSensor test Adding load test for generic tracepoint sensors. Signed-off-by: Jiri Olsa --- pkg/sensors/tracing/tracepoint_test.go | 84 ++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/pkg/sensors/tracing/tracepoint_test.go b/pkg/sensors/tracing/tracepoint_test.go index ed1da9ca94a..d376c7dfa03 100644 --- a/pkg/sensors/tracing/tracepoint_test.go +++ b/pkg/sensors/tracing/tracepoint_test.go @@ -6,19 +6,23 @@ package tracing import ( "context" "fmt" + "io/ioutil" "os" "sync" "syscall" "testing" "time" + "github.com/cilium/ebpf" "github.com/cilium/tetragon/api/v1/tetragon" ec "github.com/cilium/tetragon/api/v1/tetragon/codegen/eventchecker" "github.com/cilium/tetragon/pkg/jsonchecker" "github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1" + "github.com/cilium/tetragon/pkg/kernels" lc "github.com/cilium/tetragon/pkg/matchers/listmatcher" smatcher "github.com/cilium/tetragon/pkg/matchers/stringmatcher" "github.com/cilium/tetragon/pkg/observer" + "github.com/cilium/tetragon/pkg/sensors" testsensor "github.com/cilium/tetragon/pkg/sensors/test" tus "github.com/cilium/tetragon/pkg/testutils/sensors" "github.com/sirupsen/logrus" @@ -387,3 +391,83 @@ func TestGenericTracepointRawSyscall(t *testing.T) { doTestGenericTracepointPidFilter(t, tracepointConf, op, check) } + +func TestLoadTracepointSensor(t *testing.T) { + var sensorProgs = []tus.SensorProg{ + 0: tus.SensorProg{Name: "generic_tracepoint_event", Type: ebpf.TracePoint}, + 1: tus.SensorProg{Name: "generic_tracepoint_arg1", Type: ebpf.TracePoint}, + 2: tus.SensorProg{Name: "generic_tracepoint_arg2", Type: ebpf.TracePoint}, + 3: tus.SensorProg{Name: "generic_tracepoint_arg3", Type: ebpf.TracePoint}, + 4: tus.SensorProg{Name: "generic_tracepoint_arg4", Type: ebpf.TracePoint}, + 5: tus.SensorProg{Name: "generic_tracepoint_arg5", Type: ebpf.TracePoint}, + 6: tus.SensorProg{Name: "generic_tracepoint_event0", Type: ebpf.TracePoint}, + 7: tus.SensorProg{Name: "generic_tracepoint_event1", Type: ebpf.TracePoint}, + 8: tus.SensorProg{Name: "generic_tracepoint_event2", Type: ebpf.TracePoint}, + 9: tus.SensorProg{Name: "generic_tracepoint_event3", Type: ebpf.TracePoint}, + 10: tus.SensorProg{Name: "generic_tracepoint_event4", Type: ebpf.TracePoint}, + 11: tus.SensorProg{Name: "generic_tracepoint_filter", Type: ebpf.TracePoint}, + + // base sensor + 12: tus.SensorProg{Name: "event_execve", Type: ebpf.TracePoint}, + 13: tus.SensorProg{Name: "event_exit", Type: ebpf.TracePoint}, + 14: tus.SensorProg{Name: "event_wake_up_new_task", Type: ebpf.Kprobe}, + } + + var sensorMaps = []tus.SensorMap{ + // all programs + tus.SensorMap{Name: "tp_heap", Progs: []uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + tus.SensorMap{Name: "tp_calls", Progs: []uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + + // only generic_tracepoint_event* + tus.SensorMap{Name: "buffer_heap_map", Progs: []uint{6, 7, 8, 9, 10}}, + + // all but generic_tracepoint_event,generic_tracepoint_filter + tus.SensorMap{Name: "retprobe_map", Progs: []uint{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + + // generic_tracepoint_arg**,base + tus.SensorMap{Name: "tcpmon_map", Progs: []uint{1, 2, 3, 4, 5, 12, 13, 14}}, + + // shared with base sensor + tus.SensorMap{Name: "execve_map", Progs: []uint{12, 13, 14, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + tus.SensorMap{Name: "execve_map_stats", Progs: []uint{12, 13, 14, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + } + + if kernels.EnableLargeProgs() { + // all kprobe but generic_kprobe_process_filter,generic_kprobe_event + sensorMaps = append(sensorMaps, tus.SensorMap{Name: "config_map", Progs: []uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}) + } else { + // all kprobe but generic_kprobe_process_filter,generic_kprobe_event + sensorMaps = append(sensorMaps, tus.SensorMap{Name: "config_map", Progs: []uint{0, 6, 7, 8, 9, 10}}) + } + + readHook := ` +kind: TracingPolicy +metadata: + name: "raw-syscalls" +spec: + tracepoints: + - subsystem: "raw_syscalls" + event: "sys_enter" + # args: add both the syscall id, and the array with the arguments + args: + - index: 4 + - index: 5 +` + + var sens []*sensors.Sensor + var err error + + readConfigHook := []byte(readHook) + err = ioutil.WriteFile(testConfigFile, readConfigHook, 0644) + if err != nil { + t.Fatalf("writeFile(%s): err %s", testConfigFile, err) + } + sens, err = observer.GetDefaultSensorsWithFile(t, context.TODO(), testConfigFile, tus.Conf().TetragonLib) + if err != nil { + t.Fatalf("GetDefaultObserverWithFile error: %s", err) + } + + tus.CheckSensorLoad(sens, sensorMaps, sensorProgs, t) + + sensors.UnloadAll(tus.Conf().TetragonLib) +}