From f0ba9b980274a0372f243ec7e42a86bc61bfefec Mon Sep 17 00:00:00 2001 From: gui774ume Date: Wed, 10 Jul 2024 18:14:55 +0200 Subject: [PATCH] [CSM] Track image tags of syscalls in activity trees --- .../activity_tree/activity_tree.go | 2 +- .../activity_tree/activity_tree_graph.go | 2 +- .../activity_tree_proto_dec_v1.go | 4 +- .../activity_tree_proto_enc_v1.go | 2 +- .../activity_tree/activity_tree_stats.go | 2 + .../activity_tree/process_node.go | 37 +++++++++++---- .../activity_tree/syscalls_node.go | 46 +++++++++++++++++++ 7 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 pkg/security/security_profile/activity_tree/syscalls_node.go diff --git a/pkg/security/security_profile/activity_tree/activity_tree.go b/pkg/security/security_profile/activity_tree/activity_tree.go index b4d24ae1b2b9b6..f64b439f432bd4 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree.go +++ b/pkg/security/security_profile/activity_tree/activity_tree.go @@ -401,7 +401,7 @@ func (at *ActivityTree) insertEvent(event *model.Event, dryRun bool, insertMissi case model.BindEventType: return node.InsertBindEvent(event, imageTag, generationType, at.Stats, dryRun), nil case model.SyscallsEventType: - return node.InsertSyscalls(event, at.SyscallsMask), nil + return node.InsertSyscalls(event, imageTag, at.SyscallsMask, at.Stats, dryRun), nil case model.ExitEventType: // Update the exit time of the process (this is purely informative, do not rely on timestamps to detect // execed children) diff --git a/pkg/security/security_profile/activity_tree/activity_tree_graph.go b/pkg/security/security_profile/activity_tree/activity_tree_graph.go index 4ac254f015276b..14f70a5a98dddd 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree_graph.go +++ b/pkg/security/security_profile/activity_tree/activity_tree_graph.go @@ -293,7 +293,7 @@ func (at *ActivityTree) prepareFileNode(f *FileNode, data *utils.Graph, prefix s func (at *ActivityTree) prepareSyscallsNode(p *ProcessNode, data *utils.Graph) utils.GraphID { label := "<" for _, s := range p.Syscalls { - label += "" + label += "" } label += "
" + model.Syscall(s).String() + "
" + model.Syscall(s.Syscall).String() + "
>" diff --git a/pkg/security/security_profile/activity_tree/activity_tree_proto_dec_v1.go b/pkg/security/security_profile/activity_tree/activity_tree_proto_dec_v1.go index e4140c253144e7..8ab4eac1c9d64f 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree_proto_dec_v1.go +++ b/pkg/security/security_profile/activity_tree/activity_tree_proto_dec_v1.go @@ -38,7 +38,7 @@ func protoDecodeProcessActivityNode(parent ProcessNodeParent, pan *adproto.Proce DNSNames: make(map[string]*DNSNode, len(pan.DnsNames)), IMDSEvents: make(map[model.IMDSEvent]*IMDSNode, len(pan.ImdsEvents)), Sockets: make([]*SocketNode, 0, len(pan.Sockets)), - Syscalls: make([]int, 0, len(pan.Syscalls)), + Syscalls: make([]*SyscallNode, 0, len(pan.Syscalls)), ImageTags: pan.ImageTags, } @@ -73,7 +73,7 @@ func protoDecodeProcessActivityNode(parent ProcessNodeParent, pan *adproto.Proce } for _, sysc := range pan.Syscalls { - ppan.Syscalls = append(ppan.Syscalls, int(sysc)) + ppan.Syscalls = append(ppan.Syscalls, NewSyscallNode(int(sysc), "", Unknown)) } return ppan diff --git a/pkg/security/security_profile/activity_tree/activity_tree_proto_enc_v1.go b/pkg/security/security_profile/activity_tree/activity_tree_proto_enc_v1.go index bc54cab5015e43..05f81ed858b9e2 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree_proto_enc_v1.go +++ b/pkg/security/security_profile/activity_tree/activity_tree_proto_enc_v1.go @@ -71,7 +71,7 @@ func processActivityNodeToProto(pan *ProcessNode) *adproto.ProcessActivityNode { } for _, sysc := range pan.Syscalls { - ppan.Syscalls = append(ppan.Syscalls, uint32(sysc)) + ppan.Syscalls = append(ppan.Syscalls, uint32(sysc.Syscall)) } return ppan diff --git a/pkg/security/security_profile/activity_tree/activity_tree_stats.go b/pkg/security/security_profile/activity_tree/activity_tree_stats.go index 0ee7dbbc1b82f8..7f10be52327b44 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree_stats.go +++ b/pkg/security/security_profile/activity_tree/activity_tree_stats.go @@ -26,6 +26,7 @@ type Stats struct { DNSNodes int64 SocketNodes int64 IMDSNodes int64 + SyscallNodes int64 counts map[model.EventType]*statsPerEventType } @@ -72,6 +73,7 @@ func (stats *Stats) ApproximateSize() int64 { total += stats.DNSNodes * int64(unsafe.Sizeof(DNSNode{})) // 24 total += stats.SocketNodes * int64(unsafe.Sizeof(SocketNode{})) // 40 total += stats.IMDSNodes * int64(unsafe.Sizeof(IMDSNode{})) + total += stats.SyscallNodes * int64(unsafe.Sizeof(SyscallNode{})) return total } diff --git a/pkg/security/security_profile/activity_tree/process_node.go b/pkg/security/security_profile/activity_tree/process_node.go index 450774141ee70e..5a20b098f8adb5 100644 --- a/pkg/security/security_profile/activity_tree/process_node.go +++ b/pkg/security/security_profile/activity_tree/process_node.go @@ -43,7 +43,7 @@ type ProcessNode struct { IMDSEvents map[model.IMDSEvent]*IMDSNode Sockets []*SocketNode - Syscalls []int + Syscalls []*SyscallNode Children []*ProcessNode } @@ -201,20 +201,31 @@ func (pn *ProcessNode) Matches(entry *model.Process, matchArgs bool, normalize b } // InsertSyscalls inserts the syscall of the process in the dump -func (pn *ProcessNode) InsertSyscalls(e *model.Event, syscallMask map[int]int) bool { +func (pn *ProcessNode) InsertSyscalls(e *model.Event, imageTag string, syscallMask map[int]int, stats *Stats, dryRun bool) bool { var hasNewSyscalls bool newSyscallLoop: for _, newSyscall := range e.Syscalls.Syscalls { for _, existingSyscall := range pn.Syscalls { - if existingSyscall == int(newSyscall) { + if existingSyscall.Syscall == int(newSyscall) { + if imageTag != "" && !slices.Contains(existingSyscall.ImageTags, imageTag) { + existingSyscall.ImageTags = append(existingSyscall.ImageTags, imageTag) + } continue newSyscallLoop } } - pn.Syscalls = append(pn.Syscalls, int(newSyscall)) - syscallMask[int(newSyscall)] = int(newSyscall) + if !dryRun { + pn.Syscalls = append(pn.Syscalls, NewSyscallNode(int(newSyscall), imageTag, Runtime)) + syscallMask[int(newSyscall)] = int(newSyscall) + stats.SyscallNodes++ + } hasNewSyscalls = true + if dryRun { + // exit early + break + } } + return hasNewSyscalls } @@ -390,6 +401,9 @@ func (pn *ProcessNode) TagAllNodes(imageTag string) { for _, sock := range pn.Sockets { sock.appendImageTag(imageTag) } + for _, scall := range pn.Syscalls { + scall.appendImageTag(imageTag) + } for _, child := range pn.Children { child.TagAllNodes(imageTag) } @@ -450,6 +464,15 @@ func (pn *ProcessNode) EvictImageTag(imageTag string, DNSNames *utils.StringKeys } pn.Sockets = newSockets + newSyscalls := []*SyscallNode{} + for _, scall := range pn.Syscalls { + if shouldRemove := scall.evictImageTag(imageTag); !shouldRemove { + newSyscalls = append(newSyscalls, scall) + SyscallsMask[scall.Syscall] = scall.Syscall + } + } + pn.Syscalls = newSyscalls + newChildren := []*ProcessNode{} for _, child := range pn.Children { if shouldRemoveNode := child.EvictImageTag(imageTag, DNSNames, SyscallsMask); !shouldRemoveNode { @@ -457,9 +480,5 @@ func (pn *ProcessNode) EvictImageTag(imageTag string, DNSNames *utils.StringKeys } } pn.Children = newChildren - - for _, id := range pn.Syscalls { - SyscallsMask[id] = id - } return false } diff --git a/pkg/security/security_profile/activity_tree/syscalls_node.go b/pkg/security/security_profile/activity_tree/syscalls_node.go new file mode 100644 index 00000000000000..3ccd4577962535 --- /dev/null +++ b/pkg/security/security_profile/activity_tree/syscalls_node.go @@ -0,0 +1,46 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +// Package activitytree holds activitytree related files +package activitytree + +// SyscallNode is used to store a syscall node +type SyscallNode struct { + ImageTags []string + GenerationType NodeGenerationType + + Syscall int +} + +func (sn *SyscallNode) appendImageTag(imageTag string) { + sn.ImageTags, _ = AppendIfNotPresent(sn.ImageTags, imageTag) +} + +func (sn *SyscallNode) evictImageTag(imageTag string) bool { + imageTags, removed := removeImageTagFromList(sn.ImageTags, imageTag) + if !removed { + return false + } + if len(imageTags) == 0 { + return true + } + sn.ImageTags = imageTags + return false +} + +// NewSyscallNode returns a new SyscallNode instance +func NewSyscallNode(syscall int, imageTag string, generationType NodeGenerationType) *SyscallNode { + var imageTags []string + if len(imageTag) != 0 { + imageTags = append(imageTags, imageTag) + } + return &SyscallNode{ + Syscall: syscall, + GenerationType: generationType, + ImageTags: imageTags, + } +}