Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 104 additions & 20 deletions pkg/ebpf/tracee.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ func (t *Tracee) Init(ctx gocontext.Context) error {
return t.extensions.InitExtensionsForPhase(ctx, t, InitPhaseComplete)
}

// initTailCall initializes a given tailcall.
// initTailCall initializes a given tailcall by updating the BPF map with the program FD.
func (t *Tracee) initTailCall(tailCall events.TailCall) error {
tailCallMapName := tailCall.GetMapName()
tailCallProgName := tailCall.GetProgName()
Expand Down Expand Up @@ -626,6 +626,37 @@ func (t *Tracee) initTailCall(tailCall events.TailCall) error {
return nil
}

// uninitTailCall removes a given tailcall by deleting its entries from the BPF map.
func (t *Tracee) uninitTailCall(tailCall events.TailCall) error {
tailCallMapName := tailCall.GetMapName()
tailCallIndexes := tailCall.GetIndexes()

// Pick eBPF map by name.
bpfMap, err := t.bpfModule.GetMap(tailCallMapName)
if err != nil {
return errfmt.WrapError(err)
}

// Remove all indexes for this tailcall
for _, index := range tailCallIndexes {
// Workaround: Do not try to remove unsupported syscalls (arm64, e.g.)
if index >= uint32(events.Unsupported) {
continue
}
// Delete the entry from the BPF map
err := bpfMap.DeleteKey(unsafe.Pointer(&index))
if err != nil {
// Log but don't fail on individual delete errors
logger.Debugw("Failed to uninit tailcall index",
"map", tailCallMapName,
"index", index,
"error", err)
}
}

return nil
}

// initDerivationTable initializes tracee's events.DerivationTable. For each
// event, represented through its ID, we declare to which other events it can be
// derived and the corresponding function to derive into that Event.
Expand Down Expand Up @@ -1302,25 +1333,6 @@ func (t *Tracee) populateBPFMaps() error {
return errfmt.WrapError(err)
}

// Initialize tail call dependencies
// TODO: Tail calls are not updated upon events changes in the dependency manager.
// Hence, upon events addition, fallbacks or removal, tail calls will not be updated.
// This should be fixed dynamically in the future.
for _, eventID := range t.eventsDependencies.GetEvents() {
depsNode, err := t.eventsDependencies.GetEvent(eventID)
if err != nil {
return errfmt.Errorf("failed to get event dependencies: %v", err)
}
deps := depsNode.GetDependencies()
tailCalls := deps.GetTailCalls()
for _, tailCall := range tailCalls {
err := t.initTailCall(tailCall)
if err != nil {
return errfmt.Errorf("failed to initialize tail call: %v", err)
}
}
}

return nil
}

Expand Down Expand Up @@ -1402,6 +1414,72 @@ func (t *Tracee) attachProbes() error {
return nil
}

// attachTailCalls initializes selected tailcalls by updating their BPF maps.
func (t *Tracee) attachTailCalls() error {
// Subscribe to watchers on the dependencies to initialize and/or uninitialize
// tailcalls upon changes
t.eventsDependencies.SubscribeAdd(
dependencies.TailCallNodeType,
func(node interface{}) []dependencies.Action {
tailCallNode, ok := node.(*dependencies.TailCallNode)
if !ok {
logger.Errorw("Got node from type not requested", "type", fmt.Sprintf("%T", node))
return nil
}
tailCall := tailCallNode.GetTailCall()
err := t.initTailCall(tailCall)
if err != nil {
// Cancel adding this tailcall node if initialization fails
return []dependencies.Action{dependencies.NewCancelNodeAddAction(err)}
}
return nil
})

t.eventsDependencies.SubscribeRemove(
dependencies.TailCallNodeType,
func(node interface{}) []dependencies.Action {
tailCallNode, ok := node.(*dependencies.TailCallNode)
if !ok {
logger.Errorw("Got node from type not requested", "type", fmt.Sprintf("%T", node))
return nil
}
tailCall := tailCallNode.GetTailCall()
err := t.uninitTailCall(tailCall)
if err != nil {
logger.Debugw("Failed to uninit tailcall",
"map", tailCall.GetMapName(),
"program", tailCall.GetProgName(),
"error", err)
}
return nil
})

// Initialize all current tailcalls or fail them (and dependent events) if initialization fails.
for _, tailCallKey := range t.eventsDependencies.GetTailCalls() {
tailCallNode, err := t.eventsDependencies.GetTailCall(tailCallKey)
if err != nil {
logger.Debugw("Failed to get tailcall from dependencies manager", "key", tailCallKey, "error", err)
continue
}
tailCall := tailCallNode.GetTailCall()

// Initialize this specific tailcall
err = t.initTailCall(tailCall)
if err != nil {
// Mark this specific tailcall as failed, which will also fail all dependent events as needed.
failErr := t.eventsDependencies.FailTailCall(tailCall)
if failErr != nil {
logger.Warnw("Failed to fail tailcall in dependencies manager",
"tailcall", tailCallKey,
"init error", err,
"fail error", failErr)
}
}
}

return nil
}

// validateProbesCompatibility validates the compatibility of probes and their fallbacks.
// It will subscribe to the dependencies to validate the compatibility of new probes and their fallbacks as well.
func (t *Tracee) validateProbesCompatibility() error {
Expand Down Expand Up @@ -1579,6 +1657,12 @@ func (t *Tracee) initBPF() error {
// iteration on procfs to fill in any missing data.

err = t.attachProbes()
if err != nil {
return errfmt.WrapError(err)
}

// Initialize tailcalls for selected events
err = t.attachTailCalls()

return errfmt.WrapError(err)
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/events/definition_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,7 @@ func (tc TailCall) GetMapName() string {
func (tc TailCall) GetProgName() string {
return tc.progName
}

func (tc TailCall) IsRequired() bool {
return true // tailcalls are always required
}
2 changes: 1 addition & 1 deletion pkg/events/dependencies/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (en *EventNode) fallback() bool {
}

func (en *EventNode) GetDependents() []events.ID {
return slices.Clone[[]events.ID](en.dependents)
return slices.Clone(en.dependents)
}

func (en *EventNode) IsDependencyOf(dependent events.ID) bool {
Expand Down
Loading
Loading