Skip to content

Commit

Permalink
[btf] Cleanup AddModulesToSpec
Browse files Browse the repository at this point in the history
Now (as of 0.14.0) cilium/ebpf handles kmod BTFs natively
(https://pkg.go.dev/github.com/cilium/ebpf#ProgramOptions.KernelModuleTypes).

Thus we can remove some duplicate code (i.e. mainly function
AddModulesToSpec). This patch does this cleanup.

In Tetragon, we used kmod BTF in 2 places:
1. In order to attach to a function which is now handled automatically by
   cilium/ebpf.
2. In order to validate if the function name exists in the BTF and to check
   the argument types. In that case, we need to manually load the spec
   from the kmod that we care about. This is because cilium/ebpf checks
   (and loads the appropriate BTF) during the attach phase (i.e.
   https://github.com/cilium/ebpf/blob/5976561b28aabf23df00f3507cc3b240305b531b/prog.go#L157-L177)..

Signed-off-by: Anastasios Papagiannis <tasos.papagiannnis@gmail.com>
  • Loading branch information
tpapagian committed Jul 9, 2024
1 parent c7799fe commit 6194235
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 81 deletions.
3 changes: 0 additions & 3 deletions docs/data/tetragon_flags.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 0 additions & 54 deletions pkg/btf/btf.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
package btf

import (
"bytes"
"fmt"
"os"
"path"
"path/filepath"
"strings"

"github.com/cilium/ebpf/btf"
"github.com/cilium/tetragon/pkg/defaults"
Expand Down Expand Up @@ -90,57 +87,6 @@ func NewBTF() (*btf.Spec, error) {
return btf.LoadSpec(btfFile)
}

func AddModulesToSpec(spec *btf.Spec, kmods []string) (*btf.Spec, error) {
allTypes := []btf.Type{}
modulePaths := []string{}

iter := spec.Iterate()
for iter.Next() {
allTypes = append(allTypes, iter.Type)
}

for _, module := range kmods {
path := filepath.Join("/sys/kernel/btf", module)
f, err := os.Open(path)
if err != nil {
logger.GetLogger().WithField("path", path).Warn("btf: Path does not exist")
continue
}
defer f.Close()

modulePaths = append(modulePaths, path)

modSpec, err := btf.LoadSplitSpecFromReader(f, spec)
if err != nil {
return nil, fmt.Errorf("failed to load %s btf: %w", module, err)
}

iter := modSpec.Iterate()
for iter.Next() {
allTypes = append(allTypes, iter.Type)
}
}

logger.GetLogger().WithField("modules", strings.Join(modulePaths, " ")).Info("btf: Loaded symbols from modules")

b, err := btf.NewBuilder(allTypes)
if err != nil {
return nil, fmt.Errorf("failed to call btf.NewBuilder: %w", err)
}

raw, err := b.Marshal(nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to call b.Marshal: %w", err)
}

spec, err = btf.LoadSpecFromReader(bytes.NewReader(raw))
if err != nil {
return nil, fmt.Errorf("failed to call btf.LoadSpecFromReader: %w", err)
}

return spec, nil
}

func InitCachedBTF(lib, btf string) error {
var err error

Expand Down
19 changes: 18 additions & 1 deletion pkg/btf/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"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/ksyms"
"github.com/cilium/tetragon/pkg/logger"
"github.com/cilium/tetragon/pkg/syscallinfo"
)
Expand Down Expand Up @@ -46,8 +47,24 @@ func (e *ValidationFailed) Error() string {
func ValidateKprobeSpec(bspec *btf.Spec, call string, kspec *v1alpha1.KProbeSpec) error {
var fn *btf.Func

// get kernel symbols
ks, err := ksyms.KernelSymbols()
if err != nil {
return fmt.Errorf("validateKprobeSpec: ksyms.KernelSymbols: %w", err)
}

// check if this functio name is part of a kernel module
if kmod, err := ks.GetKmod(call); err == nil {
// get the spec from the kernel module and continue the validation with that
kmodSpec, err := btf.LoadKernelModuleSpec(kmod)
if err != nil {
return fmt.Errorf("validateKprobeSpec: btf.LoadKernelModuleSpec: %w", err)
}
bspec = kmodSpec
}

origCall := call
err := bspec.TypeByName(call, &fn)
err = bspec.TypeByName(call, &fn)
if err != nil && kspec.Syscall {
// Try with system call prefix
call, err = arch.AddSyscallPrefix(call)
Expand Down
17 changes: 17 additions & 0 deletions pkg/ksyms/ksyms.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type ksym struct {
addr uint64
name string
ty string
kmod string
}

// Ksyms is a structure for kernel symbols
Expand Down Expand Up @@ -103,6 +104,11 @@ func NewKsyms(procfs string) (*Ksyms, error) {
break
}

// check if this symbol is part of a kmod
if sym.isFunction() && len(fields) >= 4 {
sym.kmod = string(strings.Trim(fields[3], "[]"))
}

if !needsSort && len(ksyms.table) > 0 {
lastSym := ksyms.table[len(ksyms.table)-1]
if lastSym.addr > sym.addr {
Expand Down Expand Up @@ -202,3 +208,14 @@ func (k *Ksyms) IsAvailable(name string) bool {
}
return false
}

func (k *Ksyms) GetKmod(name string) (string, error) {
// This linear search is slow. But this only happens during the validation
// of kprobe-based tracing polies. TODO: optimise if needed
for _, s := range k.table {
if s.name == name && s.kmod != "" {
return s.kmod, nil
}
}
return "", fmt.Errorf("symbol %s not found in kallsyms or is not part of a module", name)
}
2 changes: 0 additions & 2 deletions pkg/option/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ type config struct {

EnableMsgHandlingLatency bool

KMods []string

EnablePodInfo bool
EnableTracingPolicyCRD bool

Expand Down
6 changes: 0 additions & 6 deletions pkg/option/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ const (

KeyEnableMsgHandlingLatency = "enable-msg-handling-latency"

KeyKmods = "kmods"

KeyEnablePodInfo = "enable-pod-info"
KeyEnableTracingPolicyCRD = "enable-tracing-policy-crd"

Expand Down Expand Up @@ -189,8 +187,6 @@ func ReadAndSetFlags() error {

Config.TracingPolicyDir = viper.GetString(KeyTracingPolicyDir)

Config.KMods = viper.GetStringSlice(KeyKmods)

Config.EnablePodInfo = viper.GetBool(KeyEnablePodInfo)
Config.EnableTracingPolicyCRD = viper.GetBool(KeyEnableTracingPolicyCRD)

Expand Down Expand Up @@ -353,8 +349,6 @@ func AddFlags(flags *pflag.FlagSet) {

flags.Bool(KeyEnableMsgHandlingLatency, false, "Enable metrics for message handling latency")

flags.StringSlice(KeyKmods, []string{}, "List of kernel modules to load symbols from")

flags.String(KeyRBQueueSize, "65535", "Set size of channel between ring buffer and sensor go routines (default 65k, allows K/M/G suffix)")

flags.Bool(KeyEnablePodInfo, false, "Enable PodInfo custom resource")
Expand Down
12 changes: 4 additions & 8 deletions pkg/sensors/program/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/cilium/ebpf/link"
cachedbtf "github.com/cilium/tetragon/pkg/btf"
"github.com/cilium/tetragon/pkg/logger"
"github.com/cilium/tetragon/pkg/option"
"github.com/cilium/tetragon/pkg/sensors/unloader"
"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -719,19 +718,13 @@ func doLoadProgram(
verbose int,
) (*LoadedCollection, error) {
var btfSpec *btf.Spec
if btfFilePath := cachedbtf.GetCachedBTFFile(); btfFilePath != "/sys/kernel/btf/vmlinux" || len(option.Config.KMods) > 0 {
if btfFilePath := cachedbtf.GetCachedBTFFile(); btfFilePath != "/sys/kernel/btf/vmlinux" {
// Non-standard path to BTF, open it and provide it as 'KernelTypes'.
var err error
btfSpec, err = btf.LoadSpec(btfFilePath)
if err != nil {
return nil, fmt.Errorf("opening BTF file '%s' failed: %w", btfFilePath, err)
}
if len(option.Config.KMods) > 0 {
btfSpec, err = cachedbtf.AddModulesToSpec(btfSpec, option.Config.KMods)
if err != nil {
return nil, fmt.Errorf("adding modules to spec failed: %w", err)
}
}
}

spec, err := ebpf.LoadCollectionSpec(load.Name)
Expand Down Expand Up @@ -821,6 +814,9 @@ func doLoadProgram(
// as that makes the loading very slow.
opts.Programs.LogLevel = 1
opts.Programs.LogSize = verifierLogBufferSize
if load.KernelTypes != nil {
opts.Programs.KernelTypes = load.KernelTypes
}
// If we hit ENOSPC that means that our log size is not big enough,
// so keep trying again with log size * 2 until we succeed or the kernel
// complains.
Expand Down
4 changes: 4 additions & 0 deletions pkg/sensors/program/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/btf"
"github.com/cilium/tetragon/pkg/sensors/unloader"
)

Expand Down Expand Up @@ -109,6 +110,9 @@ type Program struct {
LC *LoadedCollection

RewriteConstants map[string]interface{}

// Type information used for CO-RE relocations.
KernelTypes *btf.Spec
}

func (p *Program) SetRetProbe(ret bool) *Program {
Expand Down
7 changes: 0 additions & 7 deletions pkg/sensors/tracing/generickprobe.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,13 +427,6 @@ func preValidateKprobes(name string, kprobes []v1alpha1.KProbeSpec, lists []v1al
return err
}

if len(option.Config.KMods) > 0 {
btfobj, err = btf.AddModulesToSpec(btfobj, option.Config.KMods)
if err != nil {
return fmt.Errorf("adding modules to spec failed: %w", err)
}
}

// validate lists first
err = preValidateLists(lists)
if err != nil {
Expand Down

0 comments on commit 6194235

Please sign in to comment.