Skip to content

Commit

Permalink
Improve parent discarder performance (#9727)
Browse files Browse the repository at this point in the history
  • Loading branch information
safchain authored Nov 12, 2021
1 parent 067ed20 commit 99089bc
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 83 deletions.
153 changes: 96 additions & 57 deletions pkg/security/probe/discarders.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ import (
"math/rand"
"path"
"path/filepath"
"regexp"
"strings"
"time"

manager "github.com/DataDog/ebpf-manager"
lib "github.com/cilium/ebpf"
"github.com/hashicorp/golang-lru/simplelru"
"github.com/pkg/errors"

"github.com/DataDog/datadog-agent/pkg/security/ebpf"
Expand Down Expand Up @@ -141,25 +139,20 @@ type inodeDiscarder struct {
// inodeDiscarders is used to issue eRPC discarder requests
type inodeDiscarders struct {
*lib.Map
erpc *ERPC
revisions *lib.Map
revisionCache [discarderRevisionSize]uint32
dentryResolver *DentryResolver
regexCache *simplelru.LRU
erpc *ERPC
revisions *lib.Map
revisionCache [discarderRevisionSize]uint32
dentryResolver *DentryResolver
rs *rules.RuleSet
parentDiscarderFnc map[eval.Field]func(dirname string) (bool, error)
}

func newInodeDiscarders(inodesMap, revisionsMap *lib.Map, erpc *ERPC, dentryResolver *DentryResolver) (*inodeDiscarders, error) {
regexCache, err := simplelru.NewLRU(64, nil)
if err != nil {
return nil, err
}

return &inodeDiscarders{
Map: inodesMap,
erpc: erpc,
revisions: revisionsMap,
dentryResolver: dentryResolver,
regexCache: regexCache,
}, nil
}

Expand Down Expand Up @@ -219,30 +212,51 @@ var (

// Important should always be called after having checked that the file is not a discarder itself otherwise it can report incorrect
// parent discarder
func isParentPathDiscarder(rs *rules.RuleSet, regexCache *simplelru.LRU, eventType model.EventType, filenameField eval.Field, filename string) (bool, error) {
func (id *inodeDiscarders) isParentPathDiscarder(rs *rules.RuleSet, eventType model.EventType, filenameField eval.Field, filename string) (bool, error) {
dirname := filepath.Dir(filename)

bucket := rs.GetBucket(eventType.String())
if bucket == nil {
return false, nil
// check cache first
if id.rs != rs {
id.parentDiscarderFnc = make(map[eval.Field]func(dirname string) (bool, error))
id.rs = rs
} else {
if fnc, exists := id.parentDiscarderFnc[filenameField]; exists {
return fnc(dirname)
}
}

event := discarderEvent
defer func() {
*discarderEvent = eventZero
}()

if _, err := event.GetFieldType(filenameField); err != nil {
return false, err
// compile
if _, err := discarderEvent.GetFieldType(filenameField); err != nil {
fnc := func(dirname string) (bool, error) {
return false, err
}
id.parentDiscarderFnc[filenameField] = fnc
return fnc(dirname)
}

if !strings.HasSuffix(filenameField, model.PathSuffix) {
return false, errors.New("path suffix not found")
fnc := func(dirname string) (bool, error) {
return false, errors.New("path suffix not found")
}
id.parentDiscarderFnc[filenameField] = fnc
return fnc(dirname)
}

basenameField := strings.Replace(filenameField, model.PathSuffix, model.NameSuffix, 1)
if _, err := event.GetFieldType(basenameField); err != nil {
return false, err
if _, err := discarderEvent.GetFieldType(basenameField); err != nil {
fnc := func(dirname string) (bool, error) {
return false, err
}
id.parentDiscarderFnc[filenameField] = fnc
return fnc(dirname)
}

var valueFnc func(dirname string) (bool, bool, error)
var valueFncs []func(dirname string) (bool, bool, error)

bucket := rs.GetBucket(eventType.String())
if bucket == nil {
return false, nil
}

for _, rule := range bucket.GetRules() {
Expand All @@ -262,63 +276,88 @@ func isParentPathDiscarder(rs *rules.RuleSet, regexCache *simplelru.LRU, eventTy
if values := rule.GetFieldValues(filenameField); len(values) > 0 {
for _, value := range values {
if value.Type == eval.PatternValueType {
if value.Regexp.MatchString(dirname) {
return false, nil
valueDir := path.Dir(value.Value.(string))
regexDir, err := eval.PatternToRegexp(valueDir)
if err != nil {
return false, err
}

valueDir := path.Dir(value.Value.(string))
var regexDir *regexp.Regexp
if entry, found := regexCache.Get(valueDir); found {
regexDir = entry.(*regexp.Regexp)
} else {
var err error
regexDir, err = eval.PatternToRegexp(valueDir)
if err != nil {
return false, err
regexValue := value.Regexp
valueFnc = func(dirname string) (bool, bool, error) {
if regexValue.MatchString(dirname) || regexDir.MatchString(dirname) {
return false, false, nil
}
regexCache.Add(valueDir, regexDir)
}

if regexDir.MatchString(dirname) {
return false, nil
return true, false, nil
}
} else if value.Type == eval.ScalarValueType {
if strings.HasPrefix(value.Value.(string), dirname) {
return false, nil
str := value.Value.(string)
valueFnc = func(dirname string) (bool, bool, error) {
return !strings.HasPrefix(str, dirname), false, nil
}
} else {
return false, nil
valueFnc = func(dirname string) (bool, bool, error) {
return false, false, nil
}
}

valueFncs = append(valueFncs, valueFnc)
}
}

// check basename
if values := rule.GetFieldValues(basenameField); len(values) > 0 {
if err := event.SetFieldValue(basenameField, path.Base(dirname)); err != nil {
return false, err
}
valueFnc = func(dirname string) (bool, bool, error) {
if err := discarderEvent.SetFieldValue(basenameField, path.Base(dirname)); err != nil {
return false, false, err
}

if isDiscarder, _ := rs.IsDiscarder(event, basenameField); !isDiscarder {
return false, nil
if isDiscarder, _ := rs.IsDiscarder(discarderEvent, basenameField); !isDiscarder {
return false, true, nil
}

return true, true, nil
}
valueFncs = append(valueFncs, valueFnc)
}
}

if err := event.SetFieldValue(filenameField, dirname); err != nil {
return false, err
}
parentDiscarderFnc := func(dirname string) (bool, error) {
var result, altered bool
var err error

if isDiscarder, _ := rs.IsDiscarder(event, filenameField); !isDiscarder {
return false, nil
defer func() {
if altered {
*discarderEvent = eventZero
}
}()

for _, fnc := range valueFncs {
result, altered, err = fnc(dirname)
if !result {
return false, err
}
}

if err := discarderEvent.SetFieldValue(filenameField, dirname); err != nil {
return false, err
}

if isDiscarder, _ := rs.IsDiscarder(discarderEvent, filenameField); !isDiscarder {
return false, nil
}

return true, nil
}
id.parentDiscarderFnc[filenameField] = parentDiscarderFnc

seclog.Tracef("`%s` discovered as parent discarder", dirname)

return true, nil
return parentDiscarderFnc(dirname)
}

func (id *inodeDiscarders) discardParentInode(rs *rules.RuleSet, eventType model.EventType, field eval.Field, filename string, mountID uint32, inode uint64, pathID uint32) (bool, uint32, uint64, error) {
isDiscarder, err := isParentPathDiscarder(rs, id.regexCache, eventType, field, filename)
isDiscarder, err := id.isParentPathDiscarder(rs, eventType, field, filename)
if !isDiscarder {
return false, 0, 0, err
}
Expand Down
Loading

0 comments on commit 99089bc

Please sign in to comment.