Skip to content

Commit

Permalink
[CSM] Track image tags of syscalls in activity trees
Browse files Browse the repository at this point in the history
  • Loading branch information
Gui774ume committed Jul 11, 2024
1 parent e6f6737 commit f0ba9b9
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 := "<<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"5\">"
for _, s := range p.Syscalls {
label += "<TR><TD>" + model.Syscall(s).String() + "</TD></TR>"
label += "<TR><TD>" + model.Syscall(s.Syscall).String() + "</TD></TR>"
}
label += "</TABLE>>"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Stats struct {
DNSNodes int64
SocketNodes int64
IMDSNodes int64
SyscallNodes int64

counts map[model.EventType]*statsPerEventType
}
Expand Down Expand Up @@ -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
}

Expand Down
37 changes: 28 additions & 9 deletions pkg/security/security_profile/activity_tree/process_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type ProcessNode struct {
IMDSEvents map[model.IMDSEvent]*IMDSNode

Sockets []*SocketNode
Syscalls []int
Syscalls []*SyscallNode
Children []*ProcessNode
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -450,16 +464,21 @@ 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 {
newChildren = append(newChildren, child)
}
}
pn.Children = newChildren

for _, id := range pn.Syscalls {
SyscallsMask[id] = id
}
return false
}
46 changes: 46 additions & 0 deletions pkg/security/security_profile/activity_tree/syscalls_node.go
Original file line number Diff line number Diff line change
@@ -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,
}
}

0 comments on commit f0ba9b9

Please sign in to comment.