From da0db0d3f2716a0f3216235d152121e816424fcb Mon Sep 17 00:00:00 2001 From: Rafael David Tinoco Date: Sun, 30 Jul 2023 05:19:07 +0000 Subject: [PATCH] fix(events): fix arm64 tailcalls indexes for unsupported syscalls Bring back the workaround not to index, in tailcall maps, the eBPF programs with event (syscall) indexes that are unsupported (other architectures should flag unsupported syscalls with IDs >= 10000). --- pkg/ebpf/tracee.go | 51 +++++++++++------ pkg/events/core.go | 1 + pkg/events/core_arm64.go | 4 +- pkg/events/definition_group.go | 102 ++++++++++++++++----------------- 4 files changed, 88 insertions(+), 70 deletions(-) diff --git a/pkg/ebpf/tracee.go b/pkg/ebpf/tracee.go index 690e4cbbff67..7165d45b0bf1 100644 --- a/pkg/ebpf/tracee.go +++ b/pkg/ebpf/tracee.go @@ -507,33 +507,50 @@ func (t *Tracee) generateInitValues() (InitValues, error) { // initTailCall initializes a given tailcall. func (t *Tracee) initTailCall(tailCall events.TailCall) error { - bpfMap, err := t.bpfModule.GetMap(tailCall.GetMapName()) + tailCallMapName := tailCall.GetMapName() + tailCallProgName := tailCall.GetProgName() + tailCallIndexes := tailCall.GetIndexes() + + // Pick eBPF map by name. + bpfMap, err := t.bpfModule.GetMap(tailCallMapName) if err != nil { return errfmt.WrapError(err) } - progName := tailCall.GetProgName() - bpfProg, err := t.bpfModule.GetProgram(progName) + // Pick eBPF program by name. + bpfProg, err := t.bpfModule.GetProgram(tailCallProgName) if err != nil { - return errfmt.Errorf("could not get BPF program %s: %v", progName, err) + return errfmt.Errorf("could not get BPF program %s: %v", tailCallProgName, err) } - fd := bpfProg.FileDescriptor() - if fd < 0 { - return errfmt.Errorf("could not get BPF program FD for %s: %v", progName, err) + // Pick eBPF program file descriptor. + bpfProgFD := bpfProg.FileDescriptor() + if bpfProgFD < 0 { + return errfmt.Errorf("could not get BPF program FD for %s: %v", tailCallProgName, err) } - // Attach internal syscall probes if needed. - for _, index := range tailCall.GetIndexes() { + once := &sync.Once{} + + // Pick all indexes (event, or syscall, IDs) the BPF program should be related to. + for _, index := range tailCallIndexes { + // Special treatment for indexes of syscall events. if events.Core.GetDefinitionByID(events.ID(index)).IsSyscall() { - err := t.probes.Attach(probes.SyscallEnter__Internal) - if err != nil { - return errfmt.WrapError(err) - } - err = t.probes.Attach(probes.SyscallExit__Internal) - if err != nil { - return errfmt.WrapError(err) + // Optimization: enable enter/exit probes only if at least one syscall is enabled. + once.Do(func() { + err := t.probes.Attach(probes.SyscallEnter__Internal) + if err != nil { + logger.Errorw("error attaching to syscall enter", "error", err) + } + err = t.probes.Attach(probes.SyscallExit__Internal) + if err != nil { + logger.Errorw("error attaching to syscall enter", "error", err) + } + }) + // Workaround: Do not map eBPF program to unsupported syscalls (arm64, e.g.) + if index >= uint32(events.Unsupported) { + continue } } - err := bpfMap.Update(unsafe.Pointer(&index), unsafe.Pointer(&fd)) + // Update given eBPF map with the eBPF program file descriptor at given index. + err := bpfMap.Update(unsafe.Pointer(&index), unsafe.Pointer(&bpfProgFD)) if err != nil { return errfmt.WrapError(err) } diff --git a/pkg/events/core.go b/pkg/events/core.go index 03bb2fb45be7..58d0ec3ba384 100644 --- a/pkg/events/core.go +++ b/pkg/events/core.go @@ -12,6 +12,7 @@ import ( const ( Sys32Undefined ID = 0xfffffff - 1 // u32 overflows are compiler implementation dependent. Undefined ID = 0xfffffff + Unsupported ID = 10000 ) type ID int32 diff --git a/pkg/events/core_arm64.go b/pkg/events/core_arm64.go index 97dadd0963c0..290d47c03bed 100644 --- a/pkg/events/core_arm64.go +++ b/pkg/events/core_arm64.go @@ -353,7 +353,7 @@ const ( // following syscalls are undefined on arm64 const ( - Open ID = iota + 10000 + Open ID = iota + Unsupported Stat Lstat Poll @@ -954,7 +954,7 @@ const ( // following syscalls are undefined on arm32 const ( - Sys32arch_prctl ID = iota + 10000 + Sys32arch_prctl ID = iota + Unsupported Sys32getpmsg Sys32putpmsg Sys32set_thread_area diff --git a/pkg/events/definition_group.go b/pkg/events/definition_group.go index 5a58715059bc..07bc126de381 100644 --- a/pkg/events/definition_group.go +++ b/pkg/events/definition_group.go @@ -43,12 +43,12 @@ func NewDefinitionGroup() *DefinitionGroup { // GetDefinitions returns a new map of existing definitions. // TODO: iterate internally after event definition refactor is finished ? -func (e *DefinitionGroup) GetDefinitions() []Definition { - e.mutex.RLock() - defer e.mutex.RUnlock() +func (d *DefinitionGroup) GetDefinitions() []Definition { + d.mutex.RLock() + defer d.mutex.RUnlock() - definitions := make([]Definition, 0, len(e.definitions)) - for _, def := range e.definitions { + definitions := make([]Definition, 0, len(d.definitions)) + for _, def := range d.definitions { definitions = append(definitions, def) } sort.Sort(ByID(definitions)) @@ -57,15 +57,15 @@ func (e *DefinitionGroup) GetDefinitions() []Definition { } // GetDefinitionIDByName returns a definition ID by its name. -func (e *DefinitionGroup) GetDefinitionIDByName(givenName string) (ID, bool) { - e.mutex.RLock() - defer e.mutex.RUnlock() - return e.getDefinitionIDByName(givenName) +func (d *DefinitionGroup) GetDefinitionIDByName(givenName string) (ID, bool) { + d.mutex.RLock() + defer d.mutex.RUnlock() + return d.getDefinitionIDByName(givenName) } // getDefinitionIDByName returns a definition ID by its name (no locking). -func (e *DefinitionGroup) getDefinitionIDByName(givenName string) (ID, bool) { - for id, def := range e.definitions { +func (d *DefinitionGroup) getDefinitionIDByName(givenName string) (ID, bool) { + for id, def := range d.definitions { if def.GetName() == givenName { return id, true } @@ -77,11 +77,11 @@ func (e *DefinitionGroup) getDefinitionIDByName(givenName string) (ID, bool) { // GetDefinitionByID returns a definition by its ID. // NOTE: should be used together with IsDefined when definition might not exist. -func (e *DefinitionGroup) GetDefinitionByID(givenDef ID) Definition { - e.mutex.RLock() - defer e.mutex.RUnlock() +func (d *DefinitionGroup) GetDefinitionByID(givenDef ID) Definition { + d.mutex.RLock() + defer d.mutex.RUnlock() - def, ok := e.definitions[givenDef] + def, ok := d.definitions[givenDef] if !ok { logger.Debugw("definition id not found", "id", givenDef) return Definition{id: Undefined} @@ -92,35 +92,35 @@ func (e *DefinitionGroup) GetDefinitionByID(givenDef ID) Definition { // IsDefined returns true if the definition exists in the definition group. // NOTE: needed as GetDefinitionByID() is used as GetDefinitionByID().Method() multiple times. -func (e *DefinitionGroup) IsDefined(givenDef ID) bool { - e.mutex.RLock() - defer e.mutex.RUnlock() +func (d *DefinitionGroup) IsDefined(givenDef ID) bool { + d.mutex.RLock() + defer d.mutex.RUnlock() - _, ok := e.definitions[givenDef] + _, ok := d.definitions[givenDef] return ok } // Length returns the number of definitions in the definition group. -func (e *DefinitionGroup) Length() int { - e.mutex.RLock() - defer e.mutex.RUnlock() - return len(e.definitions) +func (d *DefinitionGroup) Length() int { + d.mutex.RLock() + defer d.mutex.RUnlock() + return len(d.definitions) } // Add adds a definition to the definition group. -func (e *DefinitionGroup) Add(givenId ID, givenDef Definition) error { - e.mutex.Lock() - defer e.mutex.Unlock() - return e.add(givenId, givenDef) +func (d *DefinitionGroup) Add(givenId ID, givenDef Definition) error { + d.mutex.Lock() + defer d.mutex.Unlock() + return d.add(givenId, givenDef) } // AddBatch adds multiple definitions to the definition group. -func (e *DefinitionGroup) AddBatch(givenDefs map[ID]Definition) error { - e.mutex.Lock() - defer e.mutex.Unlock() +func (d *DefinitionGroup) AddBatch(givenDefs map[ID]Definition) error { + d.mutex.Lock() + defer d.mutex.Unlock() for id, def := range givenDefs { - err := e.add(id, def) + err := d.add(id, def) if err != nil { return err } @@ -130,29 +130,29 @@ func (e *DefinitionGroup) AddBatch(givenDefs map[ID]Definition) error { } // add adds a definition to the definition group (no locking). -func (e *DefinitionGroup) add(givenId ID, givenDef Definition) error { - if _, ok := e.definitions[givenId]; ok { +func (d *DefinitionGroup) add(givenId ID, givenDef Definition) error { + if _, ok := d.definitions[givenId]; ok { return definitionIDAlreadyExistsErr(givenId) } n := givenDef.GetName() - if _, ok := e.getDefinitionIDByName(n); ok { + if _, ok := d.getDefinitionIDByName(n); ok { return definitionNameAlreadyExistsErr(n) } - e.definitions[givenId] = givenDef + d.definitions[givenId] = givenDef return nil } // NamesToIDs returns a new map of definition names to their IDs. -func (e *DefinitionGroup) NamesToIDs() map[string]ID { - e.mutex.RLock() - defer e.mutex.RUnlock() +func (d *DefinitionGroup) NamesToIDs() map[string]ID { + d.mutex.RLock() + defer d.mutex.RUnlock() - namesToIds := make(map[string]ID, len(e.definitions)) + namesToIds := make(map[string]ID, len(d.definitions)) - for id, def := range e.definitions { + for id, def := range d.definitions { namesToIds[def.GetName()] = id } @@ -160,13 +160,13 @@ func (e *DefinitionGroup) NamesToIDs() map[string]ID { } // IDs32ToIDs returns a new map of 32-bit definition IDs to their IDs. -func (e *DefinitionGroup) IDs32ToIDs() map[ID]ID { - e.mutex.RLock() - defer e.mutex.RUnlock() +func (d *DefinitionGroup) IDs32ToIDs() map[ID]ID { + d.mutex.RLock() + defer d.mutex.RUnlock() - idS32ToIDs := make(map[ID]ID, len(e.definitions)) + idS32ToIDs := make(map[ID]ID, len(d.definitions)) - for id, def := range e.definitions { + for id, def := range d.definitions { id32Bit := def.GetID32Bit() if id32Bit != Sys32Undefined { @@ -178,15 +178,15 @@ func (e *DefinitionGroup) IDs32ToIDs() map[ID]ID { } // GetTailCalls returns a list of tailcalls of all definitions in the group (for initialization). -func (e *DefinitionGroup) GetTailCalls(state map[ID]EventState) []TailCall { - e.mutex.RLock() - defer e.mutex.RUnlock() +func (d *DefinitionGroup) GetTailCalls(state map[ID]EventState) []TailCall { + d.mutex.RLock() + defer d.mutex.RUnlock() var tailCalls []TailCall - for id, def := range e.definitions { - if state[id].Submit > 0 { // only traced events to provide their tailcalls - tailCalls = append(tailCalls, def.GetDependencies().GetTailCalls()...) + for evtDefID, evtDef := range d.definitions { + if state[evtDefID].Submit > 0 { // only traced events to provide their tailcalls + tailCalls = append(tailCalls, evtDef.GetDependencies().GetTailCalls()...) } }