From 28dda9c7c4aa586ae0dad432e2595c1ee69ea033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geyslan=20Greg=C3=B3rio?= Date: Thu, 10 Oct 2024 18:19:48 -0300 Subject: [PATCH] wip --- Makefile | 29 ++ api/v1beta1/diagnostic.pb.go | 147 +++--- api/v1beta1/diagnostic.proto | 13 +- cmd/evt/cmd/root.go | 36 ++ cmd/evt/cmd/stress/stress.go | 492 ++++++++++++++++++ cmd/evt/cmd/stress/triggers/arch_prctl.sh | 1 + cmd/evt/cmd/stress/triggers/bpf_attach.sh | 1 + cmd/evt/cmd/stress/triggers/commit_creds.sh | 1 + .../cmd/stress/triggers/common/bpftrace.sh | 18 + cmd/evt/cmd/stress/triggers/common/docker.sh | 6 + .../stress/triggers/common/mktemp-ln-rm.sh | 19 + cmd/evt/cmd/stress/triggers/common/ping.sh | 13 + .../cmd/stress/triggers/common/self-comm.sh | 11 + cmd/evt/cmd/stress/triggers/common/sudo.sh | 15 + .../cmd/stress/triggers/common/timeout-nc.sh | 14 + cmd/evt/cmd/stress/triggers/common/true.sh | 11 + .../stress/triggers/common/unshare-mkdir.sh | 22 + .../cmd/stress/triggers/debugfs_create_dir.sh | 1 + .../stress/triggers/debugfs_create_file.sh | 1 + cmd/evt/cmd/stress/triggers/device_add.sh | 1 + cmd/evt/cmd/stress/triggers/do_truncate.sh | 1 + .../stress/triggers/kallsyms_lookup_name.sh | 1 + cmd/evt/cmd/stress/triggers/kprobe_attach.sh | 1 + cmd/evt/cmd/stress/triggers/list | 19 + cmd/evt/cmd/stress/triggers/magic_write.sh | 1 + .../stress/triggers/process_execute_failed.sh | 1 + cmd/evt/cmd/stress/triggers/ptrace.sh | 13 + .../cmd/stress/triggers/sched_process_exec.sh | 1 + .../cmd/stress/triggers/sched_process_exit.sh | 1 + .../cmd/stress/triggers/sched_process_fork.sh | 1 + .../cmd/stress/triggers/security_bpf_prog.sh | 11 + .../cmd/stress/triggers/security_file_open.sh | 1 + .../stress/triggers/security_inode_symlink.sh | 1 + .../stress/triggers/security_inode_unlink.sh | 1 + .../stress/triggers/security_path_notify.sh | 10 + .../cmd/stress/triggers/security_sb_mount.sh | 1 + .../stress/triggers/security_socket_bind.sh | 1 + .../triggers/security_socket_connect.sh | 1 + .../stress/triggers/security_socket_create.sh | 1 + .../stress/triggers/shared_object_loaded.sh | 1 + cmd/evt/cmd/stress/triggers/socked_dup.sh | 1 + cmd/evt/cmd/stress/triggers/switch_task_ns.sh | 1 + cmd/evt/main.go | 18 + go.mod | 1 + pkg/cmd/flags/event.go | 17 + pkg/ebpf/c/common/buffer.h | 29 +- pkg/ebpf/perf_count.go | 71 ++- pkg/metrics/stats.go | 36 +- pkg/server/grpc/diagnostic.go | 21 +- 49 files changed, 989 insertions(+), 127 deletions(-) create mode 100644 cmd/evt/cmd/root.go create mode 100644 cmd/evt/cmd/stress/stress.go create mode 120000 cmd/evt/cmd/stress/triggers/arch_prctl.sh create mode 120000 cmd/evt/cmd/stress/triggers/bpf_attach.sh create mode 120000 cmd/evt/cmd/stress/triggers/commit_creds.sh create mode 100755 cmd/evt/cmd/stress/triggers/common/bpftrace.sh create mode 100755 cmd/evt/cmd/stress/triggers/common/docker.sh create mode 100755 cmd/evt/cmd/stress/triggers/common/mktemp-ln-rm.sh create mode 100755 cmd/evt/cmd/stress/triggers/common/ping.sh create mode 100755 cmd/evt/cmd/stress/triggers/common/self-comm.sh create mode 100755 cmd/evt/cmd/stress/triggers/common/sudo.sh create mode 100755 cmd/evt/cmd/stress/triggers/common/timeout-nc.sh create mode 100755 cmd/evt/cmd/stress/triggers/common/true.sh create mode 100755 cmd/evt/cmd/stress/triggers/common/unshare-mkdir.sh create mode 120000 cmd/evt/cmd/stress/triggers/debugfs_create_dir.sh create mode 120000 cmd/evt/cmd/stress/triggers/debugfs_create_file.sh create mode 120000 cmd/evt/cmd/stress/triggers/device_add.sh create mode 120000 cmd/evt/cmd/stress/triggers/do_truncate.sh create mode 120000 cmd/evt/cmd/stress/triggers/kallsyms_lookup_name.sh create mode 120000 cmd/evt/cmd/stress/triggers/kprobe_attach.sh create mode 100644 cmd/evt/cmd/stress/triggers/list create mode 120000 cmd/evt/cmd/stress/triggers/magic_write.sh create mode 120000 cmd/evt/cmd/stress/triggers/process_execute_failed.sh create mode 100755 cmd/evt/cmd/stress/triggers/ptrace.sh create mode 120000 cmd/evt/cmd/stress/triggers/sched_process_exec.sh create mode 120000 cmd/evt/cmd/stress/triggers/sched_process_exit.sh create mode 120000 cmd/evt/cmd/stress/triggers/sched_process_fork.sh create mode 100755 cmd/evt/cmd/stress/triggers/security_bpf_prog.sh create mode 120000 cmd/evt/cmd/stress/triggers/security_file_open.sh create mode 120000 cmd/evt/cmd/stress/triggers/security_inode_symlink.sh create mode 120000 cmd/evt/cmd/stress/triggers/security_inode_unlink.sh create mode 100755 cmd/evt/cmd/stress/triggers/security_path_notify.sh create mode 120000 cmd/evt/cmd/stress/triggers/security_sb_mount.sh create mode 120000 cmd/evt/cmd/stress/triggers/security_socket_bind.sh create mode 120000 cmd/evt/cmd/stress/triggers/security_socket_connect.sh create mode 120000 cmd/evt/cmd/stress/triggers/security_socket_create.sh create mode 120000 cmd/evt/cmd/stress/triggers/shared_object_loaded.sh create mode 120000 cmd/evt/cmd/stress/triggers/socked_dup.sh create mode 120000 cmd/evt/cmd/stress/triggers/switch_task_ns.sh create mode 100644 cmd/evt/main.go diff --git a/Makefile b/Makefile index 87cad2f1f8a0..705070bdb0d9 100644 --- a/Makefile +++ b/Makefile @@ -625,6 +625,35 @@ clean-signatures: # other commands # +# evt + +EVT_SRC_DIRS = ./cmd/evt/ +EVT_SRC = $(shell find $(EVT_SRC_DIRS) \ + -type f \ + -name '*.go' \ + ! -name '*_test.go' \ + ) + +.PHONY: evt +evt: $(OUTPUT_DIR)/evt + +$(OUTPUT_DIR)/evt: \ + $(EVT_SRC) \ + $(OUTPUT_DIR)/tracee \ + | .eval_goenv \ + .checkver_$(CMD_GO) \ +# + $(GO_ENV_EBPF) $(CMD_GO) build \ + -ldflags="$(GO_DEBUG_FLAG) \ + " \ + -v -o $@ \ + ./cmd/evt + +.PHONY: clean-evt +clean-evt: +# + $(CMD_RM) -rf $(OUTPUT_DIR)/evt + # tracee-bench TRACEE_BENCH_SRC_DIRS = ./cmd/tracee-bench/ diff --git a/api/v1beta1/diagnostic.pb.go b/api/v1beta1/diagnostic.pb.go index 15a8352cffc6..e26a58a48b60 100644 --- a/api/v1beta1/diagnostic.pb.go +++ b/api/v1beta1/diagnostic.pb.go @@ -124,16 +124,17 @@ type GetMetricsResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - EventCount uint64 `protobuf:"varint,1,opt,name=EventCount,proto3" json:"EventCount,omitempty"` - EventsFiltered uint64 `protobuf:"varint,2,opt,name=EventsFiltered,proto3" json:"EventsFiltered,omitempty"` - NetCapCount uint64 `protobuf:"varint,3,opt,name=NetCapCount,proto3" json:"NetCapCount,omitempty"` - BPFLogsCount uint64 `protobuf:"varint,4,opt,name=BPFLogsCount,proto3" json:"BPFLogsCount,omitempty"` - BPFPerfEventWrites uint64 `protobuf:"varint,5,opt,name=BPFPerfEventWrites,proto3" json:"BPFPerfEventWrites,omitempty"` - ErrorCount uint64 `protobuf:"varint,6,opt,name=ErrorCount,proto3" json:"ErrorCount,omitempty"` - LostEvCount uint64 `protobuf:"varint,7,opt,name=LostEvCount,proto3" json:"LostEvCount,omitempty"` - LostWrCount uint64 `protobuf:"varint,8,opt,name=LostWrCount,proto3" json:"LostWrCount,omitempty"` - LostNtCapCount uint64 `protobuf:"varint,9,opt,name=LostNtCapCount,proto3" json:"LostNtCapCount,omitempty"` - LostBPFLogsCount uint64 `protobuf:"varint,10,opt,name=LostBPFLogsCount,proto3" json:"LostBPFLogsCount,omitempty"` + EventCount uint64 `protobuf:"varint,1,opt,name=EventCount,proto3" json:"EventCount,omitempty"` + EventsFiltered uint64 `protobuf:"varint,2,opt,name=EventsFiltered,proto3" json:"EventsFiltered,omitempty"` + NetCapCount uint64 `protobuf:"varint,3,opt,name=NetCapCount,proto3" json:"NetCapCount,omitempty"` + BPFLogsCount uint64 `protobuf:"varint,4,opt,name=BPFLogsCount,proto3" json:"BPFLogsCount,omitempty"` + BPFPerfEventWriteAttempts uint64 `protobuf:"varint,5,opt,name=BPFPerfEventWriteAttempts,proto3" json:"BPFPerfEventWriteAttempts,omitempty"` + BPFPerfEventWriteFailures uint64 `protobuf:"varint,6,opt,name=BPFPerfEventWriteFailures,proto3" json:"BPFPerfEventWriteFailures,omitempty"` + ErrorCount uint64 `protobuf:"varint,7,opt,name=ErrorCount,proto3" json:"ErrorCount,omitempty"` + LostEvCount uint64 `protobuf:"varint,8,opt,name=LostEvCount,proto3" json:"LostEvCount,omitempty"` + LostWrCount uint64 `protobuf:"varint,9,opt,name=LostWrCount,proto3" json:"LostWrCount,omitempty"` + LostNtCapCount uint64 `protobuf:"varint,10,opt,name=LostNtCapCount,proto3" json:"LostNtCapCount,omitempty"` + LostBPFLogsCount uint64 `protobuf:"varint,11,opt,name=LostBPFLogsCount,proto3" json:"LostBPFLogsCount,omitempty"` } func (x *GetMetricsResponse) Reset() { @@ -196,9 +197,16 @@ func (x *GetMetricsResponse) GetBPFLogsCount() uint64 { return 0 } -func (x *GetMetricsResponse) GetBPFPerfEventWrites() uint64 { +func (x *GetMetricsResponse) GetBPFPerfEventWriteAttempts() uint64 { if x != nil { - return x.BPFPerfEventWrites + return x.BPFPerfEventWriteAttempts + } + return 0 +} + +func (x *GetMetricsResponse) GetBPFPerfEventWriteFailures() uint64 { + if x != nil { + return x.BPFPerfEventWriteFailures } return 0 } @@ -415,7 +423,7 @@ var file_api_v1beta1_diagnostic_proto_rawDesc = []byte{ 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x22, 0x13, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x8a, 0x03, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x74, 0x22, 0xd6, 0x03, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x45, 0x76, @@ -425,60 +433,65 @@ var file_api_v1beta1_diagnostic_proto_rawDesc = []byte{ 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x4e, 0x65, 0x74, 0x43, 0x61, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x42, 0x50, 0x46, 0x4c, 0x6f, 0x67, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x42, 0x50, 0x46, 0x4c, - 0x6f, 0x67, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x12, 0x42, 0x50, 0x46, 0x50, - 0x65, 0x72, 0x66, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x73, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x42, 0x50, 0x46, 0x50, 0x65, 0x72, 0x66, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x45, 0x72, - 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x4c, 0x6f, 0x73, 0x74, - 0x45, 0x76, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x4c, - 0x6f, 0x73, 0x74, 0x45, 0x76, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x4c, 0x6f, - 0x73, 0x74, 0x57, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0b, 0x4c, 0x6f, 0x73, 0x74, 0x57, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, - 0x4c, 0x6f, 0x73, 0x74, 0x4e, 0x74, 0x43, 0x61, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x4c, 0x6f, 0x73, 0x74, 0x4e, 0x74, 0x43, 0x61, 0x70, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x4c, 0x6f, 0x73, 0x74, 0x42, 0x50, 0x46, 0x4c, - 0x6f, 0x67, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, - 0x4c, 0x6f, 0x73, 0x74, 0x42, 0x50, 0x46, 0x4c, 0x6f, 0x67, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x22, 0x47, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, - 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, - 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, - 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x18, 0x0a, 0x16, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, - 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x37, 0x0a, 0x15, 0x47, - 0x65, 0x74, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, - 0x72, 0x61, 0x63, 0x65, 0x2a, 0x56, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x12, 0x09, 0x0a, 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, - 0x6e, 0x66, 0x6f, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x61, 0x72, 0x6e, 0x10, 0x02, 0x12, - 0x09, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x50, - 0x61, 0x6e, 0x69, 0x63, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x10, - 0x05, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x61, 0x74, 0x61, 0x6c, 0x10, 0x06, 0x32, 0xa7, 0x02, 0x0a, - 0x11, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x53, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x12, 0x21, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x25, 0x2e, 0x74, 0x72, 0x61, 0x63, - 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x26, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, - 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x12, 0x24, 0x2e, 0x74, 0x72, 0x61, 0x63, - 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, - 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x25, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x2f, 0x61, 0x71, 0x75, 0x61, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, - 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x67, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x19, 0x42, 0x50, 0x46, 0x50, + 0x65, 0x72, 0x66, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x41, 0x74, 0x74, + 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, 0x42, 0x50, 0x46, + 0x50, 0x65, 0x72, 0x66, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x41, 0x74, + 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x12, 0x3c, 0x0a, 0x19, 0x42, 0x50, 0x46, 0x50, 0x65, 0x72, + 0x66, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, + 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, 0x42, 0x50, 0x46, 0x50, 0x65, + 0x72, 0x66, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x4c, 0x6f, 0x73, 0x74, 0x45, 0x76, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x4c, 0x6f, 0x73, 0x74, 0x45, + 0x76, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x4c, 0x6f, 0x73, 0x74, 0x57, 0x72, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x4c, 0x6f, 0x73, + 0x74, 0x57, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x4c, 0x6f, 0x73, 0x74, + 0x4e, 0x74, 0x43, 0x61, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0e, 0x4c, 0x6f, 0x73, 0x74, 0x4e, 0x74, 0x43, 0x61, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x2a, 0x0a, 0x10, 0x4c, 0x6f, 0x73, 0x74, 0x42, 0x50, 0x46, 0x4c, 0x6f, 0x67, 0x73, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x4c, 0x6f, 0x73, 0x74, + 0x42, 0x50, 0x46, 0x4c, 0x6f, 0x67, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x47, 0x0a, 0x15, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, + 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x18, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, + 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x16, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x37, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x74, + 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x2a, 0x56, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, + 0x44, 0x65, 0x62, 0x75, 0x67, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 0x10, + 0x01, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x61, 0x72, 0x6e, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x50, 0x61, 0x6e, 0x69, 0x63, + 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x10, 0x05, 0x12, 0x09, 0x0a, + 0x05, 0x46, 0x61, 0x74, 0x61, 0x6c, 0x10, 0x06, 0x32, 0xa7, 0x02, 0x0a, 0x11, 0x44, 0x69, 0x61, + 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, + 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x21, 0x2e, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x25, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x63, 0x6b, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x12, 0x24, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x2f, + 0x61, 0x71, 0x75, 0x61, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/v1beta1/diagnostic.proto b/api/v1beta1/diagnostic.proto index 78af1d2bdc14..78dea97cdf14 100644 --- a/api/v1beta1/diagnostic.proto +++ b/api/v1beta1/diagnostic.proto @@ -12,12 +12,13 @@ message GetMetricsResponse { uint64 EventsFiltered = 2; uint64 NetCapCount = 3; uint64 BPFLogsCount = 4; - uint64 BPFPerfEventWrites = 5; - uint64 ErrorCount = 6; - uint64 LostEvCount = 7; - uint64 LostWrCount = 8; - uint64 LostNtCapCount = 9; - uint64 LostBPFLogsCount = 10; + uint64 BPFPerfEventWriteAttempts = 5; + uint64 BPFPerfEventWriteFailures = 6; + uint64 ErrorCount = 7; + uint64 LostEvCount = 8; + uint64 LostWrCount = 9; + uint64 LostNtCapCount = 10; + uint64 LostBPFLogsCount = 11; } enum LogLevel { diff --git a/cmd/evt/cmd/root.go b/cmd/evt/cmd/root.go new file mode 100644 index 000000000000..ad69f3b87185 --- /dev/null +++ b/cmd/evt/cmd/root.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" + + "github.com/aquasecurity/tracee/cmd/evt/cmd/stress" +) + +func init() { + rootCmd.AddCommand(stress.Cmd()) +} + +var ( + rootCmd = &cobra.Command{ + Use: "evt", + Short: "An event stress testing tool", + Long: "evt is a simple testing tool that generates events to stress the system", + } +) + +func initRootCmd() error { + rootCmd.SetOutput(os.Stdout) + rootCmd.SetErr(os.Stderr) + + return nil +} + +func Execute() error { + if err := initRootCmd(); err != nil { + return err + } + + return rootCmd.Execute() +} diff --git a/cmd/evt/cmd/stress/stress.go b/cmd/evt/cmd/stress/stress.go new file mode 100644 index 000000000000..a868b2a41b3d --- /dev/null +++ b/cmd/evt/cmd/stress/stress.go @@ -0,0 +1,492 @@ +package stress + +import ( + "context" + "fmt" + "os" + "os/exec" + "os/signal" + "path/filepath" + "strconv" + "strings" + "sync" + "syscall" + "time" + + "github.com/aquasecurity/tracee/pkg/cmd/flags" + "github.com/aquasecurity/tracee/pkg/logger" + "github.com/aquasecurity/tracee/pkg/policy/v1beta1" + "github.com/dustin/go-humanize" + "github.com/spf13/cobra" + "golang.org/x/exp/slices" + "gopkg.in/yaml.v2" +) + +func init() { + stressCmd.Flags().StringArrayP( + "policy", + "p", + []string{}, + "\t\t\tPath to a policy or directory with policies to stress", + ) + stressCmd.Flags().StringSliceP( + "event", + "e", + []string{}, + "...\t\t\tSelect events to stress", + ) + + stressMode := os.Getenv("EVT_STRESS_MODE") + if stressMode == "" { + stressMode = fmt.Sprintf("weight=%d", defaultStressWeight) + } + stressCmd.Flags().StringVarP( + &stressMode, + "mode", + "m", + stressMode, + "\tStress mode", + ) +} + +type StressMode int + +const ( + StressModeTime StressMode = iota + StressModeWeight +) + +const ( + defaultStressTime = 10 * time.Minute + maxStressTime = 3 * time.Hour + defaultStressWeight = uint64(10_000_000) +) + +var ( + stressCmd = &cobra.Command{ + Use: "stress", + Aliases: []string{"s"}, + Short: "Stress the system with events", + RunE: stressRun, + SilenceErrors: true, + SilenceUsage: true, + } +) + +type StressConfig struct { + Mode StressModeConfig + Selected *SelectedToStress +} + +type StressModeConfig struct { + Selected StressMode + Weight uint64 + Time time.Duration +} + +func parseStressModeFlag(modeFlag string) (StressModeConfig, error) { + parts := strings.Split(modeFlag, "=") + mode := "" + value := "" + if len(parts) == 0 || len(parts) > 2 { + goto invalid_mode + } + + mode = parts[0] + if mode != "time" && mode != "weight" { + goto invalid_mode + } + + if len(parts) == 1 { + switch mode { + case "time": + return StressModeConfig{ + Selected: StressModeTime, + Time: defaultStressTime, + }, nil + + case "weight": + return StressModeConfig{ + Selected: StressModeWeight, + Weight: defaultStressWeight, + }, nil + } + } + + value = parts[1] + switch mode { + case "time": + t, err := time.ParseDuration(value) + if err != nil { + return StressModeConfig{}, fmt.Errorf("invalid stress time: %s", value) + } + return StressModeConfig{ + Selected: StressModeTime, + Time: t, + }, nil + + case "weight": + w, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return StressModeConfig{}, fmt.Errorf("invalid stress weight: %s", value) + } + return StressModeConfig{ + Selected: StressModeWeight, + Weight: w, + }, nil + } + +invalid_mode: + return StressModeConfig{}, fmt.Errorf("invalid stress mode: %s", modeFlag) +} + +func getStressConfig(cmd *cobra.Command) (*StressConfig, error) { + modeFlag := cmd.Flag("mode").Value.String() + modeConfig, err := parseStressModeFlag(modeFlag) + if err != nil { + return nil, err + } + + return &StressConfig{ + Mode: modeConfig, + }, nil +} + +func stressRun(cmd *cobra.Command, args []string) error { + logger.Init(logger.NewDefaultLoggingConfig()) + + cfg, err := getStressConfig(cmd) + if err != nil { + return err + } + + err = setSelectedToStress(cmd, cfg) + if err != nil { + return err + } + + fmt.Printf("Events selected to stress: %v\n", cfg.Selected.EventsNames) + + ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer cancel() + + var timeLimit time.Duration + if cfg.Mode.Selected == StressModeWeight { + timeLimit = maxStressTime + fmt.Printf("Stressing the system with weight of %v and time limit of %v\n", humanize.Comma(int64(cfg.Mode.Weight)), timeLimit) + fmt.Println("Weight mode triggers events based on the weight of the event, until it reaches the event amount or the time limit") + } else { + timeLimit = cfg.Mode.Time + fmt.Printf("Stressing the system without weight for %v\n", timeLimit) + fmt.Println("Time mode triggers events disregarding the weight, until it reaches the time limit") + } + + ctx, cancelTimeout := context.WithTimeoutCause(ctx, timeLimit, fmt.Errorf("stress time is up after %v", timeLimit)) + defer cancelTimeout() + + wg := &sync.WaitGroup{} + + for _, evt := range cfg.Selected.EventsNames { + _ = evt + // wg.Add(1) + // go stressEvent(ctx, evt) + } + + traceeStatus := RunTracee(ctx, wg, cfg.Selected, []string{}, []string{}) + + // block until receiving tracee status + err = <-traceeStatus + if err != nil { + return err + } + // unblock all stressEvent goroutines + + const coolDownTime = 10 * time.Second + fmt.Printf("tracee started: waiting %v for cool down...\n", coolDownTime) + select { + case <-time.After(coolDownTime): + case <-ctx.Done(): + goto cleanup + } + + // block until tracee is finished or context is done + for { + select { + case <-ctx.Done(): + goto cleanup + case err := <-traceeStatus: + if err != nil { + fmt.Println(err) + } + goto cleanup + } + } + +cleanup: + // drain closed channels + for err := range traceeStatus { + if err != nil { + fmt.Println(err) + } + } + + wg.Wait() + + return nil +} + +func RunTracee( + ctx context.Context, + wg *sync.WaitGroup, + selected *SelectedToStress, + filterOutCommScope []string, + filterOutPidScope []string, +) <-chan error { + errCh := make(chan error, 1) + + wg.Add(1) + go func() { + logger.Debugw("runTracee goroutine started") + defer logger.Debugw("runTracee goroutine finished") + + defer wg.Done() + defer close(errCh) + + var args []string + if selected.Origin == "policy" { + // update the policy files with the filter out scope + // updatePolicyFiles(selected.PolicyFiles, filterOutCommScope, filterOutPidScope) + + // save all updated policies to a temporary directory + const policiesTmpDir = "/tmp/evt-policies" + + err := writePolicyFiles(policiesTmpDir, selected.PolicyFiles) + if err != nil { + errCh <- fmt.Errorf("writing policy files: %w", err) + } + args = append(args, "-p", policiesTmpDir) + + } else { + args = append(args, "-s", "comm!=evt") + for _, outComm := range filterOutCommScope { + args = append(args, "-s", fmt.Sprintf("comm!=%s", outComm)) + } + + args = append(args, "-s", fmt.Sprintf("pid!=%d", os.Getpid())) + for _, outPid := range filterOutPidScope { + args = append(args, "-s", fmt.Sprintf("pid!=%s", outPid)) + } + + for _, evt := range selected.EventsFlags { + args = append(args, "-e", evt) + } + } + + // args = append(args, "--metrics") + args = append(args, "-o", "none") + + fmt.Println("Running tracee with args:", args) + + cmd := exec.CommandContext(ctx, "./dist/tracee", args...) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } + cmd.Cancel = func() error { + errCh <- fmt.Errorf("tracee being stopped: %w", context.Cause(ctx)) + return cmd.Process.Signal(syscall.SIGTERM) + } + cmd.Stdin = nil + + logFile, err := os.Create(fmt.Sprintf("tracee-%s.log", time.Now().Format("20060102-150405"))) + if err != nil { + errCh <- fmt.Errorf("creating log file: %w", err) + return + } + defer logFile.Close() + + cmd.Stdout = logFile + cmd.Stderr = logFile + + err = cmd.Start() + if err != nil { + errCh <- fmt.Errorf("starting tracee: %w", err) + return + } + + cmdWaitChan := waitForCommand(cmd) + select { + case err = <-cmdWaitChan: + if err == nil { + errCh <- fmt.Errorf("tracee finished with no error right after starting") + return + } + + // tracee finished with error + errCh <- fmt.Errorf("tracee finished: %w", err) + case <-time.After(1 * time.Second): + // give tracee some time to start + break + } + if err != nil { + return + } + + // tracee started, unblock the caller + errCh <- nil + + // wait for tracee to finish + err = <-cmdWaitChan + if err != nil { + errCh <- fmt.Errorf("tracee finished: %w", err) + } + }() + + return errCh +} + +func writePolicyFiles(policiesTmpDir string, policyFiles []v1beta1.PolicyFile) error { + err := os.RemoveAll(policiesTmpDir) + if err != nil { + return fmt.Errorf("removing policies tmp dir: %w", err) + } + err = os.MkdirAll(policiesTmpDir, 0755) + if err != nil { + return fmt.Errorf("creating policies tmp dir: %w", err) + } + + for _, policyFile := range policyFiles { + policyYaml, err := yaml.Marshal(policyFile) + if err != nil { + return fmt.Errorf("marshaling policy to yaml: %w", err) + } + + policyFilePath := fmt.Sprintf("%s/%s.yaml", policiesTmpDir, policyFile.Metadata.Name) + err = os.WriteFile(policyFilePath, policyYaml, 0755) + if err != nil { + return fmt.Errorf("writing policy to file: %w", err) + } + } + + return nil +} + +func waitForCommand(cmd *exec.Cmd) <-chan error { + done := make(chan error) + + go func() { + done <- cmd.Wait() + close(done) + }() + + return done +} + +type SelectedToStress struct { + Origin string + EventsNames []string + EventsFlags []string + PolicyFlags []string + PolicyFiles []v1beta1.PolicyFile +} + +func setSelectedToStress(cmd *cobra.Command, stressConfig *StressConfig) error { + policyFlags, err := cmd.Flags().GetStringArray("policy") + if err != nil { + return err + } + eventFlags, err := cmd.Flags().GetStringSlice("event") + if err != nil { + return err + } + + if len(policyFlags) == 0 && len(eventFlags) == 0 { + return fmt.Errorf("no policies or events provided") + } + if len(policyFlags) > 0 && len(eventFlags) > 0 { + return fmt.Errorf("policy and event flags cannot be used together") + } + + var events []string + var policyFiles []v1beta1.PolicyFile + origin := "" + if len(policyFlags) > 0 { + origin = "policy" + events, policyFiles, err = getEventsAndPoliciesFromPolicyFiles(policyFlags) + if err != nil { + return err + } + } else { + origin = "event" + events, err = getEventsFromEventFlags(eventFlags) + if err != nil { + return err + } + } + + slices.Sort(events) + events = slices.Compact(events) + + // set the selected events + stressConfig.Selected = &SelectedToStress{ + Origin: origin, + EventsNames: events, + EventsFlags: eventFlags, + PolicyFlags: policyFlags, + PolicyFiles: policyFiles, + } + + return nil +} + +func getFilterOutCommScope() string { + return fmt.Sprintf("comm!=%s", filepath.Base(os.Args[0])) +} + +func getFilterOutPidScope() string { + return fmt.Sprintf("pid!=%d", os.Getpid()) +} + +func getEventsAndPoliciesFromPolicyFiles(policyFlags []string) ([]string, []v1beta1.PolicyFile, error) { + policyInterfaceSlice, err := v1beta1.PoliciesFromPaths(policyFlags) + if err != nil { + return nil, nil, err + } + + policyFiles := make([]v1beta1.PolicyFile, 0, len(policyInterfaceSlice)) + for i := range policyInterfaceSlice { + p, ok := policyInterfaceSlice[i].(v1beta1.PolicyFile) + if !ok { + return nil, nil, fmt.Errorf("policy file is not a v1beta1.PolicyFile") + } + + if idx := slices.Index(p.Spec.Scope, "global"); idx != -1 { + p.Spec.Scope[idx] = getFilterOutCommScope() + } else { + p.Spec.Scope = append(p.Spec.Scope, getFilterOutCommScope()) + } + p.Spec.Scope = append(p.Spec.Scope, getFilterOutPidScope()) + + // store the modified policy file + policyFiles = append(policyFiles, p) + } + + _, policyEventsMap, err := flags.PrepareFilterMapsFromPolicies(policyInterfaceSlice) + if err != nil { + return nil, nil, err + } + + return policyEventsMap.GetSelectedEvents(), policyFiles, nil +} + +func getEventsFromEventFlags(eventFlags []string) ([]string, error) { + policyEventsMap, err := flags.PrepareEventMapFromFlags(eventFlags) + if err != nil { + return nil, err + } + + return policyEventsMap.GetSelectedEvents(), nil +} + +func Cmd() *cobra.Command { + return stressCmd +} diff --git a/cmd/evt/cmd/stress/triggers/arch_prctl.sh b/cmd/evt/cmd/stress/triggers/arch_prctl.sh new file mode 120000 index 000000000000..f00670e47d78 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/arch_prctl.sh @@ -0,0 +1 @@ +common/true.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/bpf_attach.sh b/cmd/evt/cmd/stress/triggers/bpf_attach.sh new file mode 120000 index 000000000000..8bb8ef0f5c5b --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/bpf_attach.sh @@ -0,0 +1 @@ +common/bpftrace.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/commit_creds.sh b/cmd/evt/cmd/stress/triggers/commit_creds.sh new file mode 120000 index 000000000000..a93f46bb9bb4 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/commit_creds.sh @@ -0,0 +1 @@ +common/sudo.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/common/bpftrace.sh b/cmd/evt/cmd/stress/triggers/common/bpftrace.sh new file mode 100755 index 000000000000..74f08e27dc5a --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/common/bpftrace.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# common + +# security_file_open 60 +# shared_object_loaded 44 +# sched_process_exec 2 +# arch_prctl 2 +# security_bpf_prog 4 +# kallsyms_lookup_name 2 +# kprobe_attach 1 +# bpf_attach 1 +# sched_process_exit 2 + +bpftrace -e 'kprobe:__do_sys_vfork { }' & +bpftrace_pid=$! +sleep 3 +kill -KILL $bpftrace_pid diff --git a/cmd/evt/cmd/stress/triggers/common/docker.sh b/cmd/evt/cmd/stress/triggers/common/docker.sh new file mode 100755 index 000000000000..58c5b76aa709 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/common/docker.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# common + + +sh -c 'docker run --rm -it ubuntu /bin/bash' diff --git a/cmd/evt/cmd/stress/triggers/common/mktemp-ln-rm.sh b/cmd/evt/cmd/stress/triggers/common/mktemp-ln-rm.sh new file mode 100755 index 000000000000..a45e66116c74 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/common/mktemp-ln-rm.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# common + +# sched_process_exec 5 +# security_file_open 17 +# shared_object_loaded 5 +# arch_prctl 5 +# security_inode_unlink 3 +# security_inode_symlink 1 +# sched_process_exit 5 + +file=$(mktemp /tmp/fileXXXXXX) +link1=$(mktemp /tmp/link1XXXXXX) + +rm -f "$link1" + +ln -s "$file" "$link1" +rm "$file" "$link1" diff --git a/cmd/evt/cmd/stress/triggers/common/ping.sh b/cmd/evt/cmd/stress/triggers/common/ping.sh new file mode 100755 index 000000000000..b33f2f2b83e5 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/common/ping.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# common + +# sched_process_exec 1 +# security_file_open 8 +# shared_object_loaded 4 +# arch_prctl 1 +# security_socket_create 3 +# security_socket_connect 1 +# sched_process_exit 1 + +ping 0.0.0.0 -c 1 diff --git a/cmd/evt/cmd/stress/triggers/common/self-comm.sh b/cmd/evt/cmd/stress/triggers/common/self-comm.sh new file mode 100755 index 000000000000..8a7f245ca1f6 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/common/self-comm.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# common + +# magic_write 2 +# security_file_open 1 +# do_truncate 1 +# sched_process_exit 1 + +echo "fake-comm" > /proc/self/comm # trigger magic-write by fake-comm +echo "fake-comm" > /proc/self/comm # trigger do_truncate by fake-comm diff --git a/cmd/evt/cmd/stress/triggers/common/sudo.sh b/cmd/evt/cmd/stress/triggers/common/sudo.sh new file mode 100755 index 000000000000..8f45c5b51e76 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/common/sudo.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# common + +# sched_process_exec 3 +# security_file_open 113 +# shared_object_loaded 40 +# arch_prctl 3 +# security_socket_create 19 +# commit_creds 4 +# sched_process_fork 3 +# sched_process_exit 3 +# socket_dup 2 + +sudo echo sudo >/dev/null diff --git a/cmd/evt/cmd/stress/triggers/common/timeout-nc.sh b/cmd/evt/cmd/stress/triggers/common/timeout-nc.sh new file mode 100755 index 000000000000..fdd1575f8246 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/common/timeout-nc.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# sched_process_exec 2 +# security_file_open 11 +# shared_object_loaded 2 +# arch_prctl 2 +# security_file_open 12 +# sched_process_fork 1 +# process_execute_failed 5 +# security_socket_create 1 +# security_socket_bind 1 +# sched_process_exit 2 + +timeout 0.1 nc -l -p 8888 diff --git a/cmd/evt/cmd/stress/triggers/common/true.sh b/cmd/evt/cmd/stress/triggers/common/true.sh new file mode 100755 index 000000000000..d14c3a84219a --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/common/true.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# common + +# sched_process_exec 1 +# security_file_open 2 +# shared_object_loaded 1 +# arch_prctl 1 +# sched_process_exit 1 + +/bin/true # full path to avoid shell built-in diff --git a/cmd/evt/cmd/stress/triggers/common/unshare-mkdir.sh b/cmd/evt/cmd/stress/triggers/common/unshare-mkdir.sh new file mode 100755 index 000000000000..66f11745db9d --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/common/unshare-mkdir.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# common + +# sched_process_exec 2 +# security_file_open 13 +# shared_object_loaded 2 +# arch_prctl 2 +# debugfs_create_dir 1 +# debugfs_create_file 2 +# security_socket_create 15 +# device_add 1 +# switch_task_ns 1 +# sched_process_fork 1 +# magic_write 3 +# security_sb_mount 1 +# process_execute_failed 4 +# sched_process_exit 2 + +unshare --mount --pid --net --ipc --uts --user --fork --map-root-user sh & +sleep 1 # wait for the unshare to complete and exit +exit 0 diff --git a/cmd/evt/cmd/stress/triggers/debugfs_create_dir.sh b/cmd/evt/cmd/stress/triggers/debugfs_create_dir.sh new file mode 120000 index 000000000000..5126d17dc5a3 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/debugfs_create_dir.sh @@ -0,0 +1 @@ +common/unshare-mkdir.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/debugfs_create_file.sh b/cmd/evt/cmd/stress/triggers/debugfs_create_file.sh new file mode 120000 index 000000000000..5126d17dc5a3 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/debugfs_create_file.sh @@ -0,0 +1 @@ +common/unshare-mkdir.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/device_add.sh b/cmd/evt/cmd/stress/triggers/device_add.sh new file mode 120000 index 000000000000..5126d17dc5a3 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/device_add.sh @@ -0,0 +1 @@ +common/unshare-mkdir.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/do_truncate.sh b/cmd/evt/cmd/stress/triggers/do_truncate.sh new file mode 120000 index 000000000000..b7d3fd2c787b --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/do_truncate.sh @@ -0,0 +1 @@ +common/self-comm.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/kallsyms_lookup_name.sh b/cmd/evt/cmd/stress/triggers/kallsyms_lookup_name.sh new file mode 120000 index 000000000000..8bb8ef0f5c5b --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/kallsyms_lookup_name.sh @@ -0,0 +1 @@ +common/bpftrace.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/kprobe_attach.sh b/cmd/evt/cmd/stress/triggers/kprobe_attach.sh new file mode 120000 index 000000000000..8bb8ef0f5c5b --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/kprobe_attach.sh @@ -0,0 +1 @@ +common/bpftrace.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/list b/cmd/evt/cmd/stress/triggers/list new file mode 100644 index 000000000000..fe99f18fba3a --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/list @@ -0,0 +1,19 @@ +arch_prctl,bpf_attach,call_usermodehelper,commit_creds,container_create,container_remove,debugfs_create_dir,debugfs_create_file,device_add,dirty_pipe_splice,do_truncate,existing_container,hidden_kernel_module,hooked_syscall,init_namespaces,kallsyms_lookup_name,kprobe_attach,magic_write,mem_prot_alert,module_load,print_mem_dump,proc_create,process_execute_failed,process_vm_writev,ptrace,register_chrdev,sched_process_exec,sched_process_exit,sched_process_fork,security_bpf_prog,security_file_open,security_inode_rename,security_inode_symlink,security_inode_unlink,security_path_notify,security_sb_mount,security_socket_bind,security_socket_connect,security_socket_create,set_fs_pwd,shared_object_loaded,socket_dup,switch_task_ns,symbols_loaded + + + +call_usermodehelper +dirty_pipe_splice +mem_prot_alert +process_vm_writev +register_chrdev +set_fs_pwd + + +pid 1: +container_create +container_remove + +dockerd: +security_inode_rename + diff --git a/cmd/evt/cmd/stress/triggers/magic_write.sh b/cmd/evt/cmd/stress/triggers/magic_write.sh new file mode 120000 index 000000000000..b7d3fd2c787b --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/magic_write.sh @@ -0,0 +1 @@ +common/self-comm.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/process_execute_failed.sh b/cmd/evt/cmd/stress/triggers/process_execute_failed.sh new file mode 120000 index 000000000000..0f387e997ad6 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/process_execute_failed.sh @@ -0,0 +1 @@ +common/timeout-nc.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/ptrace.sh b/cmd/evt/cmd/stress/triggers/ptrace.sh new file mode 100755 index 000000000000..f432a71d05b2 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/ptrace.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# ptrace + +# sched_process_exec 2 +# security_file_open 14 +# shared_object_loaded 6 +# arch_prctl 2 +# sched_process_fork 2 +# ptrace 287 +# sched_process_exit 4 + +strace /bin/true # full path to avoid shell built-in diff --git a/cmd/evt/cmd/stress/triggers/sched_process_exec.sh b/cmd/evt/cmd/stress/triggers/sched_process_exec.sh new file mode 120000 index 000000000000..f00670e47d78 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/sched_process_exec.sh @@ -0,0 +1 @@ +common/true.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/sched_process_exit.sh b/cmd/evt/cmd/stress/triggers/sched_process_exit.sh new file mode 120000 index 000000000000..f00670e47d78 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/sched_process_exit.sh @@ -0,0 +1 @@ +common/true.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/sched_process_fork.sh b/cmd/evt/cmd/stress/triggers/sched_process_fork.sh new file mode 120000 index 000000000000..0f387e997ad6 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/sched_process_fork.sh @@ -0,0 +1 @@ +common/timeout-nc.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/security_bpf_prog.sh b/cmd/evt/cmd/stress/triggers/security_bpf_prog.sh new file mode 100755 index 000000000000..b1d850b47849 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/security_bpf_prog.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# security_bpf_prog + +# sched_process_exec 1 +# arch_prctl 2 +# security_bpf_prog 487 +# security_file_open 3 +# sched_process_exit 1 + +bpftool prog dump xlated name trace_execute_finished diff --git a/cmd/evt/cmd/stress/triggers/security_file_open.sh b/cmd/evt/cmd/stress/triggers/security_file_open.sh new file mode 120000 index 000000000000..f00670e47d78 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/security_file_open.sh @@ -0,0 +1 @@ +common/true.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/security_inode_symlink.sh b/cmd/evt/cmd/stress/triggers/security_inode_symlink.sh new file mode 120000 index 000000000000..ac2bb20b000e --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/security_inode_symlink.sh @@ -0,0 +1 @@ +common/mktemp-ln-rm.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/security_inode_unlink.sh b/cmd/evt/cmd/stress/triggers/security_inode_unlink.sh new file mode 120000 index 000000000000..ac2bb20b000e --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/security_inode_unlink.sh @@ -0,0 +1 @@ +common/mktemp-ln-rm.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/security_path_notify.sh b/cmd/evt/cmd/stress/triggers/security_path_notify.sh new file mode 100755 index 000000000000..f29d07d52ce1 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/security_path_notify.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# sched_process_exec 1 +# security_file_open 6 +# shared_object_loaded 5 +# arch_prctl 1 +# security_path_notify 1 +# sched_process_exit 1 + +inotifywait -m /tmp -t 1 diff --git a/cmd/evt/cmd/stress/triggers/security_sb_mount.sh b/cmd/evt/cmd/stress/triggers/security_sb_mount.sh new file mode 120000 index 000000000000..5126d17dc5a3 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/security_sb_mount.sh @@ -0,0 +1 @@ +common/unshare-mkdir.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/security_socket_bind.sh b/cmd/evt/cmd/stress/triggers/security_socket_bind.sh new file mode 120000 index 000000000000..0f387e997ad6 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/security_socket_bind.sh @@ -0,0 +1 @@ +common/timeout-nc.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/security_socket_connect.sh b/cmd/evt/cmd/stress/triggers/security_socket_connect.sh new file mode 120000 index 000000000000..603cfb870512 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/security_socket_connect.sh @@ -0,0 +1 @@ +common/ping.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/security_socket_create.sh b/cmd/evt/cmd/stress/triggers/security_socket_create.sh new file mode 120000 index 000000000000..603cfb870512 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/security_socket_create.sh @@ -0,0 +1 @@ +common/ping.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/shared_object_loaded.sh b/cmd/evt/cmd/stress/triggers/shared_object_loaded.sh new file mode 120000 index 000000000000..f00670e47d78 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/shared_object_loaded.sh @@ -0,0 +1 @@ +common/true.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/socked_dup.sh b/cmd/evt/cmd/stress/triggers/socked_dup.sh new file mode 120000 index 000000000000..a93f46bb9bb4 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/socked_dup.sh @@ -0,0 +1 @@ +common/sudo.sh \ No newline at end of file diff --git a/cmd/evt/cmd/stress/triggers/switch_task_ns.sh b/cmd/evt/cmd/stress/triggers/switch_task_ns.sh new file mode 120000 index 000000000000..5126d17dc5a3 --- /dev/null +++ b/cmd/evt/cmd/stress/triggers/switch_task_ns.sh @@ -0,0 +1 @@ +common/unshare-mkdir.sh \ No newline at end of file diff --git a/cmd/evt/main.go b/cmd/evt/main.go new file mode 100644 index 000000000000..7fb60c83ea2b --- /dev/null +++ b/cmd/evt/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "os" + + "github.com/aquasecurity/tracee/cmd/evt/cmd" +) + +func main() { + err := cmd.Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + os.Exit(0) +} diff --git a/go.mod b/go.mod index 2c5045052b1a..5beb28d798b2 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/aquasecurity/tracee/types v0.0.0-20241008181102-d40bc1f81863 github.com/containerd/containerd v1.7.21 github.com/docker/docker v26.1.5+incompatible + github.com/dustin/go-humanize v1.0.1 github.com/golang/protobuf v1.5.4 github.com/google/gopacket v1.1.19 github.com/grafana/pyroscope-go v1.1.1 diff --git a/pkg/cmd/flags/event.go b/pkg/cmd/flags/event.go index b1343c24c481..ebb89585b103 100644 --- a/pkg/cmd/flags/event.go +++ b/pkg/cmd/flags/event.go @@ -28,6 +28,23 @@ type eventFlag struct { filter string } +func (pe PolicyEventMap) GetSelectedEvents() []string { + evtMap := make(map[string]struct{}) + + for _, events := range pe { + for _, event := range events.eventFlags { + evtMap[event.eventName] = struct{}{} + } + } + + evts := make([]string, 0, len(evtMap)) + for evt := range evtMap { + evts = append(evts, evt) + } + + return evts +} + func PrepareEventMapFromFlags(eventsArr []string) (PolicyEventMap, error) { // parse and store events flags var evtFlags []eventFlag diff --git a/pkg/ebpf/c/common/buffer.h b/pkg/ebpf/c/common/buffer.h index 9bcef571e9a3..c171c40c1324 100644 --- a/pkg/ebpf/c/common/buffer.h +++ b/pkg/ebpf/c/common/buffer.h @@ -459,12 +459,17 @@ statfunc int save_args_to_submit_buf(event_data_t *event, args_t *args) } #ifdef DEBUG -struct event_counts { +typedef struct { + u64 attempts; + u64 failures; +} event_stats_values_t; + +struct events_stats { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, MAX_EVENT_ID); - __type(key, u32); // eventid - __type(value, u64); // count -} event_counts SEC(".maps"); + __type(key, u32); // eventid + __type(value, event_stats_values_t); +} events_stats SEC(".maps"); #endif statfunc int events_perf_submit(program_data_t *p, long ret) @@ -493,14 +498,20 @@ statfunc int events_perf_submit(program_data_t *p, long ret) : : [size] "r"(size), [max_size] "i"(MAX_EVENT_SIZE)); + u64 perf_ret = bpf_perf_event_output(p->ctx, &events, BPF_F_CURRENT_CPU, p->event, size); + #ifdef DEBUG - // increment event count before event submission attempt - u64 *event_count = bpf_map_lookup_elem(&event_counts, &p->event->context.eventid); - if (event_count) - __sync_fetch_and_add(event_count, 1); + // update event stats + event_stats_values_t *evt_stat = bpf_map_lookup_elem(&events_stats, &p->event->context.eventid); + if (unlikely(evt_stat == NULL)) + return perf_ret; + + __sync_fetch_and_add(&evt_stat->attempts, 1); + if (perf_ret < 0) + __sync_fetch_and_add(&evt_stat->failures, 1); #endif - return bpf_perf_event_output(p->ctx, &events, BPF_F_CURRENT_CPU, p->event, size); + return perf_ret; } statfunc int signal_perf_submit(void *ctx, controlplane_signal_t *sig) diff --git a/pkg/ebpf/perf_count.go b/pkg/ebpf/perf_count.go index 8cc9f1326e79..52f3383014c3 100644 --- a/pkg/ebpf/perf_count.go +++ b/pkg/ebpf/perf_count.go @@ -11,29 +11,35 @@ import ( "github.com/aquasecurity/tracee/pkg/logger" ) -// countPerfEventWrites counts the number of times each event is attempted -// to be written to the perf buffer. +type eventStatsValues struct { + attempts uint64 + failures uint64 +} + +// countPerfEventWrites logs the number of attempts and failures to write to the perf event buffer +// for each event type, as well as the total attempts and failures. func (t *Tracee) countPerfEventWrites(ctx context.Context) { logger.Debugw("Starting countPerfEventWrites goroutine") defer logger.Debugw("Stopped countPerfEventWrites goroutine") - evtsCountsBPFMap, err := t.bpfModule.GetMap("event_counts") + evtsCountsBPFMap, err := t.bpfModule.GetMap("events_stats") if err != nil { - logger.Errorw("Failed to get event_counts map", "error", err) + logger.Errorw("Failed to get events_stats map", "error", err) return } + evtStatZero := eventStatsValues{} for _, id := range t.policyManager.EventsSelected() { key := uint32(id) - value := uint64(0) - err := evtsCountsBPFMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&value)) + err := evtsCountsBPFMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&evtStatZero)) if err != nil { - logger.Errorw("Failed to update event_counts map", "error", err) + logger.Errorw("Failed to update events_stats map", "error", err) } } - total := counter.NewCounter(0) - evtsCounts := make(map[uint32]uint64) + totalAttempts := counter.NewCounter(0) + totalFailures := counter.NewCounter(0) + evtsCounts := make(map[uint32]eventStatsValues) ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() @@ -42,36 +48,55 @@ func (t *Tracee) countPerfEventWrites(ctx context.Context) { case <-ctx.Done(): return case <-ticker.C: + // Get the counts of each event from the BPF map into a hashmap iter := evtsCountsBPFMap.Iterator() for iter.Next() { key := binary.LittleEndian.Uint32(iter.Key()) value, err := evtsCountsBPFMap.GetValue(unsafe.Pointer(&key)) if err != nil { - logger.Errorw("Failed to get value from event_counts map", "error", err) + logger.Errorw("Failed to get value from events_stats map", "error", err) continue } - evtsCounts[key] = binary.LittleEndian.Uint64(value) + evtsCounts[key] = eventStatsValues{ + attempts: binary.LittleEndian.Uint64(value[0:8]), + failures: binary.LittleEndian.Uint64(value[8:16]), + } } - total.Set(0) + // Get the counts of each event from the hashmap into a slice (key value pairs) + // and calculate the total count + keyValsAttempts := make([]interface{}, 0, len(evtsCounts)*2+1) + keyValsFailures := make([]interface{}, 0, len(evtsCounts)*2+1) + totalAttempts.Set(0) + totalFailures.Set(0) for k, v := range evtsCounts { - if v == 0 { - continue - } - err := total.Increment(v) + keyValsAttempts = append(keyValsAttempts, + events.Core.GetDefinitionByID(events.ID(k)).GetName(), + v.attempts, + ) + keyValsFailures = append(keyValsFailures, + events.Core.GetDefinitionByID(events.ID(k)).GetName(), + v.failures, + ) + + err := totalAttempts.Increment(v.attempts) if err != nil { logger.Errorw("Failed to increment total counter", "error", err) } - - logger.Debugw("Event sending attempts", - "event", events.Core.GetDefinitionByID(events.ID(k)).GetName(), - "count", v, - ) + err = totalFailures.Increment(v.failures) + if err != nil { + logger.Errorw("Failed to increment total failures counter", "error", err) + } } - logger.Debugw("Event sending attempts", "total", total.Get()) - t.stats.BPFPerfEventWrites.Set(total.Get()) + // Log the counts + keyValsAttempts = append(keyValsAttempts, "total", totalAttempts.Get()) + logger.Infow("Event sending attempts", keyValsAttempts...) + keyValsFailures = append(keyValsFailures, "total", totalFailures.Get()) + logger.Infow("Event sending failures", keyValsFailures...) + t.stats.BPFPerfEventWriteAttempts.Set(totalAttempts.Get()) + t.stats.BPFPerfEventWriteFailures.Set(totalFailures.Get()) } } } diff --git a/pkg/metrics/stats.go b/pkg/metrics/stats.go index 45f86cd09ef6..8498e38397ea 100644 --- a/pkg/metrics/stats.go +++ b/pkg/metrics/stats.go @@ -9,16 +9,17 @@ import ( // When updating this struct, please make sure to update the relevant exporting functions type Stats struct { - EventCount counter.Counter - EventsFiltered counter.Counter - NetCapCount counter.Counter // network capture events - BPFLogsCount counter.Counter - BPFPerfEventWrites counter.Counter // calls to write to the event perf buffer - ErrorCount counter.Counter - LostEvCount counter.Counter - LostWrCount counter.Counter - LostNtCapCount counter.Counter // lost network capture events - LostBPFLogsCount counter.Counter + EventCount counter.Counter + EventsFiltered counter.Counter + NetCapCount counter.Counter // network capture events + BPFLogsCount counter.Counter + BPFPerfEventWriteAttempts counter.Counter // calls to write to the event perf buffer + BPFPerfEventWriteFailures counter.Counter // failed calls to write to the event perf buffer + ErrorCount counter.Counter + LostEvCount counter.Counter + LostWrCount counter.Counter + LostNtCapCount counter.Counter // lost network capture events + LostBPFLogsCount counter.Counter } // Register Stats to prometheus metrics exporter @@ -65,9 +66,20 @@ func (stats *Stats) RegisterPrometheus() error { err = prometheus.Register(prometheus.NewCounterFunc(prometheus.CounterOpts{ Namespace: "tracee_ebpf", - Name: "bpf_perf_event_writes_total", + Name: "bpf_perf_event_write_attempts_total", Help: "total number of calls to write to the event perf buffer", - }, func() float64 { return float64(stats.BPFPerfEventWrites.Get()) })) + }, func() float64 { return float64(stats.BPFPerfEventWriteAttempts.Get()) })) + + if err != nil { + return errfmt.WrapError(err) + } + + // This must be similar to LostWrCount but is updated in a different manner + err = prometheus.Register(prometheus.NewCounterFunc(prometheus.CounterOpts{ + Namespace: "tracee_ebpf", + Name: "bpf_perf_event_write_failures_total", + Help: "total number of failed calls to write to the event perf buffer", + }, func() float64 { return float64(stats.BPFPerfEventWriteFailures.Get()) })) if err != nil { return errfmt.WrapError(err) diff --git a/pkg/server/grpc/diagnostic.go b/pkg/server/grpc/diagnostic.go index 4f703cb81bca..93ac65194b01 100644 --- a/pkg/server/grpc/diagnostic.go +++ b/pkg/server/grpc/diagnostic.go @@ -17,16 +17,17 @@ type DiagnosticService struct { func (s *DiagnosticService) GetMetrics(ctx context.Context, in *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { stats := s.tracee.Stats() metrics := &pb.GetMetricsResponse{ - EventCount: stats.EventCount.Get(), - EventsFiltered: stats.EventsFiltered.Get(), - NetCapCount: stats.NetCapCount.Get(), - BPFLogsCount: stats.BPFLogsCount.Get(), - BPFPerfEventWrites: stats.BPFPerfEventWrites.Get(), // only available in debug build - ErrorCount: stats.ErrorCount.Get(), - LostEvCount: stats.LostEvCount.Get(), - LostWrCount: stats.LostWrCount.Get(), - LostNtCapCount: stats.LostNtCapCount.Get(), - LostBPFLogsCount: stats.LostBPFLogsCount.Get(), + EventCount: stats.EventCount.Get(), + EventsFiltered: stats.EventsFiltered.Get(), + NetCapCount: stats.NetCapCount.Get(), + BPFLogsCount: stats.BPFLogsCount.Get(), + BPFPerfEventWriteAttempts: stats.BPFPerfEventWriteAttempts.Get(), // only available in debug build + BPFPerfEventWriteFailures: stats.BPFPerfEventWriteFailures.Get(), // only available in debug build + ErrorCount: stats.ErrorCount.Get(), + LostEvCount: stats.LostEvCount.Get(), + LostWrCount: stats.LostWrCount.Get(), + LostNtCapCount: stats.LostNtCapCount.Get(), + LostBPFLogsCount: stats.LostBPFLogsCount.Get(), } return metrics, nil