Skip to content

Commit

Permalink
Use generic watchers for all dependencies nodes.
Browse files Browse the repository at this point in the history
Change the watchers to support any type of node to trigger watchers, according to required type.
Also add the ability for add watcher to cancel the add of the node to get less buggy order of operations.
  • Loading branch information
AlonZivony committed May 22, 2024
1 parent 910d4d1 commit 0f62fe0
Show file tree
Hide file tree
Showing 4 changed files with 332 additions and 148 deletions.
128 changes: 83 additions & 45 deletions pkg/ebpf/tracee.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,19 +175,19 @@ func (t *Tracee) addDependenciesToStateRecursive(eventNode *dependencies.EventNo
eventID := eventNode.GetID()
for _, dependencyEventID := range eventNode.GetDependencies().GetIDs() {
t.addDependencyEventToState(dependencyEventID, []events.ID{eventID})
dependencyNode, ok := t.eventsDependencies.GetEvent(dependencyEventID)
if ok {
dependencyNode, err := t.eventsDependencies.GetEvent(dependencyEventID)
if err == nil {
t.addDependenciesToStateRecursive(dependencyNode)
}
}
}

func (t *Tracee) chooseEvent(eventID events.ID, chosenState events.EventState) {
t.addEventState(eventID, chosenState)
t.eventsDependencies.SelectEvent(eventID)
eventNode, ok := t.eventsDependencies.GetEvent(eventID)
if !ok {
logger.Errorw("Event is missing from dependency right after being selected")
eventNode, err := t.eventsDependencies.SelectEvent(eventID)
if err != nil {
logger.Errorw("Event choosing failed",
"event", events.Core.GetDefinitionByID(eventID).GetName())
return
}
t.addDependenciesToStateRecursive(eventNode)
Expand Down Expand Up @@ -240,13 +240,28 @@ func New(cfg config.Config) (*Tracee, error) {
}),
}

// TODO: As events add and remove becomes a dynamic thing, we should subscribe all the watchers
// before choosing them. There is no reason to choose the event in the New function anyhow.
t.eventsDependencies.SubscribeAdd(
func(node *dependencies.EventNode) {
t.addDependencyEventToState(node.GetID(), node.GetDependants())
dependencies.EventNodeType,
func(node interface{}) dependencies.Action {
eventNode, ok := node.(*dependencies.EventNode)
if !ok {
logger.Errorw("Got node from type not requested")
return dependencies.NoAction
}
t.addDependencyEventToState(eventNode.GetID(), eventNode.GetDependents())
return dependencies.NoAction
})
t.eventsDependencies.SubscribeRemove(
func(node *dependencies.EventNode) {
t.removeEventFromState(node.GetID())
dependencies.EventNodeType,
func(node interface{}) {
eventNode, ok := node.(*dependencies.EventNode)
if !ok {
logger.Errorw("Got node from type not requested")
return
}
t.removeEventFromState(eventNode.GetID())
})

// Initialize capabilities rings soon
Expand Down Expand Up @@ -331,8 +346,8 @@ func New(cfg config.Config) (*Tracee, error) {
if !events.Core.IsDefined(id) {
return t, errfmt.Errorf("event %d is not defined", id)
}
depsNode, ok := t.eventsDependencies.GetEvent(id)
if ok {
depsNode, err := t.eventsDependencies.GetEvent(id)
if err == nil {
deps := depsNode.GetDependencies()
evtCaps := deps.GetCapabilities()
err = caps.BaseRingAdd(evtCaps.GetBase()...)
Expand Down Expand Up @@ -893,7 +908,7 @@ func (t *Tracee) validateKallsymsDependencies() {
return deps.GetKSymbols()
}

validateEvent := func(eventId events.ID) {
validateEvent := func(eventId events.ID) bool {
missingDepSyms := getUnavailbaleKsymbols(evtDefSymDeps(eventId), t.kernelSymbols)
shouldFailEvent := false
for _, symDep := range missingDepSyms {
Expand All @@ -912,18 +927,30 @@ func (t *Tracee) validateKallsymsDependencies() {
"Event canceled because of missing kernel symbol dependency",
"missing symbols", missingSymsNames, "event", eventNameToCancel,
)
// Cancel the event, it dependencies and its dependent events
t.eventsDependencies.RemoveEvent(eventId)
return false
}
return true
}

t.eventsDependencies.SubscribeAdd(
func(node *dependencies.EventNode) {
validateEvent(node.GetID())
dependencies.EventNodeType,
func(node interface{}) dependencies.Action {
eventNode, ok := node.(*dependencies.EventNode)
if !ok {
logger.Errorw("Got node from type not requested")
return dependencies.NoAction
}
if !validateEvent(eventNode.GetID()) {
return dependencies.CancelNodeAdd
}
return dependencies.NoAction
})

for eventId := range t.eventsState {
validateEvent(eventId)
if !validateEvent(eventId) {
// Cancel the event, it dependencies and its dependent events
t.eventsDependencies.RemoveEvent(eventId)
}
}
}

Expand Down Expand Up @@ -1085,13 +1112,13 @@ func (t *Tracee) populateFilterMaps(newPolicies *policy.Policies, updateProcTree
// attachEvent attaches the probes of the given event to their respective eBPF programs.
// Calling attachment of a probes is a supported behavior, and will do nothing
// if it has been attached already.
// In the case of failure in attaching, cancel the event using the dependencies API.
func (t *Tracee) attachEvent(id events.ID) {
// Return whether the event was successfully attached or not.
func (t *Tracee) attachEvent(id events.ID) error {
evtName := events.Core.GetDefinitionByID(id).GetName()
depsNode, ok := t.eventsDependencies.GetEvent(id)
if !ok {
depsNode, err := t.eventsDependencies.GetEvent(id)
if err != nil {
logger.Errorw("Missing event in dependencies", "event", evtName)
return
return err
}
for _, probe := range depsNode.GetDependencies().GetProbes() {
err := t.probes.Attach(probe.GetHandle(), t.cgroups)
Expand All @@ -1104,47 +1131,58 @@ func (t *Tracee) attachEvent(id events.ID) {
"missing probe", probe.GetHandle(), "event", evtName,
"error", err,
)
t.eventsDependencies.RemoveEvent(id)
} else {
logger.Debugw(
"Failed to attach non-required probe for event",
"event", evtName,
"probe", probe.GetHandle(), "error", err,
)
return err
}
logger.Debugw(
"Failed to attach non-required probe for event",
"event", evtName,
"probe", probe.GetHandle(), "error", err,
)
}
return nil
}

// attachProbes attaches selected events probes to their respective eBPF programs.
func (t *Tracee) attachProbes() error {
// Subscribe to all watchers on the dependencies to attach and/or detach
// probes upon changes
t.eventsDependencies.SubscribeAdd(
func(node *dependencies.EventNode) {
t.attachEvent(node.GetID())
dependencies.ProbeNodeType,
func(node interface{}) dependencies.Action {
probeNode, ok := node.(*dependencies.ProbeNode)
if !ok {
logger.Errorw("Got node from type not requested")
return dependencies.NoAction
}
err := t.probes.Attach(probeNode.GetHandle(), t.cgroups)
if err != nil {
return dependencies.CancelNodeAdd
}
return dependencies.NoAction
})

detachUnusedProbes := func(probesToCheck []events.Probe) {
for _, probe := range probesToCheck {
_, ok := t.eventsDependencies.GetHandle(probe.GetHandle())
if ok {
continue
t.eventsDependencies.SubscribeRemove(
dependencies.ProbeNodeType,
func(node interface{}) {
probeNode, ok := node.(*dependencies.ProbeNode)
if !ok {
logger.Errorw("Got node from type not requested")
return
}
err := t.probes.Detach(probe.GetHandle())
err := t.probes.Detach(probeNode.GetHandle())
if err != nil {
logger.Debugw("Failed to detach probe",
"probe", probe.GetHandle(),
"probe", probeNode.GetHandle(),
"error", err)
}
}
}
t.eventsDependencies.SubscribeRemove(func(node *dependencies.EventNode) {
detachUnusedProbes(node.GetDependencies().GetProbes())
})
})

// Attach probes to their respective eBPF programs or cancel events if a required probe is missing.
for eventID := range t.eventsState {
t.attachEvent(eventID)
err := t.attachEvent(eventID)
if err != nil {
t.eventsDependencies.RemoveEvent(eventID)
}
}
// TODO: Update the eBPF maps according to events state after failure of attachments
return nil
Expand Down
Loading

0 comments on commit 0f62fe0

Please sign in to comment.