From b8c4984ea3b002ef38d23bf702e1f9c50a935f1f Mon Sep 17 00:00:00 2001 From: Alon Zivony Date: Mon, 10 Jun 2024 14:54:57 +0300 Subject: [PATCH] feat(trace): attach and detach probes using the dependencies manager Introduced watchers to the dependencies manager that track probe additions and removals, and attach or detach them accordingly. If a probe attach fails, the watcher will cancel the operation. --- pkg/ebpf/tracee.go | 122 ++++++++++++++++++++++++++++----------------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/pkg/ebpf/tracee.go b/pkg/ebpf/tracee.go index 6a9d3514a7c5..8a50d636c402 100644 --- a/pkg/ebpf/tracee.go +++ b/pkg/ebpf/tracee.go @@ -1113,51 +1113,86 @@ func (t *Tracee) populateFilterMaps(newPolicies *policy.Policies, updateProcTree return nil } -// 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 { - depsNode, _ := t.eventsDependencies.GetEvent(id) - deps := depsNode.GetDependencies() - return deps.GetProbes() +// attachEvent attaches the probes of the given event to their respective eBPF programs. +// Calling attachment of probes if a supported behavior, and will do nothing +// if it has been attached already. +// Return whether the event was successfully attached or not. +func (t *Tracee) attachEvent(id events.ID) error { + evtName := events.Core.GetDefinitionByID(id).GetName() + depsNode, err := t.eventsDependencies.GetEvent(id) + if err != nil { + logger.Errorw("Missing event in dependencies", "event", evtName) + return err } - - // Get the list of probes to attach for each event being traced. - probesToEvents := make(map[events.Probe][]events.ID) - for id := range t.eventsState { - if !events.Core.IsDefined(id) { + for _, probe := range depsNode.GetDependencies().GetProbes() { + err := t.probes.Attach(probe.GetHandle(), t.cgroups) + if err == nil { continue } - for _, probeDep := range getProbeDeps(id) { - probesToEvents[probeDep] = append(probesToEvents[probeDep], id) + if probe.IsRequired() { + logger.Warnw( + "Cancelling event and its dependencies because of a missing probe", + "missing probe", probe.GetHandle(), "event", evtName, + "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( + dependencies.ProbeNodeType, + func(node interface{}) []dependencies.Action { + probeNode, ok := node.(*dependencies.ProbeNode) + if !ok { + logger.Errorw("Got node from type not requested") + return nil + } + err := t.probes.Attach(probeNode.GetHandle(), t.cgroups) + if err != nil { + return []dependencies.Action{dependencies.NewCancelNodeAddAction(err)} + } + return nil + }) + + t.eventsDependencies.SubscribeRemove( + dependencies.ProbeNodeType, + func(node interface{}) []dependencies.Action { + probeNode, ok := node.(*dependencies.ProbeNode) + if !ok { + logger.Errorw("Got node from type not requested") + return nil + } + err := t.probes.Detach(probeNode.GetHandle()) + if err != nil { + logger.Debugw("Failed to detach probe", + "probe", probeNode.GetHandle(), + "error", err) + } + return nil + }) // Attach probes to their respective eBPF programs or cancel events if a required probe is missing. - for probe, evtID := range probesToEvents { - err = t.probes.Attach(probe.GetHandle(), t.cgroups) // attach bpf program to probe + for eventID := range t.eventsState { + err := t.attachEvent(eventID) if err != nil { - for _, evtID := range evtID { - evtName := events.Core.GetDefinitionByID(evtID).GetName() - if probe.IsRequired() { - t.eventsDependencies.RemoveEvent(evtID) - logger.Warnw( - "Cancelling event and its dependencies because of missing probe", - "missing probe", probe.GetHandle(), "event", evtName, - "error", err, - ) - } else { - logger.Debugw( - "Failed to attach non-required probe for event", - "event", evtName, - "probe", probe.GetHandle(), "error", err, - ) - } + err := t.eventsDependencies.RemoveEvent(eventID) + if err != nil { + logger.Warnw("Failed to remove event from dependencies manager", "remove reason", "failed probes attachment", "error", err) } } } + // TODO: Update the eBPF maps according to events state after failure of attachments return nil } @@ -1212,17 +1247,6 @@ func (t *Tracee) initBPF() error { return errfmt.WrapError(err) } - // Attach eBPF programs to selected event's probes - - err = t.attachProbes() - if err != nil { - return errfmt.WrapError(err) - } - - // Update all ProcessTreeFilters after probes are attached: reduce the - // possible race window between the bpf programs updating the maps and - // userland reading procfs and also dealing with same maps. - // returned PoliciesConfig is not used here, therefore it's discarded _, err = t.config.Policies.UpdateBPF(t.bpfModule, t.containers, t.eventsState, t.eventsParamTypes, false, true) if err != nil { @@ -1286,6 +1310,14 @@ func (t *Tracee) initBPF() error { return errfmt.Errorf("error initializing logs perf map: %v", err) } + // Attach eBPF programs to selected event's probes + + // TODO: Solve race window between ProcessTreeFilters initialization through procfs and the + // attachment of the probes that update the filters from the eBPF side. Maybe perform another + // iteration on procfs to fill in any missing data. + + err = t.attachProbes() + return errfmt.WrapError(err) }