diff --git a/docs/data/tetragon_flags.yaml b/docs/data/tetragon_flags.yaml index 7a27fa4c60d..88af3e2d93a 100644 --- a/docs/data/tetragon_flags.yaml +++ b/docs/data/tetragon_flags.yaml @@ -95,6 +95,9 @@ options: - name: event-queue-size default_value: "10000" usage: Set the size of the internal event queue. + - name: execve-map-entries + default_value: "32768" + usage: Set entries for execve_map table (entries/size/max) - name: export-aggregation-buffer-size default_value: "10000" usage: Aggregator channel buffer size diff --git a/pkg/option/config.go b/pkg/option/config.go index 30871885afc..560692461c4 100644 --- a/pkg/option/config.go +++ b/pkg/option/config.go @@ -110,6 +110,8 @@ type config struct { EventCacheRetryDelay int CompatibilitySyscall64SizeType bool + + ExecveMapEntries string } var ( diff --git a/pkg/option/flags.go b/pkg/option/flags.go index 05a72d0e2a4..4ae0e040fc5 100644 --- a/pkg/option/flags.go +++ b/pkg/option/flags.go @@ -118,6 +118,8 @@ const ( KeyEventCacheRetryDelay = "event-cache-retry-delay" KeyCompatibilitySyscall64SizeType = "enable-compatibility-syscall64-size-type" + + KeyExecveMapEntries = "execve-map-entries" ) type UsernameMetadaCode int @@ -252,6 +254,7 @@ func ReadAndSetFlags() error { Config.CompatibilitySyscall64SizeType = viper.GetBool(KeyCompatibilitySyscall64SizeType) + Config.ExecveMapEntries = viper.GetString(KeyExecveMapEntries) return nil } @@ -419,4 +422,6 @@ func AddFlags(flags *pflag.FlagSet) { flags.Int(KeyEventCacheRetryDelay, defaults.DefaultEventCacheRetryDelay, "Delay in seconds between event cache retries") flags.Bool(KeyCompatibilitySyscall64SizeType, false, "syscall64 type will produce output of type size (compatibility flag, will be removed in v1.4)") + + flags.String(KeyExecveMapEntries, "32768", "Set entries for execve_map table (entries/size/max)") } diff --git a/pkg/sensors/base/base.go b/pkg/sensors/base/base.go index f080fde22a5..158a4fe5dfc 100644 --- a/pkg/sensors/base/base.go +++ b/pkg/sensors/base/base.go @@ -5,20 +5,27 @@ package base import ( "log" + "os" + "strconv" + "strings" "sync" "testing" + "unsafe" "github.com/cilium/tetragon/pkg/errmetrics" "github.com/cilium/tetragon/pkg/ksyms" "github.com/cilium/tetragon/pkg/logger" "github.com/cilium/tetragon/pkg/mbset" + "github.com/cilium/tetragon/pkg/option" "github.com/cilium/tetragon/pkg/sensors" "github.com/cilium/tetragon/pkg/sensors/exec/config" + "github.com/cilium/tetragon/pkg/sensors/exec/execvemap" "github.com/cilium/tetragon/pkg/sensors/program" + "github.com/cilium/tetragon/pkg/strutils" ) const ( - execveMapMaxEntries = 32768 + ExecveMapMaxEntries = 32768 ) var ( @@ -77,6 +84,43 @@ var ( ErrMetricsMap = program.MapBuilder(errmetrics.MapName, Execve) ) +func readThreadsMax(path string) (int64, error) { + data, err := os.ReadFile(path) + if err != nil { + return 0, err + } + str := strings.TrimRight(string(data), "\n") + return strconv.ParseInt(str, 10, 32) +} + +func GetExecveMapEntries(str string) int { + entry := int(unsafe.Sizeof(execvemap.ExecveValue{})) + + if len(str) == 0 { + return ExecveMapMaxEntries + } + // pure number of entries + if val, err := strconv.ParseUint(str, 10, 32); err == nil { + return int(val) + } + // follow threads-max entries + if str == "max" { + if val, err := readThreadsMax("/proc/sys/kernel/threads-max"); err == nil { + return int(val) + } + logger.GetLogger().Warn("Failed to read /proc/sys/kernel/threads-max file, falling back to default") + return ExecveMapMaxEntries + } + // set entries based on size + size, err := strutils.ParseSize(str) + if err != nil { + logger.GetLogger().Warn("Failed to parse --execve-map-max value, falling back to default") + return ExecveMapMaxEntries + } + val := size / entry + return val +} + func setupSensor() { // exit program function ks, err := ksyms.KernelSymbols() @@ -97,7 +141,13 @@ func setupSensor() { } logger.GetLogger().Infof("Exit probe on %s", Exit.Attach) - ExecveMap.SetMaxEntries(execveMapMaxEntries) + entries := GetExecveMapEntries(option.Config.ExecveMapEntries) + ExecveMap.SetMaxEntries(entries) + + logger.GetLogger(). + WithField("size", strutils.SizeWithSuffix(entries*int(unsafe.Sizeof(execvemap.ExecveValue{})))). + WithField("config", option.Config.ExecveMapEntries). + Infof("Set execve_map entries %d", entries) } func GetExecveMap() *program.Map { diff --git a/pkg/sensors/exec/procevents/proc_reader.go b/pkg/sensors/exec/procevents/proc_reader.go index 6e3f0d3aba9..e0c8acbdfb4 100644 --- a/pkg/sensors/exec/procevents/proc_reader.go +++ b/pkg/sensors/exec/procevents/proc_reader.go @@ -335,6 +335,15 @@ func writeExecveMap(procs []procs) { panic(err) } } + + entries, ok := base.ExecveMap.GetMaxEntries() + if !ok { + logger.GetLogger().Warnf("Failed to get number of execve_map entries, using default.") + entries = base.ExecveMapMaxEntries + } + logger.GetLogger().Infof("Maximum execve_map entries %d, need to add %d.", entries, len(procs)) + i := uint32(0) + for _, p := range procs { k := &execvemap.ExecveKey{Pid: p.pid} v := &execvemap.ExecveValue{} @@ -365,6 +374,11 @@ func writeExecveMap(procs []procs) { if err != nil { logger.GetLogger().WithField("value", v).WithError(err).Warn("failed to put value in execve_map") } + + i++ + if i == entries { + break + } } // In order for kprobe events from kernel ctx to not abort we need the // execve lookup to map to a valid entry. So to simplify the kernel side @@ -381,7 +395,7 @@ func writeExecveMap(procs []procs) { }) m.Close() - updateExecveMapStats(int64(len(procs))) + updateExecveMapStats(int64(entries)) } func pushEvents(ps []procs) {