From 3ae527feee89f9c887fd9952b11470ea46fbbb6d Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Thu, 30 May 2024 19:37:22 +0100 Subject: [PATCH] btf: take first entry on multiple function matches TypeByName() can fail with ErrMultipleMatches if we have multiple candidates. If so, let's try again and take first match as it is. This can help solve our immediate issue of having multiple definitions per system calls, however the long-term fix would be to iterate over all candidate, match their proto and arguments definitions, then attach to the corresponding ones. Example output: time="2024-05-31T15:53:17+01:00" level=info msg="BTF includes '2' matched candidates on call \"__x64_sys_init_module\", using first one" metadata=/home/tixxdz/btf Signed-off-by: Djalal Harouni --- pkg/btf/validation.go | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/pkg/btf/validation.go b/pkg/btf/validation.go index f17ed7346a2..f867d268e07 100644 --- a/pkg/btf/validation.go +++ b/pkg/btf/validation.go @@ -4,13 +4,16 @@ package btf import ( + "errors" "fmt" "os" + "reflect" "strings" "github.com/cilium/ebpf/btf" "github.com/cilium/tetragon/pkg/arch" "github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1" + "github.com/cilium/tetragon/pkg/logger" "github.com/cilium/tetragon/pkg/syscallinfo" ) @@ -43,20 +46,40 @@ func (e *ValidationFailed) Error() string { func ValidateKprobeSpec(bspec *btf.Spec, call string, kspec *v1alpha1.KProbeSpec) error { var fn *btf.Func + origCall := call err := bspec.TypeByName(call, &fn) - if err != nil { - if !kspec.Syscall { - return &ValidationFailed{s: fmt.Sprintf("call %q %v", call, err)} - } - origCall := call + if err != nil && kspec.Syscall { + // Try with system call prefix call, err = arch.AddSyscallPrefix(call) if err == nil { err = bspec.TypeByName(call, &fn) } - if err != nil { + } + + // BTF include multiple candidates + if errors.Is(err, btf.ErrMultipleMatches) { + var allTypes, fnTypes []btf.Type + allTypes, err = bspec.AnyTypesByName(call) + if err == nil { + for _, typ := range allTypes { + // Assert again the appropriate type + if _, ok := typ.(*btf.Func); ok { + fnTypes = append(fnTypes, typ) + } + } + logger.GetLogger().Infof("BTF metadata includes '%d' matched candidates on call %q, using first one", len(fnTypes), call) + // take first one. + reflect.ValueOf(&fn).Elem().Set(reflect.ValueOf(fnTypes[0])) + } + } + + if err != nil { + if kspec.Syscall { return &ValidationFailed{ s: fmt.Sprintf("syscall %q (or %q) %v", origCall, call, err), } + } else { + return &ValidationFailed{s: fmt.Sprintf("call %q %v", call, err)} } }