diff --git a/cmd/tetra/debug/progs.go b/cmd/tetra/debug/progs.go index d56fc787503..97c2c6b2bcf 100644 --- a/cmd/tetra/debug/progs.go +++ b/cmd/tetra/debug/progs.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "io/fs" "log" "os" "path" @@ -24,6 +25,7 @@ import ( "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/link" + "github.com/cilium/ebpf/pin" "github.com/spf13/cobra" "golang.org/x/sys/unix" "golang.org/x/term" @@ -403,26 +405,20 @@ func getTetragonProgs(base string) ([]*prog, error) { var progs []*prog // Walk bpffs/tetragon and look for programs - err := filepath.Walk(base, - func(path string, finfo os.FileInfo, err error) error { + err := pin.WalkDir(base, + func(path string, finfo fs.DirEntry, obj pin.Pinner, err error) error { if err != nil { return err } if finfo.IsDir() { return nil } - if strings.HasSuffix(path, "/link") || strings.HasSuffix(path, "/link_override") { - return nil // skip BPF links, they make the syscall fail since cilium/ebpf@78074c59 - } - p, err := ebpf.LoadPinnedProgram(path, nil) - if err != nil { - return err - } - defer p.Close() - if !isProg(p.FD()) { + p, ok := obj.(*ebpf.Program) + if !ok { return nil } + defer p.Close() info, err := p.Info() if err != nil { @@ -440,7 +436,7 @@ func getTetragonProgs(base string) ([]*prog, error) { progs = append(progs, &prog{ id: uint32(id), name: getName(p, info), - pin: path, + pin: filepath.Join(base, path), cnt: runCnt, time: runTime, alive: true, @@ -450,18 +446,6 @@ func getTetragonProgs(base string) ([]*prog, error) { return progs, err } -func isProg(fd int) bool { - return isBPFObject("prog", fd) -} - -func isBPFObject(object string, fd int) bool { - readlink, err := os.Readlink(fmt.Sprintf("/proc/self/fd/%d", fd)) - if err != nil { - return false - } - return readlink == fmt.Sprintf("anon_inode:bpf-%s", object) -} - func getName(p *ebpf.Program, info *ebpf.ProgramInfo) string { handle, err := p.Handle() if err != nil { diff --git a/go.mod b/go.mod index dd26a5a5f3c..8a32306036c 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/alecthomas/kong v1.6.0 github.com/bombsimon/logrusr/v4 v4.1.0 github.com/cilium/cilium v1.17.0-pre.3 - github.com/cilium/ebpf v0.16.1-0.20241205185900-f0eec7efba9d + github.com/cilium/ebpf v0.17.1 github.com/cilium/little-vm-helper v0.0.19 github.com/cilium/lumberjack/v2 v2.4.1 github.com/cilium/tetragon/api v0.0.0-00010101000000-000000000000 diff --git a/go.sum b/go.sum index fc9ae927521..e91ccf27c54 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cilium/cilium v1.17.0-pre.3 h1:3wZkE1S6MPY7Sw0yqo8i2f2sENgIGebKjkQ//W+wNmo= github.com/cilium/cilium v1.17.0-pre.3/go.mod h1:rsmS3OsYDHg6bGnr9qVeB/qij+j7pi++G1WzoQqjicE= -github.com/cilium/ebpf v0.16.1-0.20241205185900-f0eec7efba9d h1:f1E6PeQ/j1Xd2ZNlffKOfscx+daNInIUAJObbiU+7aQ= -github.com/cilium/ebpf v0.16.1-0.20241205185900-f0eec7efba9d/go.mod h1:vay2FaYSmIlv3r8dNACd4mW/OCaZLJKJOo+IHBvCIO8= +github.com/cilium/ebpf v0.17.1 h1:G8mzU81R2JA1nE5/8SRubzqvBMmAmri2VL8BIZPWvV0= +github.com/cilium/ebpf v0.17.1/go.mod h1:vay2FaYSmIlv3r8dNACd4mW/OCaZLJKJOo+IHBvCIO8= github.com/cilium/hive v0.0.0-20241122120553-e90d0875ae01 h1:eNOjtUsqu2W/nTZlInLAj+zyIg/HIQ1K7norewaxsgM= github.com/cilium/hive v0.0.0-20241122120553-e90d0875ae01/go.mod h1:pI2GJ1n3SLKIQVFrKF7W6A6gb6BQkZ+3Hp4PAEo5SuI= github.com/cilium/little-vm-helper v0.0.19 h1:eJeJM/03MGLrLUXXTBDZo2JoX5cIbm5+9iWjoHgpy/M= diff --git a/pkg/bugtool/maps.go b/pkg/bugtool/maps.go index a836056ff23..f4ba0071519 100644 --- a/pkg/bugtool/maps.go +++ b/pkg/bugtool/maps.go @@ -10,13 +10,12 @@ import ( "iter" "maps" "os" - "path/filepath" "slices" "sort" - "strings" "syscall" "github.com/cilium/ebpf" + "github.com/cilium/ebpf/pin" "github.com/cilium/tetragon/pkg/bpf" ) @@ -90,27 +89,20 @@ func FindAllMaps() ([]bpf.ExtendedMapInfo, error) { // specified as argument. func FindPinnedMaps(path string) ([]bpf.ExtendedMapInfo, error) { var infos []bpf.ExtendedMapInfo - err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { + + err := pin.WalkDir(path, func(_ string, d fs.DirEntry, obj pin.Pinner, err error) error { if err != nil { return err } if d.IsDir() { return nil // skip directories } - m, err := ebpf.LoadPinnedMap(path, nil) - if err != nil { - return fmt.Errorf("failed to load pinned map %q: %w", path, err) - } - defer m.Close() - // check if it's really a map because ebpf.LoadPinnedMap does not return - // an error but garbage info on doing this on a prog - if ok, err := isMap(m.FD()); err != nil || !ok { - if err != nil { - return err - } + m, ok := obj.(*ebpf.Map) + if !ok { return nil // skip non map } + defer m.Close() xInfo, err := bpf.ExtendedInfoFromMap(m) if err != nil { @@ -153,58 +145,33 @@ func mapIDsFromProgs(prog *ebpf.Program) (iter.Seq[int], error) { func mapIDsFromPinnedProgs(path string) (iter.Seq[int], error) { mapSet := map[int]bool{} progArrays := []*ebpf.Map{} - err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { + err := pin.WalkDir(path, func(_ string, d fs.DirEntry, obj pin.Pinner, err error) error { if err != nil { return err } if d.IsDir() { return nil // skip directories } - if strings.HasSuffix(path, "/link") || strings.HasSuffix(path, "/link_override") { - return nil // skip BPF links, they make the syscall fail since cilium/ebpf@78074c59 - } - prog, err := ebpf.LoadPinnedProgram(path, nil) - if err != nil { - return fmt.Errorf("failed to load pinned object %q: %w", path, err) - } - defer prog.Close() - if ok, err := isProg(prog.FD()); err != nil || !ok { + switch typedObj := obj.(type) { + case *ebpf.Program: + newIDs, err := mapIDsFromProgs(typedObj) if err != nil { - return err + return fmt.Errorf("failed to retrieve map IDs from prog: %w", err) } - - // we want to keep a ref to prog array containing tail calls to - // search reference to map inside - ok, err := isMap(prog.FD()) - if err != nil { - return err + typedObj.Close() + for id := range newIDs { + mapSet[id] = true } - if ok { - m, err := ebpf.LoadPinnedMap(path, &ebpf.LoadPinOptions{ - ReadOnly: true, - }) - if err != nil { - return fmt.Errorf("failed to load pinned map %q: %w", path, err) - } - if m.Type() == ebpf.ProgramArray { - progArrays = append(progArrays, m) - // don't forget to close those files when used later on - } else { - m.Close() - } + case *ebpf.Map: + if typedObj.Type() == ebpf.ProgramArray { + progArrays = append(progArrays, typedObj) + // don't forget to close those files when used later on + } else { + typedObj.Close() } - - return nil // skip the non-prog } - newIDs, err := mapIDsFromProgs(prog) - if err != nil { - return fmt.Errorf("failed to retrieve map IDs from prog: %w", err) - } - for id := range newIDs { - mapSet[id] = true - } return nil }) if err != nil { @@ -248,22 +215,6 @@ func mapIDsFromPinnedProgs(path string) (iter.Seq[int], error) { return maps.Keys(mapSet), nil } -func isProg(fd int) (bool, error) { - return isBPFObject("prog", fd) -} - -func isMap(fd int) (bool, error) { - return isBPFObject("map", fd) -} - -func isBPFObject(object string, fd int) (bool, error) { - readlink, err := os.Readlink(fmt.Sprintf("/proc/self/fd/%d", fd)) - if err != nil { - return false, fmt.Errorf("failed to readlink the fd (%d): %w", fd, err) - } - return readlink == fmt.Sprintf("anon_inode:bpf-%s", object), nil -} - const TetragonBPFFS = "/sys/fs/bpf/tetragon" type DiffMap struct { diff --git a/vendor/github.com/cilium/ebpf/Makefile b/vendor/github.com/cilium/ebpf/Makefile index 13fff832ddd..e0fe9749206 100644 --- a/vendor/github.com/cilium/ebpf/Makefile +++ b/vendor/github.com/cilium/ebpf/Makefile @@ -39,7 +39,6 @@ TARGETS := \ testdata/subprog_reloc \ testdata/fwd_decl \ testdata/kconfig \ - testdata/kconfig_config \ testdata/ksym \ testdata/kfunc \ testdata/invalid-kfunc \ diff --git a/vendor/github.com/cilium/ebpf/README.md b/vendor/github.com/cilium/ebpf/README.md index 85871db1ae3..8238256c8ed 100644 --- a/vendor/github.com/cilium/ebpf/README.md +++ b/vendor/github.com/cilium/ebpf/README.md @@ -53,6 +53,7 @@ This library includes the following packages: * [rlimit](https://pkg.go.dev/github.com/cilium/ebpf/rlimit) provides a convenient API to lift the `RLIMIT_MEMLOCK` constraint on kernels before 5.11. * [btf](https://pkg.go.dev/github.com/cilium/ebpf/btf) allows reading the BPF Type Format. +* [pin](https://pkg.go.dev/github.com/cilium/ebpf/pin) provides APIs for working with pinned objects on bpffs. ## Requirements diff --git a/vendor/github.com/cilium/ebpf/btf/format.go b/vendor/github.com/cilium/ebpf/btf/format.go index 5e581b4a851..3e0dedaa2bd 100644 --- a/vendor/github.com/cilium/ebpf/btf/format.go +++ b/vendor/github.com/cilium/ebpf/btf/format.go @@ -161,6 +161,9 @@ func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error { case *Datasec: err = gf.writeDatasecLit(v, depth) + case *Var: + err = gf.writeTypeLit(v.Type, depth) + default: return fmt.Errorf("type %T: %w", v, ErrNotSupported) } diff --git a/vendor/github.com/cilium/ebpf/btf/types.go b/vendor/github.com/cilium/ebpf/btf/types.go index 44d393067fe..dbcdf9dd7aa 100644 --- a/vendor/github.com/cilium/ebpf/btf/types.go +++ b/vendor/github.com/cilium/ebpf/btf/types.go @@ -1291,6 +1291,20 @@ func UnderlyingType(typ Type) Type { return &cycle{typ} } +// QualifiedType returns the type with all qualifiers removed. +func QualifiedType(typ Type) Type { + result := typ + for depth := 0; depth <= maxResolveDepth; depth++ { + switch v := (result).(type) { + case qualifier: + result = v.qualify() + default: + return result + } + } + return &cycle{typ} +} + // As returns typ if is of type T. Otherwise it peels qualifiers and Typedefs // until it finds a T. // diff --git a/vendor/github.com/cilium/ebpf/collection.go b/vendor/github.com/cilium/ebpf/collection.go index 459ea90d8f8..1bda110a401 100644 --- a/vendor/github.com/cilium/ebpf/collection.go +++ b/vendor/github.com/cilium/ebpf/collection.go @@ -701,6 +701,7 @@ func resolveKconfig(m *MapSpec) error { type configInfo struct { offset uint32 + size uint32 typ btf.Type } @@ -742,6 +743,7 @@ func resolveKconfig(m *MapSpec) error { default: // Catch CONFIG_*. configs[n] = configInfo{ offset: vsi.Offset, + size: vsi.Size, typ: v.Type, } } @@ -768,10 +770,10 @@ func resolveKconfig(m *MapSpec) error { for n, info := range configs { value, ok := kernelConfig[n] if !ok { - return fmt.Errorf("config option %q does not exists for this kernel", n) + return fmt.Errorf("config option %q does not exist on this kernel", n) } - err := kconfig.PutValue(data[info.offset:], info.typ, value) + err := kconfig.PutValue(data[info.offset:info.offset+info.size], info.typ, value) if err != nil { return fmt.Errorf("problem adding value for %s: %w", n, err) } diff --git a/vendor/github.com/cilium/ebpf/elf_reader.go b/vendor/github.com/cilium/ebpf/elf_reader.go index 47656380792..9e8dbc7ae5a 100644 --- a/vendor/github.com/cilium/ebpf/elf_reader.go +++ b/vendor/github.com/cilium/ebpf/elf_reader.go @@ -1013,6 +1013,13 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b } } + // Some maps don't support value sizes, but annotating their map definitions + // with __type macros can still be useful, especially to let bpf2go generate + // type definitions for them. + if value != nil && !mapType.canHaveValueSize() { + valueSize = 0 + } + return &MapSpec{ Name: SanitizeName(name, -1), Type: MapType(mapType), diff --git a/vendor/github.com/cilium/ebpf/features/misc.go b/vendor/github.com/cilium/ebpf/features/misc.go index 0c4e1a2619a..c039020a95e 100644 --- a/vendor/github.com/cilium/ebpf/features/misc.go +++ b/vendor/github.com/cilium/ebpf/features/misc.go @@ -1,9 +1,12 @@ package features import ( + "errors" + "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/sys" ) // HaveLargeInstructions probes the running kernel if more than 4096 instructions @@ -62,7 +65,7 @@ func HaveV2ISA() error { } var haveV2ISA = internal.NewFeatureTest("v2 ISA", func() error { - return probeProgram(&ebpf.ProgramSpec{ + err := probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), @@ -71,6 +74,11 @@ var haveV2ISA = internal.NewFeatureTest("v2 ISA", func() error { asm.Return().WithSymbol("exit"), }, }) + // This sometimes bubbles up from the JIT on aarch64. + if errors.Is(err, sys.ENOTSUPP) { + return ebpf.ErrNotSupported + } + return err }, "4.14") // HaveV3ISA probes the running kernel if instructions of the v3 ISA are supported. @@ -83,7 +91,7 @@ func HaveV3ISA() error { } var haveV3ISA = internal.NewFeatureTest("v3 ISA", func() error { - return probeProgram(&ebpf.ProgramSpec{ + err := probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), @@ -92,4 +100,36 @@ var haveV3ISA = internal.NewFeatureTest("v3 ISA", func() error { asm.Return().WithSymbol("exit"), }, }) + // This sometimes bubbles up from the JIT on aarch64. + if errors.Is(err, sys.ENOTSUPP) { + return ebpf.ErrNotSupported + } + return err }, "5.1") + +// HaveV4ISA probes the running kernel if instructions of the v4 ISA are supported. +// +// Upstream commit 1f9a1ea821ff ("bpf: Support new sign-extension load insns"). +// +// See the package documentation for the meaning of the error return value. +func HaveV4ISA() error { + return haveV4ISA() +} + +var haveV4ISA = internal.NewFeatureTest("v4 ISA", func() error { + err := probeProgram(&ebpf.ProgramSpec{ + Type: ebpf.SocketFilter, + Instructions: asm.Instructions{ + asm.Mov.Imm(asm.R0, 0), + asm.JEq.Imm(asm.R0, 1, "error"), + asm.LongJump("exit"), + asm.Mov.Imm(asm.R0, 1).WithSymbol("error"), + asm.Return().WithSymbol("exit"), + }, + }) + // This sometimes bubbles up from the JIT on aarch64. + if errors.Is(err, sys.ENOTSUPP) { + return ebpf.ErrNotSupported + } + return err +}, "6.6") diff --git a/vendor/github.com/cilium/ebpf/internal/epoll/poller.go b/vendor/github.com/cilium/ebpf/internal/epoll/poller.go index 72f0ab54cca..733e839be7b 100644 --- a/vendor/github.com/cilium/ebpf/internal/epoll/poller.go +++ b/vendor/github.com/cilium/ebpf/internal/epoll/poller.go @@ -168,13 +168,8 @@ func (p *Poller) Wait(events []unix.EpollEvent, deadline time.Time) (int, error) for { timeout := int(-1) if !deadline.IsZero() { - msec := time.Until(deadline).Milliseconds() - // Deadline is in the past, don't block. - msec = max(msec, 0) - // Deadline is too far in the future. - msec = min(msec, math.MaxInt) - - timeout = int(msec) + // Ensure deadline is not in the past and not too far into the future. + timeout = int(internal.Between(time.Until(deadline).Milliseconds(), 0, math.MaxInt)) } n, err := unix.EpollWait(p.epollFd, events, timeout) diff --git a/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go b/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go index c32c066eb0b..29c62b6266e 100644 --- a/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go +++ b/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go @@ -103,12 +103,13 @@ func PutValue(data []byte, typ btf.Type, value string) error { switch value { case "y", "n", "m": return putValueTri(data, typ, value) - default: - if strings.HasPrefix(value, `"`) { - return putValueString(data, typ, value) - } - return putValueNumber(data, typ, value) } + + if strings.HasPrefix(value, `"`) { + return putValueString(data, typ, value) + } + + return putValueNumber(data, typ, value) } // Golang translation of libbpf_tristate enum: @@ -145,6 +146,10 @@ func putValueTri(data []byte, typ btf.Type, value string) error { return fmt.Errorf("cannot use enum %q, only libbpf_tristate is supported", v.Name) } + if len(data) != 4 { + return fmt.Errorf("expected enum value to occupy 4 bytes in datasec, got: %d", len(data)) + } + var tri triState switch value { case "y": @@ -154,10 +159,10 @@ func putValueTri(data []byte, typ btf.Type, value string) error { case "n": tri = TriNo default: - return fmt.Errorf("value %q is not support for libbpf_tristate", value) + return fmt.Errorf("value %q is not supported for libbpf_tristate", value) } - internal.NativeEndian.PutUint64(data, uint64(tri)) + internal.NativeEndian.PutUint32(data, uint32(tri)) default: return fmt.Errorf("cannot add number value, expected btf.Int or btf.Enum, got: %T", v) } diff --git a/vendor/github.com/cilium/ebpf/internal/math.go b/vendor/github.com/cilium/ebpf/internal/math.go index 93a1670728e..10cde66860d 100644 --- a/vendor/github.com/cilium/ebpf/internal/math.go +++ b/vendor/github.com/cilium/ebpf/internal/math.go @@ -10,6 +10,17 @@ func IsPow[I Integer](n I) bool { return n != 0 && (n&(n-1)) == 0 } +// Between returns the value clamped between a and b. +func Between[I Integer](val, a, b I) I { + lower, upper := a, b + if lower > upper { + upper, lower = a, b + } + + val = min(val, upper) + return max(val, lower) +} + // Integer represents all possible integer types. // Remove when x/exp/constraints is moved to the standard library. type Integer interface { diff --git a/vendor/github.com/cilium/ebpf/internal/sys/fd.go b/vendor/github.com/cilium/ebpf/internal/sys/fd.go index 028be004557..e2ba43fd3b3 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/fd.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/fd.go @@ -4,8 +4,10 @@ import ( "fmt" "math" "os" + "path/filepath" "runtime" "strconv" + "strings" "github.com/cilium/ebpf/internal/testutils/fdtrace" "github.com/cilium/ebpf/internal/unix" @@ -79,10 +81,13 @@ func (fd *FD) Close() error { return nil } - return unix.Close(fd.disown()) + return unix.Close(fd.Disown()) } -func (fd *FD) disown() int { +// Disown destroys the FD and returns its raw file descriptor without closing +// it. After this call, the underlying fd is no longer tied to the FD's +// lifecycle. +func (fd *FD) Disown() int { value := fd.raw fdtrace.ForgetFD(value) fd.raw = -1 @@ -116,5 +121,45 @@ func (fd *FD) File(name string) *os.File { return nil } - return os.NewFile(uintptr(fd.disown()), name) + return os.NewFile(uintptr(fd.Disown()), name) +} + +// ObjGetTyped wraps [ObjGet] with a readlink call to extract the type of the +// underlying bpf object. +func ObjGetTyped(attr *ObjGetAttr) (*FD, ObjType, error) { + fd, err := ObjGet(attr) + if err != nil { + return nil, 0, err + } + + typ, err := readType(fd) + if err != nil { + _ = fd.Close() + return nil, 0, fmt.Errorf("reading fd type: %w", err) + } + + return fd, typ, nil +} + +// readType returns the bpf object type of the file descriptor by calling +// readlink(3). Returns an error if the file descriptor does not represent a bpf +// object. +func readType(fd *FD) (ObjType, error) { + s, err := os.Readlink(filepath.Join("/proc/self/fd/", fd.String())) + if err != nil { + return 0, fmt.Errorf("readlink fd %d: %w", fd.Int(), err) + } + + s = strings.TrimPrefix(s, "anon_inode:") + + switch s { + case "bpf-map": + return BPF_TYPE_MAP, nil + case "bpf-prog": + return BPF_TYPE_PROG, nil + case "bpf-link": + return BPF_TYPE_LINK, nil + } + + return 0, fmt.Errorf("unknown type %s of fd %d", s, fd.Int()) } diff --git a/vendor/github.com/cilium/ebpf/internal/sys/types.go b/vendor/github.com/cilium/ebpf/internal/sys/types.go index f8792da0526..88001c319eb 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/types.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/types.go @@ -566,6 +566,15 @@ const ( BPF_MAP_TYPE_CGRP_STORAGE MapType = 32 ) +type ObjType uint32 + +const ( + BPF_TYPE_UNSPEC ObjType = 0 + BPF_TYPE_PROG ObjType = 1 + BPF_TYPE_MAP ObjType = 2 + BPF_TYPE_LINK ObjType = 3 +) + type PerfEventType uint32 const ( diff --git a/vendor/github.com/cilium/ebpf/link/link.go b/vendor/github.com/cilium/ebpf/link/link.go index eef834a812d..796769f8ea4 100644 --- a/vendor/github.com/cilium/ebpf/link/link.go +++ b/vendor/github.com/cilium/ebpf/link/link.go @@ -78,7 +78,9 @@ func NewFromID(id ID) (Link, error) { return wrapRawLink(&RawLink{fd, ""}) } -// LoadPinnedLink loads a link that was persisted into a bpffs. +// LoadPinnedLink loads a Link from a pin (file) on the BPF virtual filesystem. +// +// Requires at least Linux 5.7. func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) { raw, err := loadPinnedRawLink(fileName, opts) if err != nil { @@ -350,7 +352,7 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { } func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) { - fd, err := sys.ObjGet(&sys.ObjGetAttr{ + fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(fileName), FileFlags: opts.Marshal(), }) @@ -358,6 +360,11 @@ func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, er return nil, fmt.Errorf("load pinned link: %w", err) } + if typ != sys.BPF_TYPE_LINK { + _ = fd.Close() + return nil, fmt.Errorf("%s is not a Link", fileName) + } + return &RawLink{fd, fileName}, nil } diff --git a/vendor/github.com/cilium/ebpf/map.go b/vendor/github.com/cilium/ebpf/map.go index c5010b4190d..e5d8bd78095 100644 --- a/vendor/github.com/cilium/ebpf/map.go +++ b/vendor/github.com/cilium/ebpf/map.go @@ -158,6 +158,17 @@ func (spec *MapSpec) fixupMagicFields() (*MapSpec, error) { // behaviour in the past. spec.MaxEntries = n } + + case CPUMap: + n, err := PossibleCPU() + if err != nil { + return nil, fmt.Errorf("fixup cpu map: %w", err) + } + + if n := uint32(n); spec.MaxEntries == 0 || spec.MaxEntries > n { + // Perform clamping similar to PerfEventArray. + spec.MaxEntries = n + } } return spec, nil @@ -1560,9 +1571,11 @@ func (m *Map) unmarshalValue(value any, buf sysenc.Buffer) error { return buf.Unmarshal(value) } -// LoadPinnedMap loads a Map from a BPF file. +// LoadPinnedMap opens a Map from a pin (file) on the BPF virtual filesystem. +// +// Requires at least Linux 4.5. func LoadPinnedMap(fileName string, opts *LoadPinOptions) (*Map, error) { - fd, err := sys.ObjGet(&sys.ObjGetAttr{ + fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(fileName), FileFlags: opts.Marshal(), }) @@ -1570,6 +1583,11 @@ func LoadPinnedMap(fileName string, opts *LoadPinOptions) (*Map, error) { return nil, err } + if typ != sys.BPF_TYPE_MAP { + _ = fd.Close() + return nil, fmt.Errorf("%s is not a Map", fileName) + } + m, err := newMapFromFD(fd) if err == nil { m.pinnedPath = fileName diff --git a/vendor/github.com/cilium/ebpf/pin/doc.go b/vendor/github.com/cilium/ebpf/pin/doc.go new file mode 100644 index 00000000000..3b602906887 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/pin/doc.go @@ -0,0 +1,3 @@ +// Package pin provides utility functions for working with pinned objects on bpffs. + +package pin diff --git a/vendor/github.com/cilium/ebpf/pin/load.go b/vendor/github.com/cilium/ebpf/pin/load.go new file mode 100644 index 00000000000..817e177e2b1 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/pin/load.go @@ -0,0 +1,40 @@ +package pin + +import ( + "fmt" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal/sys" + "github.com/cilium/ebpf/link" +) + +// Pinner is an interface implemented by all eBPF objects that support pinning +// to a bpf virtual filesystem. +type Pinner interface { + Pin(string) error +} + +// Load retrieves a pinned object from a bpf virtual filesystem. It returns one +// of [ebpf.Map], [ebpf.Program], or [link.Link]. +// +// Trying to open anything other than a bpf object is an error. +func Load(path string, opts *ebpf.LoadPinOptions) (Pinner, error) { + fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{ + Pathname: sys.NewStringPointer(path), + FileFlags: opts.Marshal(), + }) + if err != nil { + return nil, fmt.Errorf("opening pin %s: %w", path, err) + } + + switch typ { + case sys.BPF_TYPE_MAP: + return ebpf.NewMapFromFD(fd.Disown()) + case sys.BPF_TYPE_PROG: + return ebpf.NewProgramFromFD(fd.Disown()) + case sys.BPF_TYPE_LINK: + return link.NewFromFD(fd.Disown()) + } + + return nil, fmt.Errorf("unknown object type %d", typ) +} diff --git a/vendor/github.com/cilium/ebpf/pin/walk.go b/vendor/github.com/cilium/ebpf/pin/walk.go new file mode 100644 index 00000000000..96335f0021a --- /dev/null +++ b/vendor/github.com/cilium/ebpf/pin/walk.go @@ -0,0 +1,49 @@ +package pin + +import ( + "fmt" + "io/fs" + "os" + "path/filepath" + + "github.com/cilium/ebpf/internal/linux" + "github.com/cilium/ebpf/internal/unix" +) + +// WalkDirFunc is the type of the function called for each object visited by +// [WalkDir]. It's identical to [fs.WalkDirFunc], but with an extra [Pinner] +// argument. If the visited node is a directory, obj is nil. +// +// err contains any errors encountered during bpffs traversal or object loading. +type WalkDirFunc func(path string, d fs.DirEntry, obj Pinner, err error) error + +// WalkDir walks the file tree rooted at path, calling bpffn for each node in +// the tree, including directories. Running WalkDir on a non-bpf filesystem is +// an error. Otherwise identical in behavior to [fs.WalkDir]. +// +// See the [WalkDirFunc] for more information. +func WalkDir(root string, bpffn WalkDirFunc) error { + fsType, err := linux.FSType(root) + if err != nil { + return err + } + if fsType != unix.BPF_FS_MAGIC { + return fmt.Errorf("%s is not on a bpf filesystem", root) + } + + fn := func(path string, d fs.DirEntry, err error) error { + if err != nil { + return bpffn(path, nil, nil, err) + } + + if d.IsDir() { + return bpffn(path, d, nil, err) + } + + obj, err := Load(filepath.Join(root, path), nil) + + return bpffn(path, d, obj, err) + } + + return fs.WalkDir(os.DirFS(root), ".", fn) +} diff --git a/vendor/github.com/cilium/ebpf/prog.go b/vendor/github.com/cilium/ebpf/prog.go index bde48a56be0..4f3ce43bfae 100644 --- a/vendor/github.com/cilium/ebpf/prog.go +++ b/vendor/github.com/cilium/ebpf/prog.go @@ -51,6 +51,11 @@ const ( // verifier log. const minVerifierLogSize = 64 * 1024 +// maxVerifierLogSize is the maximum size of verifier log buffer the kernel +// will accept before returning EINVAL. May be increased to MaxUint32 in the +// future, but avoid the unnecessary EINVAL for now. +const maxVerifierLogSize = math.MaxUint32 >> 2 + // ProgramOptions control loading a program into the kernel. type ProgramOptions struct { // Bitmap controlling the detail emitted by the kernel's eBPF verifier log. @@ -70,6 +75,11 @@ type ProgramOptions struct { // attempt at loading the program. LogLevel LogLevel + // Starting size of the verifier log buffer. If the verifier log is larger + // than this size, the buffer will be grown to fit the entire log. Leave at + // its default value unless troubleshooting. + LogSizeStart uint32 + // Disables the verifier log completely, regardless of other options. LogDisabled bool @@ -401,9 +411,10 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er // The caller requested a specific verifier log level. Set up the log buffer // so that there is a chance of loading the program in a single shot. + logSize := internal.Between(opts.LogSizeStart, minVerifierLogSize, maxVerifierLogSize) var logBuf []byte if !opts.LogDisabled && opts.LogLevel != 0 { - logBuf = make([]byte, minVerifierLogSize) + logBuf = make([]byte, logSize) attr.LogLevel = opts.LogLevel attr.LogSize = uint32(len(logBuf)) attr.LogBuf = sys.NewSlicePointer(logBuf) @@ -437,12 +448,11 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er attr.LogLevel = LogLevelBranch } - // Make an educated guess how large the buffer should be. Start - // at minVerifierLogSize and then double the size. - logSize := uint32(max(len(logBuf)*2, minVerifierLogSize)) - if int(logSize) < len(logBuf) { - return nil, errors.New("overflow while probing log buffer size") - } + // Make an educated guess how large the buffer should be by multiplying. + // Ensure the size doesn't overflow. + const factor = 2 + logSize = internal.Between(logSize, minVerifierLogSize, maxVerifierLogSize/factor) + logSize *= factor if attr.LogTrueSize != 0 { // The kernel has given us a hint how large the log buffer has to be. @@ -468,6 +478,12 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er return nil, fmt.Errorf("load program: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err) } + case errors.Is(err, unix.EFAULT): + // EFAULT is returned when the kernel hits a verifier bug, and always + // overrides ENOSPC, defeating the buffer growth strategy. Warn the user + // that they may need to increase the buffer size manually. + return nil, fmt.Errorf("load program: %w (hit verifier bug, increase LogSizeStart to fit the log and check dmesg)", err) + case errors.Is(err, unix.EINVAL): if bytes.Contains(tail, coreBadCall) { err = errBadRelocation @@ -906,11 +922,12 @@ func marshalProgram(p *Program, length int) ([]byte, error) { return buf, nil } -// LoadPinnedProgram loads a Program from a BPF file. +// LoadPinnedProgram loads a Program from a pin (file) on the BPF virtual +// filesystem. // // Requires at least Linux 4.11. func LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error) { - fd, err := sys.ObjGet(&sys.ObjGetAttr{ + fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(fileName), FileFlags: opts.Marshal(), }) @@ -918,6 +935,11 @@ func LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error) return nil, err } + if typ != sys.BPF_TYPE_PROG { + _ = fd.Close() + return nil, fmt.Errorf("%s is not a Program", fileName) + } + info, err := newProgramInfoFromFd(fd) if err != nil { _ = fd.Close() diff --git a/vendor/github.com/cilium/ebpf/types.go b/vendor/github.com/cilium/ebpf/types.go index 02493f74cf6..211b308bbc7 100644 --- a/vendor/github.com/cilium/ebpf/types.go +++ b/vendor/github.com/cilium/ebpf/types.go @@ -127,6 +127,21 @@ func (mt MapType) canStoreProgram() bool { return mt == ProgramArray } +// canHaveValueSize returns true if the map type supports setting a value size. +func (mt MapType) canHaveValueSize() bool { + switch mt { + case RingBuf, Arena: + return false + + // Special-case perf events since they require a value size of either 0 or 4 + // for historical reasons. Let the library fix this up later. + case PerfEventArray: + return false + } + + return true +} + // ProgramType of the eBPF program type ProgramType uint32 diff --git a/vendor/modules.txt b/vendor/modules.txt index 6f184581f07..e5c1ae810ab 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -63,7 +63,7 @@ github.com/cilium/cilium/pkg/resiliency github.com/cilium/cilium/pkg/time github.com/cilium/cilium/pkg/version github.com/cilium/cilium/pkg/versioncheck -# github.com/cilium/ebpf v0.16.1-0.20241205185900-f0eec7efba9d +# github.com/cilium/ebpf v0.17.1 ## explicit; go 1.22 github.com/cilium/ebpf github.com/cilium/ebpf/asm @@ -81,6 +81,7 @@ github.com/cilium/ebpf/internal/tracefs github.com/cilium/ebpf/internal/unix github.com/cilium/ebpf/link github.com/cilium/ebpf/perf +github.com/cilium/ebpf/pin github.com/cilium/ebpf/rlimit # github.com/cilium/little-vm-helper v0.0.19 ## explicit; go 1.22.0