Skip to content

Commit

Permalink
events: add dependencies tree manager
Browse files Browse the repository at this point in the history
Create a manager object for the dependencies between event.
It helps to follow what events are really used, and will enable to effectively cancel and add events and probes.
  • Loading branch information
AlonZivony committed Mar 26, 2024
1 parent e81c9f0 commit f41827f
Show file tree
Hide file tree
Showing 5 changed files with 436 additions and 79 deletions.
3 changes: 2 additions & 1 deletion pkg/ebpf/ksymbols.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ func (t *Tracee) UpdateKallsyms() error {

// Wrap long method names.
evtDefSymDeps := func(id events.ID) []events.KSymbol {
return events.Core.GetDefinitionByID(id).GetDependencies().GetKSymbols()
deps, _ := t.eventsDependencies.GetEvent(id)
return deps.GetKSymbols()
}

// Get the symbols all events being traced require (t.eventsState already
Expand Down
150 changes: 72 additions & 78 deletions pkg/ebpf/tracee.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/aquasecurity/tracee/pkg/ebpf/probes"
"github.com/aquasecurity/tracee/pkg/errfmt"
"github.com/aquasecurity/tracee/pkg/events"
"github.com/aquasecurity/tracee/pkg/events/dependencies"
"github.com/aquasecurity/tracee/pkg/events/derive"
"github.com/aquasecurity/tracee/pkg/events/sorting"
"github.com/aquasecurity/tracee/pkg/events/trigger"
Expand Down Expand Up @@ -118,6 +119,8 @@ type Tracee struct {
streamsManager *streams.StreamsManager
// policyManager manages policy state
policyManager *policyManager
// The dependencies of events used by Tracee
eventsDependencies *dependencies.Manager
}

func (t *Tracee) Stats() *metrics.Stats {
Expand Down Expand Up @@ -161,25 +164,38 @@ func GetCaptureEventsList(cfg config.Config) map[events.ID]events.EventState {
return captureEvents
}

// handleEventsDependencies handles all events dependencies recursively.
func (t *Tracee) handleEventsDependencies(givenEvtId events.ID, givenEvtState events.EventState) {
givenEventDefinition := events.Core.GetDefinitionByID(givenEvtId)
for _, depEventId := range givenEventDefinition.GetDependencies().GetIDs() {
depEventState, ok := t.eventsState[depEventId]
if !ok {
depEventState = events.EventState{}
t.handleEventsDependencies(depEventId, givenEvtState)
}
func (t *Tracee) addEventState(eventID events.ID, chosenState events.EventState) {
currentState := t.eventsState[eventID]
currentState.Submit |= chosenState.Submit
currentState.Emit |= chosenState.Emit
t.eventsState[eventID] = currentState
}

// Make sure dependencies are submitted if the given event is submitted.
depEventState.Submit |= givenEvtState.Submit
t.eventsState[depEventId] = depEventState
func (t *Tracee) chooseEvent(eventID events.ID, chosenState events.EventState) {
t.addEventState(eventID, chosenState)
t.eventsDependencies.AddEvent(eventID)
}

// If the given event is a signature, mark all dependencies as signatures.
if events.Core.GetDefinitionByID(givenEvtId).IsSignature() {
t.eventSignatures[depEventId] = true
}
// addDependencyEventToState adds to tracee's state an event that is a dependency of other event
// The difference from chosen events is that it should not be emitted
func (t *Tracee) addDependencyEventToState(evtID events.ID) {
dependantEvts, ok := t.eventsDependencies.GetDependantEvents(evtID)
if !ok {
logger.Errorw("adding dependency event to state", "error", "event is missing upon add watcher")
}
newState := events.EventState{}
for _, dependantEvent := range dependantEvts {
newState.Submit |= t.eventsState[dependantEvent].Submit
}
t.addEventState(evtID, newState)
if events.Core.GetDefinitionByID(evtID).IsSignature() {
t.eventSignatures[evtID] = true
}
}

func (t *Tracee) removeEventFromState(evtID events.ID) {
delete(t.eventsState, evtID)
delete(t.eventSignatures, evtID)
}

// New creates a new Tracee instance based on a given valid Config. It is expected that it won't
Expand All @@ -204,8 +220,15 @@ func New(cfg config.Config) (*Tracee, error) {
eventSignatures: make(map[events.ID]bool),
streamsManager: streams.NewStreamsManager(),
policyManager: policyManager,
eventsDependencies: dependencies.NewDependenciesManager(
func(id events.ID) events.Dependencies {
return events.Core.GetDefinitionByID(id).GetDependencies()
}),
}

t.eventsDependencies.SubscribeIndirectAdd(t.addDependencyEventToState)
t.eventsDependencies.SubscribeIndirectRemove(t.removeEventFromState)

// Initialize capabilities rings soon

err = capabilities.Initialize(t.config.Capabilities.BypassCaps)
Expand All @@ -216,32 +239,32 @@ func New(cfg config.Config) (*Tracee, error) {

// Initialize events state with mandatory events (TODO: review this need for sched exec)

t.eventsState[events.SchedProcessFork] = events.EventState{}
t.eventsState[events.SchedProcessExec] = events.EventState{}
t.eventsState[events.SchedProcessExit] = events.EventState{}
t.chooseEvent(events.SchedProcessFork, events.EventState{})
t.chooseEvent(events.SchedProcessExec, events.EventState{})
t.chooseEvent(events.SchedProcessExit, events.EventState{})

// Control Plane Events

t.eventsState[events.SignalCgroupMkdir] = policy.AlwaysSubmit
t.eventsState[events.SignalCgroupRmdir] = policy.AlwaysSubmit
t.chooseEvent(events.SignalCgroupMkdir, policy.AlwaysSubmit)
t.chooseEvent(events.SignalCgroupRmdir, policy.AlwaysSubmit)

// Control Plane Process Tree Events

pipeEvts := func() {
t.eventsState[events.SchedProcessFork] = policy.AlwaysSubmit
t.eventsState[events.SchedProcessExec] = policy.AlwaysSubmit
t.eventsState[events.SchedProcessExit] = policy.AlwaysSubmit
t.chooseEvent(events.SchedProcessFork, policy.AlwaysSubmit)
t.chooseEvent(events.SchedProcessExec, policy.AlwaysSubmit)
t.chooseEvent(events.SchedProcessExit, policy.AlwaysSubmit)
}
signalEvts := func() {
t.eventsState[events.SignalSchedProcessFork] = policy.AlwaysSubmit
t.eventsState[events.SignalSchedProcessExec] = policy.AlwaysSubmit
t.eventsState[events.SignalSchedProcessExit] = policy.AlwaysSubmit
t.chooseEvent(events.SignalSchedProcessFork, policy.AlwaysSubmit)
t.chooseEvent(events.SignalSchedProcessExec, policy.AlwaysSubmit)
t.chooseEvent(events.SignalSchedProcessExit, policy.AlwaysSubmit)
}

// DNS Cache events

if t.config.DNSCacheConfig.Enable {
t.eventsState[events.NetPacketDNS] = policy.AlwaysSubmit
t.chooseEvent(events.NetPacketDNS, policy.AlwaysSubmit)
}

switch t.config.ProcTree.Source {
Expand All @@ -257,7 +280,7 @@ func New(cfg config.Config) (*Tracee, error) {
// Pseudo events added by capture (if enabled by the user)

for eventID, eCfg := range GetCaptureEventsList(cfg) {
t.eventsState[eventID] = eCfg
t.chooseEvent(eventID, eCfg)
}

// Events chosen by the user
Expand All @@ -273,20 +296,12 @@ func New(cfg config.Config) (*Tracee, error) {
}
utils.SetBit(&submit, uint(p.ID))
utils.SetBit(&emit, uint(p.ID))
t.eventsState[e] = events.EventState{Submit: submit, Emit: emit}
t.chooseEvent(e, events.EventState{Submit: submit, Emit: emit})

policyManager.EnableRule(p.ID, e)
}
}

// Handle all essential events dependencies

// TODO: extract this to a function to be called from here and from
// policies changes.
for id, state := range t.eventsState {
t.handleEventsDependencies(id, state)
}

// Update capabilities rings with all events dependencies

// TODO: extract this to a function to be called from here and from
Expand All @@ -295,14 +310,17 @@ func New(cfg config.Config) (*Tracee, error) {
if !events.Core.IsDefined(id) {
return t, errfmt.Errorf("event %d is not defined", id)
}
evtCaps := events.Core.GetDefinitionByID(id).GetDependencies().GetCapabilities()
err = caps.BaseRingAdd(evtCaps.GetBase()...)
if err != nil {
return t, errfmt.WrapError(err)
}
err = caps.BaseRingAdd(evtCaps.GetEBPF()...)
if err != nil {
return t, errfmt.WrapError(err)
deps, ok := t.eventsDependencies.GetEvent(id)
if ok {
evtCaps := deps.GetCapabilities()
err = caps.BaseRingAdd(evtCaps.GetBase()...)
if err != nil {
return t, errfmt.WrapError(err)
}
err = caps.BaseRingAdd(evtCaps.GetEBPF()...)
if err != nil {
return t, errfmt.WrapError(err)
}
}
}

Expand Down Expand Up @@ -531,24 +549,6 @@ func (t *Tracee) Init(ctx gocontext.Context) error {
return nil
}

type InitValues struct {
Kallsyms bool
}

func (t *Tracee) generateInitValues() (InitValues, error) {
initVals := InitValues{}
for evt := range t.eventsState {
if !events.Core.IsDefined(evt) {
return initVals, errfmt.Errorf("event %d is undefined", evt)
}
for range events.Core.GetDefinitionByID(evt).GetDependencies().GetKSymbols() {
initVals.Kallsyms = true // only if length > 0
}
}

return initVals, nil
}

// initTailCall initializes a given tailcall.
func (t *Tracee) initTailCall(tailCall events.TailCall) error {
tailCallMapName := tailCall.GetMapName()
Expand Down Expand Up @@ -837,7 +837,8 @@ func (t *Tracee) getUnavKsymsPerEvtID() map[events.ID][]string {
unavSymsPerEvtID := map[events.ID][]string{}

evtDefSymDeps := func(id events.ID) []events.KSymbol {
return events.Core.GetDefinitionByID(id).GetDependencies().GetKSymbols()
deps, _ := t.eventsDependencies.GetEvent(id)
return deps.GetKSymbols()
}

for evtID := range t.eventsState {
Expand Down Expand Up @@ -878,7 +879,8 @@ func (t *Tracee) validateKallsymsDependencies() {

// Find all events that depend on eventToCancel
for eventID := range t.eventsState {
depsIDs := events.Core.GetDefinitionByID(eventID).GetDependencies().GetIDs()
deps, _ := t.eventsDependencies.GetEvent(eventID)
depsIDs := deps.GetIDs()
for _, depID := range depsIDs {
if depID == eventToCancel {
depsToCancel[eventID] = eventNameToCancel
Expand Down Expand Up @@ -1053,22 +1055,14 @@ func (t *Tracee) populateFilterMaps(newPolicies *policy.Policies, updateProcTree
return nil
}

// cancelEventFromEventState cancels an event and all its dependencies from the eventsState map.
func (t *Tracee) cancelEventFromEventState(evtID events.ID) {
delete(t.eventsState, evtID)
evtDef := events.Core.GetDefinitionByID(evtID)
for _, evtDeps := range evtDef.GetDependencies().GetIDs() {
t.cancelEventFromEventState(evtDeps)
}
}

// attachProbes attaches selected events probes to their respective eBPF progs
func (t *Tracee) attachProbes() error {
var err error

// Get probe dependencies for a given event ID
getProbeDeps := func(id events.ID) []events.Probe {
return events.Core.GetDefinitionByID(id).GetDependencies().GetProbes()
deps, _ := t.eventsDependencies.GetEvent(id)
return deps.GetProbes()
}

// Get the list of probes to attach for each event being traced.
Expand All @@ -1089,12 +1083,13 @@ func (t *Tracee) attachProbes() error {
for _, evtID := range evtID {
evtName := events.Core.GetDefinitionByID(evtID).GetName()
if probe.IsRequired() {
t.eventsDependencies.RemoveEvent(evtID)
t.removeEventFromState(evtID)
logger.Warnw(
"Cancelling event and its dependencies because of missing probe",
"missing probe", probe.GetHandle(), "event", evtName,
"error", err,
)
t.cancelEventFromEventState(evtID) // cancel event recursively
} else {
logger.Debugw(
"Failed to attach non-required probe for event",
Expand All @@ -1105,7 +1100,6 @@ func (t *Tracee) attachProbes() error {
}
}
}

return nil
}

Expand Down
Loading

0 comments on commit f41827f

Please sign in to comment.