Skip to content

Commit

Permalink
btf: take first entry on multiple function matches
Browse files Browse the repository at this point in the history
[ OSS upstream 0636406 ]

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"

Signed-off-by: Djalal Harouni <tixxdz@gmail.com>
  • Loading branch information
tixxdz committed Jun 4, 2024
1 parent 6afff87 commit 1ece66b
Showing 1 changed file with 32 additions and 7 deletions.
39 changes: 32 additions & 7 deletions pkg/btf/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -43,21 +46,43 @@ 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 not found", call)}
}
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)
}
}
// TypeByName() above ensures btf.Func type, but Check again so semantically we are correct
if len(fnTypes) > 0 {
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) not found.", origCall, call),
s: fmt.Sprintf("syscall %q (or %q) %v", origCall, call, err),
}
}
return &ValidationFailed{s: fmt.Sprintf("call %q %v", call, err)}
}

proto, ok := fn.Type.(*btf.FuncProto)
Expand Down

0 comments on commit 1ece66b

Please sign in to comment.