From e809db842fa8bf66dd2b6b16a347b6a6a60ab9f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 00:07:03 +0000 Subject: [PATCH] build(deps): bump github.com/cilium/ebpf from 0.16.0 to 0.17.0 Bumps [github.com/cilium/ebpf](https://github.com/cilium/ebpf) from 0.16.0 to 0.17.0. - [Release notes](https://github.com/cilium/ebpf/releases) - [Commits](https://github.com/cilium/ebpf/compare/v0.16.0...v0.17.0) --- updated-dependencies: - dependency-name: github.com/cilium/ebpf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 3 +- go.sum | 6 +- vendor/github.com/cilium/ebpf/.golangci.yaml | 6 + vendor/github.com/cilium/ebpf/CODEOWNERS | 2 + vendor/github.com/cilium/ebpf/Makefile | 6 +- vendor/github.com/cilium/ebpf/README.md | 1 + .../github.com/cilium/ebpf/asm/instruction.go | 3 +- vendor/github.com/cilium/ebpf/btf/btf.go | 24 +- .../github.com/cilium/ebpf/btf/btf_types.go | 1 + vendor/github.com/cilium/ebpf/btf/ext_info.go | 150 +++---- vendor/github.com/cilium/ebpf/btf/feature.go | 55 ++- vendor/github.com/cilium/ebpf/btf/format.go | 3 + vendor/github.com/cilium/ebpf/btf/kernel.go | 6 +- vendor/github.com/cilium/ebpf/btf/marshal.go | 83 +++- .../github.com/cilium/ebpf/btf/traversal.go | 48 ++- vendor/github.com/cilium/ebpf/btf/types.go | 138 ++++++- .../github.com/cilium/ebpf/btf/workarounds.go | 2 +- vendor/github.com/cilium/ebpf/collection.go | 253 ++++++++---- vendor/github.com/cilium/ebpf/elf_reader.go | 172 ++++++-- vendor/github.com/cilium/ebpf/info.go | 370 ++++++++++++++++-- .../github.com/cilium/ebpf/internal/errors.go | 6 +- .../cilium/ebpf/internal/feature.go | 55 ++- .../cilium/ebpf/internal/kallsyms/cache.go | 20 + .../cilium/ebpf/internal/kallsyms/kallsyms.go | 291 +++++++++++--- .../cilium/ebpf/internal/kallsyms/reader.go | 118 ++++++ .../cilium/ebpf/internal/kconfig/kconfig.go | 45 +-- .../cilium/ebpf/internal/{ => linux}/auxv.go | 2 +- .../cilium/ebpf/internal/linux/doc.go | 2 + .../cilium/ebpf/internal/linux/kconfig.go | 31 ++ .../ebpf/internal/{ => linux}/platform.go | 2 +- .../ebpf/internal/{ => linux}/statfs.go | 2 +- .../cilium/ebpf/internal/{ => linux}/vdso.go | 9 +- .../cilium/ebpf/internal/linux/version.go | 34 ++ .../github.com/cilium/ebpf/internal/math.go | 28 +- .../github.com/cilium/ebpf/internal/sys/fd.go | 74 +++- .../cilium/ebpf/internal/sys/fd_trace.go | 93 ----- .../ebpf/internal/sys/mapflags_string.go | 53 --- .../cilium/ebpf/internal/{ => sys}/pinning.go | 16 +- .../cilium/ebpf/internal/sys/ptr.go | 6 +- .../cilium/ebpf/internal/sys/syscall.go | 37 +- .../cilium/ebpf/internal/sys/types.go | 185 ++++++++- .../cilium/ebpf/internal/sysenc/buffer.go | 10 +- .../internal/testutils/fdtrace/fd_trace.go | 103 +++++ .../ebpf/internal/testutils/fdtrace/main.go | 31 ++ .../cilium/ebpf/internal/tracefs/kprobe.go | 8 +- .../cilium/ebpf/internal/unix/types_linux.go | 5 +- .../cilium/ebpf/internal/unix/types_other.go | 5 +- .../cilium/ebpf/internal/version.go | 30 -- vendor/github.com/cilium/ebpf/link/kprobe.go | 12 +- .../cilium/ebpf/link/kprobe_multi.go | 8 +- vendor/github.com/cilium/ebpf/link/link.go | 15 +- .../github.com/cilium/ebpf/link/netfilter.go | 2 +- .../github.com/cilium/ebpf/link/perf_event.go | 8 +- .../github.com/cilium/ebpf/link/syscalls.go | 24 +- vendor/github.com/cilium/ebpf/link/tracing.go | 2 +- vendor/github.com/cilium/ebpf/link/uprobe.go | 6 +- .../cilium/ebpf/link/uprobe_multi.go | 13 +- vendor/github.com/cilium/ebpf/linker.go | 41 ++ vendor/github.com/cilium/ebpf/map.go | 127 ++++-- vendor/github.com/cilium/ebpf/memory.go | 145 +++++++ vendor/github.com/cilium/ebpf/prog.go | 102 +++-- vendor/github.com/cilium/ebpf/syscalls.go | 80 ++-- vendor/github.com/cilium/ebpf/types.go | 28 +- vendor/github.com/cilium/ebpf/types_string.go | 8 +- vendor/github.com/cilium/ebpf/variable.go | 230 +++++++++++ vendor/golang.org/x/exp/LICENSE | 27 -- vendor/golang.org/x/exp/PATENTS | 22 -- .../x/exp/constraints/constraints.go | 50 --- vendor/modules.txt | 9 +- 69 files changed, 2727 insertions(+), 865 deletions(-) create mode 100644 vendor/github.com/cilium/ebpf/internal/kallsyms/cache.go create mode 100644 vendor/github.com/cilium/ebpf/internal/kallsyms/reader.go rename vendor/github.com/cilium/ebpf/internal/{ => linux}/auxv.go (98%) create mode 100644 vendor/github.com/cilium/ebpf/internal/linux/doc.go create mode 100644 vendor/github.com/cilium/ebpf/internal/linux/kconfig.go rename vendor/github.com/cilium/ebpf/internal/{ => linux}/platform.go (97%) rename vendor/github.com/cilium/ebpf/internal/{ => linux}/statfs.go (96%) rename vendor/github.com/cilium/ebpf/internal/{ => linux}/vdso.go (93%) create mode 100644 vendor/github.com/cilium/ebpf/internal/linux/version.go delete mode 100644 vendor/github.com/cilium/ebpf/internal/sys/fd_trace.go delete mode 100644 vendor/github.com/cilium/ebpf/internal/sys/mapflags_string.go rename vendor/github.com/cilium/ebpf/internal/{ => sys}/pinning.go (77%) create mode 100644 vendor/github.com/cilium/ebpf/internal/testutils/fdtrace/fd_trace.go create mode 100644 vendor/github.com/cilium/ebpf/internal/testutils/fdtrace/main.go create mode 100644 vendor/github.com/cilium/ebpf/memory.go create mode 100644 vendor/github.com/cilium/ebpf/variable.go delete mode 100644 vendor/golang.org/x/exp/LICENSE delete mode 100644 vendor/golang.org/x/exp/PATENTS delete mode 100644 vendor/golang.org/x/exp/constraints/constraints.go diff --git a/go.mod b/go.mod index 3d844324a81..c90769e5af7 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ toolchain go1.22.4 require ( github.com/checkpoint-restore/go-criu/v6 v6.3.0 - github.com/cilium/ebpf v0.16.0 + github.com/cilium/ebpf v0.17.0 github.com/containerd/console v1.0.4 github.com/coreos/go-systemd/v22 v22.5.0 github.com/cyphar/filepath-securejoin v0.3.6 @@ -35,5 +35,4 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/vishvananda/netns v0.0.4 // indirect - golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect ) diff --git a/go.sum b/go.sum index 53d1e395311..daf1b74a81c 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/checkpoint-restore/go-criu/v6 v6.3.0 h1:mIdrSO2cPNWQY1truPg6uHLXyKHk3Z5Odx4wjKOASzA= github.com/checkpoint-restore/go-criu/v6 v6.3.0/go.mod h1:rrRTN/uSwY2X+BPRl/gkulo9gsKOSAeVp9/K2tv7xZI= -github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= -github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= +github.com/cilium/ebpf v0.17.0 h1:wAgiAWE86w8DxbR/UJ9KsaaoUfjrO0yhg5yE7f898As= +github.com/cilium/ebpf v0.17.0/go.mod h1:vay2FaYSmIlv3r8dNACd4mW/OCaZLJKJOo+IHBvCIO8= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= @@ -81,8 +81,6 @@ github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQ github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= -golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= -golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= diff --git a/vendor/github.com/cilium/ebpf/.golangci.yaml b/vendor/github.com/cilium/ebpf/.golangci.yaml index 65f91b910bf..366d4893f20 100644 --- a/vendor/github.com/cilium/ebpf/.golangci.yaml +++ b/vendor/github.com/cilium/ebpf/.golangci.yaml @@ -11,3 +11,9 @@ linters: - typecheck - unused - gofmt +linters-settings: + goimports: + # A comma-separated list of prefixes, which, if set, checks import paths + # with the given prefixes are grouped after 3rd-party packages. + # Default: "" + local-prefixes: github.com/cilium/ebpf diff --git a/vendor/github.com/cilium/ebpf/CODEOWNERS b/vendor/github.com/cilium/ebpf/CODEOWNERS index ca65d23c09d..0f76dce85c8 100644 --- a/vendor/github.com/cilium/ebpf/CODEOWNERS +++ b/vendor/github.com/cilium/ebpf/CODEOWNERS @@ -9,3 +9,5 @@ ringbuf/ @florianl btf/ @dylandreimerink cmd/bpf2go/ @mejedi + +docs/ @ti-mo diff --git a/vendor/github.com/cilium/ebpf/Makefile b/vendor/github.com/cilium/ebpf/Makefile index d355eea71ca..e0fe9749206 100644 --- a/vendor/github.com/cilium/ebpf/Makefile +++ b/vendor/github.com/cilium/ebpf/Makefile @@ -39,16 +39,18 @@ TARGETS := \ testdata/subprog_reloc \ testdata/fwd_decl \ testdata/kconfig \ - testdata/kconfig_config \ + testdata/ksym \ testdata/kfunc \ testdata/invalid-kfunc \ testdata/kfunc-kmod \ testdata/constants \ testdata/errors \ + testdata/variables \ btf/testdata/relocs \ btf/testdata/relocs_read \ btf/testdata/relocs_read_tgt \ btf/testdata/relocs_enum \ + btf/testdata/tags \ cmd/bpf2go/testdata/minimal .PHONY: all clean container-all container-shell generate @@ -57,7 +59,7 @@ TARGETS := \ # Build all ELF binaries using a containerized LLVM toolchain. container-all: - +${CONTAINER_ENGINE} run --rm -t ${CONTAINER_RUN_ARGS} \ + +${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \ -v "${REPODIR}":/ebpf -w /ebpf --env MAKEFLAGS \ --env HOME="/tmp" \ --env BPF2GO_CC="$(CLANG)" \ 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/asm/instruction.go b/vendor/github.com/cilium/ebpf/asm/instruction.go index 67cd39d6f67..86b384c02a9 100644 --- a/vendor/github.com/cilium/ebpf/asm/instruction.go +++ b/vendor/github.com/cilium/ebpf/asm/instruction.go @@ -12,7 +12,6 @@ import ( "strings" "github.com/cilium/ebpf/internal/sys" - "github.com/cilium/ebpf/internal/unix" ) // InstructionSize is the size of a BPF instruction in bytes @@ -804,7 +803,7 @@ func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) { return "", fmt.Errorf("instruction %d: %w", i, err) } } - return hex.EncodeToString(h.Sum(nil)[:unix.BPF_TAG_SIZE]), nil + return hex.EncodeToString(h.Sum(nil)[:sys.BPF_TAG_SIZE]), nil } // encodeFunctionReferences populates the Offset (or Constant, depending on diff --git a/vendor/github.com/cilium/ebpf/btf/btf.go b/vendor/github.com/cilium/ebpf/btf/btf.go index 671f680b2af..880c5ade0c1 100644 --- a/vendor/github.com/cilium/ebpf/btf/btf.go +++ b/vendor/github.com/cilium/ebpf/btf/btf.go @@ -443,13 +443,19 @@ func fixupDatasec(types []Type, sectionSizes map[string]uint32, offsets map[symb // Some Datasecs are virtual and don't have corresponding ELF sections. switch name { case ".ksyms": - // .ksyms describes forward declarations of kfunc signatures. + // .ksyms describes forward declarations of kfunc signatures, as well as + // references to kernel symbols. // Nothing to fix up, all sizes and offsets are 0. for _, vsi := range ds.Vars { - _, ok := vsi.Type.(*Func) - if !ok { - // Only Funcs are supported in the .ksyms Datasec. - return fmt.Errorf("data section %s: expected *btf.Func, not %T: %w", name, vsi.Type, ErrNotSupported) + switch t := vsi.Type.(type) { + case *Func: + continue + case *Var: + if _, ok := t.Type.(*Void); !ok { + return fmt.Errorf("data section %s: expected %s to be *Void, not %T: %w", name, vsi.Type.TypeName(), vsi.Type, ErrNotSupported) + } + default: + return fmt.Errorf("data section %s: expected to be either *btf.Func or *btf.Var, not %T: %w", name, vsi.Type, ErrNotSupported) } } @@ -695,5 +701,13 @@ func (iter *TypesIterator) Next() bool { iter.Type, ok = iter.spec.typeByID(iter.id) iter.id++ iter.done = !ok + if !iter.done { + // Skip declTags, during unmarshaling declTags become `Tags` fields of other types. + // We keep them in the spec to avoid holes in the ID space, but for the purposes of + // iteration, they are not useful to the user. + if _, ok := iter.Type.(*declTag); ok { + return iter.Next() + } + } return !iter.done } diff --git a/vendor/github.com/cilium/ebpf/btf/btf_types.go b/vendor/github.com/cilium/ebpf/btf/btf_types.go index f0e327abc0e..320311b3320 100644 --- a/vendor/github.com/cilium/ebpf/btf/btf_types.go +++ b/vendor/github.com/cilium/ebpf/btf/btf_types.go @@ -39,6 +39,7 @@ const ( kindFloat // Float // Added 5.16 kindDeclTag // DeclTag + // Added 5.17 kindTypeTag // TypeTag // Added 6.0 kindEnum64 // Enum64 diff --git a/vendor/github.com/cilium/ebpf/btf/ext_info.go b/vendor/github.com/cilium/ebpf/btf/ext_info.go index eb9044badf2..2c684fe2a79 100644 --- a/vendor/github.com/cilium/ebpf/btf/ext_info.go +++ b/vendor/github.com/cilium/ebpf/btf/ext_info.go @@ -16,8 +16,8 @@ import ( // ExtInfos contains ELF section metadata. type ExtInfos struct { // The slices are sorted by offset in ascending order. - funcInfos map[string]FuncInfos - lineInfos map[string]LineInfos + funcInfos map[string]FuncOffsets + lineInfos map[string]LineOffsets relocationInfos map[string]CORERelocationInfos } @@ -58,9 +58,9 @@ func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, er return nil, fmt.Errorf("parsing BTF function info: %w", err) } - funcInfos := make(map[string]FuncInfos, len(btfFuncInfos)) + funcInfos := make(map[string]FuncOffsets, len(btfFuncInfos)) for section, bfis := range btfFuncInfos { - funcInfos[section], err = newFuncInfos(bfis, spec) + funcInfos[section], err = newFuncOffsets(bfis, spec) if err != nil { return nil, fmt.Errorf("section %s: func infos: %w", section, err) } @@ -72,7 +72,7 @@ func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, er return nil, fmt.Errorf("parsing BTF line info: %w", err) } - lineInfos := make(map[string]LineInfos, len(btfLineInfos)) + lineInfos := make(map[string]LineOffsets, len(btfLineInfos)) for section, blis := range btfLineInfos { lineInfos[section], err = newLineInfos(blis, spec.strings) if err != nil { @@ -102,8 +102,10 @@ func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, er return &ExtInfos{funcInfos, lineInfos, coreRelos}, nil } -type funcInfoMeta struct{} -type coreRelocationMeta struct{} +type ( + funcInfoMeta struct{} + coreRelocationMeta struct{} +) // Assign per-section metadata from BTF to a section's instructions. func (ei *ExtInfos) Assign(insns asm.Instructions, section string) { @@ -117,20 +119,20 @@ func (ei *ExtInfos) Assign(insns asm.Instructions, section string) { // Assign per-instruction metadata to the instructions in insns. func AssignMetadataToInstructions( insns asm.Instructions, - funcInfos FuncInfos, - lineInfos LineInfos, + funcInfos FuncOffsets, + lineInfos LineOffsets, reloInfos CORERelocationInfos, ) { iter := insns.Iterate() for iter.Next() { - if len(funcInfos.infos) > 0 && funcInfos.infos[0].offset == iter.Offset { - *iter.Ins = WithFuncMetadata(*iter.Ins, funcInfos.infos[0].fn) - funcInfos.infos = funcInfos.infos[1:] + if len(funcInfos) > 0 && funcInfos[0].Offset == iter.Offset { + *iter.Ins = WithFuncMetadata(*iter.Ins, funcInfos[0].Func) + funcInfos = funcInfos[1:] } - if len(lineInfos.infos) > 0 && lineInfos.infos[0].offset == iter.Offset { - *iter.Ins = iter.Ins.WithSource(lineInfos.infos[0].line) - lineInfos.infos = lineInfos.infos[1:] + if len(lineInfos) > 0 && lineInfos[0].Offset == iter.Offset { + *iter.Ins = iter.Ins.WithSource(lineInfos[0].Line) + lineInfos = lineInfos[1:] } if len(reloInfos.infos) > 0 && reloInfos.infos[0].offset == iter.Offset { @@ -159,9 +161,9 @@ marshal: var fiBuf, liBuf bytes.Buffer for { if fn := FuncMetadata(iter.Ins); fn != nil { - fi := &funcInfo{ - fn: fn, - offset: iter.Offset, + fi := &FuncOffset{ + Func: fn, + Offset: iter.Offset, } if err := fi.marshal(&fiBuf, b); err != nil { return nil, nil, fmt.Errorf("write func info: %w", err) @@ -178,9 +180,9 @@ marshal: } } - li := &lineInfo{ - line: line, - offset: iter.Offset, + li := &LineOffset{ + Offset: iter.Offset, + Line: line, } if err := li.marshal(&liBuf, b); err != nil { return nil, nil, fmt.Errorf("write line info: %w", err) @@ -333,17 +335,17 @@ func parseExtInfoRecordSize(r io.Reader, bo binary.ByteOrder) (uint32, error) { return recordSize, nil } -// FuncInfos contains a sorted list of func infos. -type FuncInfos struct { - infos []funcInfo -} +// FuncOffsets is a sorted slice of FuncOffset. +type FuncOffsets []FuncOffset // The size of a FuncInfo in BTF wire format. var FuncInfoSize = uint32(binary.Size(bpfFuncInfo{})) -type funcInfo struct { - fn *Func - offset asm.RawInstructionOffset +// FuncOffset represents a [btf.Func] and its raw instruction offset within a +// BPF program. +type FuncOffset struct { + Offset asm.RawInstructionOffset + Func *Func } type bpfFuncInfo struct { @@ -352,7 +354,7 @@ type bpfFuncInfo struct { TypeID TypeID } -func newFuncInfo(fi bpfFuncInfo, spec *Spec) (*funcInfo, error) { +func newFuncOffset(fi bpfFuncInfo, spec *Spec) (*FuncOffset, error) { typ, err := spec.TypeByID(fi.TypeID) if err != nil { return nil, err @@ -368,31 +370,32 @@ func newFuncInfo(fi bpfFuncInfo, spec *Spec) (*funcInfo, error) { return nil, fmt.Errorf("func with type ID %d doesn't have a name", fi.TypeID) } - return &funcInfo{ - fn, + return &FuncOffset{ asm.RawInstructionOffset(fi.InsnOff), + fn, }, nil } -func newFuncInfos(bfis []bpfFuncInfo, spec *Spec) (FuncInfos, error) { - fis := FuncInfos{ - infos: make([]funcInfo, 0, len(bfis)), - } +func newFuncOffsets(bfis []bpfFuncInfo, spec *Spec) (FuncOffsets, error) { + fos := make(FuncOffsets, 0, len(bfis)) + for _, bfi := range bfis { - fi, err := newFuncInfo(bfi, spec) + fi, err := newFuncOffset(bfi, spec) if err != nil { - return FuncInfos{}, fmt.Errorf("offset %d: %w", bfi.InsnOff, err) + return FuncOffsets{}, fmt.Errorf("offset %d: %w", bfi.InsnOff, err) } - fis.infos = append(fis.infos, *fi) + fos = append(fos, *fi) } - sort.Slice(fis.infos, func(i, j int) bool { - return fis.infos[i].offset <= fis.infos[j].offset + sort.Slice(fos, func(i, j int) bool { + return fos[i].Offset <= fos[j].Offset }) - return fis, nil + return fos, nil } -// LoadFuncInfos parses BTF func info in kernel wire format. -func LoadFuncInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (FuncInfos, error) { +// LoadFuncInfos parses BTF func info from kernel wire format into a +// [FuncOffsets], a sorted slice of [btf.Func]s of (sub)programs within a BPF +// program with their corresponding raw instruction offsets. +func LoadFuncInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (FuncOffsets, error) { fis, err := parseFuncInfoRecords( reader, bo, @@ -401,20 +404,20 @@ func LoadFuncInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec false, ) if err != nil { - return FuncInfos{}, fmt.Errorf("parsing BTF func info: %w", err) + return FuncOffsets{}, fmt.Errorf("parsing BTF func info: %w", err) } - return newFuncInfos(fis, spec) + return newFuncOffsets(fis, spec) } // marshal into the BTF wire format. -func (fi *funcInfo) marshal(w *bytes.Buffer, b *Builder) error { - id, err := b.Add(fi.fn) +func (fi *FuncOffset) marshal(w *bytes.Buffer, b *Builder) error { + id, err := b.Add(fi.Func) if err != nil { return err } bfi := bpfFuncInfo{ - InsnOff: uint32(fi.offset), + InsnOff: uint32(fi.Offset), TypeID: id, } buf := make([]byte, FuncInfoSize) @@ -515,14 +518,13 @@ func (li *Line) String() string { return li.line } -// LineInfos contains a sorted list of line infos. -type LineInfos struct { - infos []lineInfo -} +// LineOffsets contains a sorted list of line infos. +type LineOffsets []LineOffset -type lineInfo struct { - line *Line - offset asm.RawInstructionOffset +// LineOffset represents a line info and its raw instruction offset. +type LineOffset struct { + Offset asm.RawInstructionOffset + Line *Line } // Constants for the format of bpfLineInfo.LineCol. @@ -541,7 +543,7 @@ type bpfLineInfo struct { } // LoadLineInfos parses BTF line info in kernel wire format. -func LoadLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (LineInfos, error) { +func LoadLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (LineOffsets, error) { lis, err := parseLineInfoRecords( reader, bo, @@ -550,57 +552,55 @@ func LoadLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec false, ) if err != nil { - return LineInfos{}, fmt.Errorf("parsing BTF line info: %w", err) + return LineOffsets{}, fmt.Errorf("parsing BTF line info: %w", err) } return newLineInfos(lis, spec.strings) } -func newLineInfo(li bpfLineInfo, strings *stringTable) (lineInfo, error) { +func newLineInfo(li bpfLineInfo, strings *stringTable) (LineOffset, error) { line, err := strings.Lookup(li.LineOff) if err != nil { - return lineInfo{}, fmt.Errorf("lookup of line: %w", err) + return LineOffset{}, fmt.Errorf("lookup of line: %w", err) } fileName, err := strings.Lookup(li.FileNameOff) if err != nil { - return lineInfo{}, fmt.Errorf("lookup of filename: %w", err) + return LineOffset{}, fmt.Errorf("lookup of filename: %w", err) } lineNumber := li.LineCol >> bpfLineShift lineColumn := li.LineCol & bpfColumnMax - return lineInfo{ + return LineOffset{ + asm.RawInstructionOffset(li.InsnOff), &Line{ fileName, line, lineNumber, lineColumn, }, - asm.RawInstructionOffset(li.InsnOff), }, nil } -func newLineInfos(blis []bpfLineInfo, strings *stringTable) (LineInfos, error) { - lis := LineInfos{ - infos: make([]lineInfo, 0, len(blis)), - } +func newLineInfos(blis []bpfLineInfo, strings *stringTable) (LineOffsets, error) { + lis := make([]LineOffset, 0, len(blis)) for _, bli := range blis { li, err := newLineInfo(bli, strings) if err != nil { - return LineInfos{}, fmt.Errorf("offset %d: %w", bli.InsnOff, err) + return LineOffsets{}, fmt.Errorf("offset %d: %w", bli.InsnOff, err) } - lis.infos = append(lis.infos, li) + lis = append(lis, li) } - sort.Slice(lis.infos, func(i, j int) bool { - return lis.infos[i].offset <= lis.infos[j].offset + sort.Slice(lis, func(i, j int) bool { + return lis[i].Offset <= lis[j].Offset }) return lis, nil } // marshal writes the binary representation of the LineInfo to w. -func (li *lineInfo) marshal(w *bytes.Buffer, b *Builder) error { - line := li.line +func (li *LineOffset) marshal(w *bytes.Buffer, b *Builder) error { + line := li.Line if line.lineNumber > bpfLineMax { return fmt.Errorf("line %d exceeds %d", line.lineNumber, bpfLineMax) } @@ -620,7 +620,7 @@ func (li *lineInfo) marshal(w *bytes.Buffer, b *Builder) error { } bli := bpfLineInfo{ - uint32(li.offset), + uint32(li.Offset), fileNameOff, lineOff, (line.lineNumber << bpfLineShift) | line.lineColumn, @@ -799,7 +799,7 @@ func parseCORERelos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map return nil, err } - records, err := parseCOREReloRecords(r, bo, recordSize, infoHeader.NumInfo) + records, err := parseCOREReloRecords(r, bo, infoHeader.NumInfo) if err != nil { return nil, fmt.Errorf("section %v: %w", secName, err) } @@ -811,7 +811,7 @@ func parseCORERelos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map // parseCOREReloRecords parses a stream of CO-RE relocation entries into a // coreRelos. These records appear after a btf_ext_info_sec header in the // core_relos sub-section of .BTF.ext. -func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfCORERelo, error) { +func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordNum uint32) ([]bpfCORERelo, error) { var out []bpfCORERelo var relo bpfCORERelo diff --git a/vendor/github.com/cilium/ebpf/btf/feature.go b/vendor/github.com/cilium/ebpf/btf/feature.go index 6feb08dfbb0..e71c707fe4d 100644 --- a/vendor/github.com/cilium/ebpf/btf/feature.go +++ b/vendor/github.com/cilium/ebpf/btf/feature.go @@ -11,19 +11,19 @@ import ( // haveBTF attempts to load a BTF blob containing an Int. It should pass on any // kernel that supports BPF_BTF_LOAD. -var haveBTF = internal.NewFeatureTest("BTF", "4.18", func() error { +var haveBTF = internal.NewFeatureTest("BTF", func() error { // 0-length anonymous integer err := probeBTF(&Int{}) if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { return internal.ErrNotSupported } return err -}) +}, "4.18") // haveMapBTF attempts to load a minimal BTF blob containing a Var. It is // used as a proxy for .bss, .data and .rodata map support, which generally // come with a Var and Datasec. These were introduced in Linux 5.2. -var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)", "5.2", func() error { +var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)", func() error { if err := haveBTF(); err != nil { return err } @@ -40,12 +40,12 @@ var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)", "5.2", func() return internal.ErrNotSupported } return err -}) +}, "5.2") // haveProgBTF attempts to load a BTF blob containing a Func and FuncProto. It // is used as a proxy for ext_info (func_info) support, which depends on // Func(Proto) by definition. -var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)", "5.0", func() error { +var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)", func() error { if err := haveBTF(); err != nil { return err } @@ -60,9 +60,9 @@ var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)", "5.0", return internal.ErrNotSupported } return err -}) +}, "5.0") -var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", "5.6", func() error { +var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", func() error { if err := haveProgBTF(); err != nil { return err } @@ -78,9 +78,44 @@ var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", "5.6", func() return internal.ErrNotSupported } return err -}) +}, "5.6") -var haveEnum64 = internal.NewFeatureTest("ENUM64", "6.0", func() error { +var haveDeclTags = internal.NewFeatureTest("BTF decl tags", func() error { + if err := haveBTF(); err != nil { + return err + } + + t := &Typedef{ + Name: "a", + Type: &Int{}, + Tags: []string{"a"}, + } + + err := probeBTF(t) + if errors.Is(err, unix.EINVAL) { + return internal.ErrNotSupported + } + return err +}, "5.16") + +var haveTypeTags = internal.NewFeatureTest("BTF type tags", func() error { + if err := haveBTF(); err != nil { + return err + } + + t := &TypeTag{ + Type: &Int{}, + Value: "a", + } + + err := probeBTF(t) + if errors.Is(err, unix.EINVAL) { + return internal.ErrNotSupported + } + return err +}, "5.17") + +var haveEnum64 = internal.NewFeatureTest("ENUM64", func() error { if err := haveBTF(); err != nil { return err } @@ -97,7 +132,7 @@ var haveEnum64 = internal.NewFeatureTest("ENUM64", "6.0", func() error { return internal.ErrNotSupported } return err -}) +}, "6.0") func probeBTF(typ Type) error { b, err := NewBuilder([]Type{typ}) 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/kernel.go b/vendor/github.com/cilium/ebpf/btf/kernel.go index 8584ebcb932..1a9321f054c 100644 --- a/vendor/github.com/cilium/ebpf/btf/kernel.go +++ b/vendor/github.com/cilium/ebpf/btf/kernel.go @@ -8,7 +8,7 @@ import ( "sync" "github.com/cilium/ebpf/internal" - "github.com/cilium/ebpf/internal/kallsyms" + "github.com/cilium/ebpf/internal/linux" ) var kernelBTF = struct { @@ -21,8 +21,6 @@ var kernelBTF = struct { // FlushKernelSpec removes any cached kernel type information. func FlushKernelSpec() { - kallsyms.FlushKernelModuleCache() - kernelBTF.Lock() defer kernelBTF.Unlock() @@ -130,7 +128,7 @@ func loadKernelModuleSpec(module string, base *Spec) (*Spec, error) { // findVMLinux scans multiple well-known paths for vmlinux kernel images. func findVMLinux() (*os.File, error) { - release, err := internal.KernelRelease() + release, err := linux.KernelRelease() if err != nil { return nil, err } diff --git a/vendor/github.com/cilium/ebpf/btf/marshal.go b/vendor/github.com/cilium/ebpf/btf/marshal.go index f14cfa6e973..d7204e62476 100644 --- a/vendor/github.com/cilium/ebpf/btf/marshal.go +++ b/vendor/github.com/cilium/ebpf/btf/marshal.go @@ -18,6 +18,10 @@ type MarshalOptions struct { Order binary.ByteOrder // Remove function linkage information for compatibility with <5.6 kernels. StripFuncLinkage bool + // Replace decl tags with a placeholder for compatibility with <5.16 kernels. + ReplaceDeclTags bool + // Replace TypeTags with a placeholder for compatibility with <5.17 kernels. + ReplaceTypeTags bool // Replace Enum64 with a placeholder for compatibility with <6.0 kernels. ReplaceEnum64 bool // Prevent the "No type found" error when loading BTF without any types. @@ -29,6 +33,8 @@ func KernelMarshalOptions() *MarshalOptions { return &MarshalOptions{ Order: internal.NativeEndian, StripFuncLinkage: haveFuncLinkage() != nil, + ReplaceDeclTags: haveDeclTags() != nil, + ReplaceTypeTags: haveTypeTags() != nil, ReplaceEnum64: haveEnum64() != nil, PreventNoTypeFound: true, // All current kernels require this. } @@ -318,15 +324,7 @@ func (e *encoder) deflateType(typ Type) (err error) { return errors.New("Void is implicit in BTF wire format") case *Int: - raw.SetKind(kindInt) - raw.SetSize(v.Size) - - var bi btfInt - bi.SetEncoding(v.Encoding) - // We need to set bits in addition to size, since btf_type_int_is_regular - // otherwise flags this as a bitfield. - bi.SetBits(byte(v.Size) * 8) - raw.data = bi + e.deflateInt(&raw, v) case *Pointer: raw.SetKind(kindPointer) @@ -368,8 +366,7 @@ func (e *encoder) deflateType(typ Type) (err error) { raw.SetType(e.id(v.Type)) case *Const: - raw.SetKind(kindConst) - raw.SetType(e.id(v.Type)) + e.deflateConst(&raw, v) case *Restrict: raw.SetKind(kindRestrict) @@ -404,15 +401,10 @@ func (e *encoder) deflateType(typ Type) (err error) { raw.SetSize(v.Size) case *declTag: - raw.SetKind(kindDeclTag) - raw.SetType(e.id(v.Type)) - raw.data = &btfDeclTag{uint32(v.Index)} - raw.NameOff, err = e.strings.Add(v.Value) + err = e.deflateDeclTag(&raw, v) - case *typeTag: - raw.SetKind(kindTypeTag) - raw.SetType(e.id(v.Type)) - raw.NameOff, err = e.strings.Add(v.Value) + case *TypeTag: + err = e.deflateTypeTag(&raw, v) default: return fmt.Errorf("don't know how to deflate %T", v) @@ -425,6 +417,57 @@ func (e *encoder) deflateType(typ Type) (err error) { return raw.Marshal(e.buf, e.Order) } +func (e *encoder) deflateInt(raw *rawType, i *Int) { + raw.SetKind(kindInt) + raw.SetSize(i.Size) + + var bi btfInt + bi.SetEncoding(i.Encoding) + // We need to set bits in addition to size, since btf_type_int_is_regular + // otherwise flags this as a bitfield. + bi.SetBits(byte(i.Size) * 8) + raw.data = bi +} + +func (e *encoder) deflateDeclTag(raw *rawType, tag *declTag) (err error) { + // Replace a decl tag with an integer for compatibility with <5.16 kernels, + // following libbpf behaviour. + if e.ReplaceDeclTags { + typ := &Int{"decl_tag_placeholder", 1, Unsigned} + e.deflateInt(raw, typ) + + // Add the placeholder type name to the string table. The encoder added the + // original type name before this call. + raw.NameOff, err = e.strings.Add(typ.TypeName()) + return + } + + raw.SetKind(kindDeclTag) + raw.SetType(e.id(tag.Type)) + raw.data = &btfDeclTag{uint32(tag.Index)} + raw.NameOff, err = e.strings.Add(tag.Value) + return +} + +func (e *encoder) deflateConst(raw *rawType, c *Const) { + raw.SetKind(kindConst) + raw.SetType(e.id(c.Type)) +} + +func (e *encoder) deflateTypeTag(raw *rawType, tag *TypeTag) (err error) { + // Replace a type tag with a const qualifier for compatibility with <5.17 + // kernels, following libbpf behaviour. + if e.ReplaceTypeTags { + e.deflateConst(raw, &Const{tag.Type}) + return + } + + raw.SetKind(kindTypeTag) + raw.SetType(e.id(tag.Type)) + raw.NameOff, err = e.strings.Add(tag.Value) + return +} + func (e *encoder) deflateUnion(raw *rawType, union *Union) (err error) { raw.SetKind(kindUnion) raw.SetSize(union.Size) @@ -521,7 +564,7 @@ func (e *encoder) deflateEnum64(raw *rawType, enum *Enum) (err error) { }) } - return e.deflateUnion(raw, &Union{enum.Name, enum.Size, members}) + return e.deflateUnion(raw, &Union{enum.Name, enum.Size, members, nil}) } raw.SetKind(kindEnum64) diff --git a/vendor/github.com/cilium/ebpf/btf/traversal.go b/vendor/github.com/cilium/ebpf/btf/traversal.go index c39dc66e46c..13647d931ff 100644 --- a/vendor/github.com/cilium/ebpf/btf/traversal.go +++ b/vendor/github.com/cilium/ebpf/btf/traversal.go @@ -40,9 +40,12 @@ func children(typ Type, yield func(child *Type) bool) bool { // Explicitly type switch on the most common types to allow the inliner to // do its work. This avoids allocating intermediate slices from walk() on // the heap. + var tags []string switch v := typ.(type) { - case *Void, *Int, *Enum, *Fwd, *Float: + case *Void, *Int, *Enum, *Fwd, *Float, *declTag: // No children to traverse. + // declTags is declared as a leaf type since it's parsed into .Tags fields of other types + // during unmarshaling. case *Pointer: if !yield(&v.Target) { return false @@ -59,17 +62,32 @@ func children(typ Type, yield func(child *Type) bool) bool { if !yield(&v.Members[i].Type) { return false } + for _, t := range v.Members[i].Tags { + var tag Type = &declTag{v, t, i} + if !yield(&tag) { + return false + } + } } + tags = v.Tags case *Union: for i := range v.Members { if !yield(&v.Members[i].Type) { return false } + for _, t := range v.Members[i].Tags { + var tag Type = &declTag{v, t, i} + if !yield(&tag) { + return false + } + } } + tags = v.Tags case *Typedef: if !yield(&v.Type) { return false } + tags = v.Tags case *Volatile: if !yield(&v.Type) { return false @@ -86,6 +104,20 @@ func children(typ Type, yield func(child *Type) bool) bool { if !yield(&v.Type) { return false } + if fp, ok := v.Type.(*FuncProto); ok { + for i := range fp.Params { + if len(v.ParamTags) <= i { + continue + } + for _, t := range v.ParamTags[i] { + var tag Type = &declTag{v, t, i} + if !yield(&tag) { + return false + } + } + } + } + tags = v.Tags case *FuncProto: if !yield(&v.Return) { return false @@ -99,17 +131,14 @@ func children(typ Type, yield func(child *Type) bool) bool { if !yield(&v.Type) { return false } + tags = v.Tags case *Datasec: for i := range v.Vars { if !yield(&v.Vars[i].Type) { return false } } - case *declTag: - if !yield(&v.Type) { - return false - } - case *typeTag: + case *TypeTag: if !yield(&v.Type) { return false } @@ -119,5 +148,12 @@ func children(typ Type, yield func(child *Type) bool) bool { panic(fmt.Sprintf("don't know how to walk Type %T", v)) } + for _, t := range tags { + var tag Type = &declTag{typ, t, -1} + if !yield(&tag) { + return false + } + } + return true } diff --git a/vendor/github.com/cilium/ebpf/btf/types.go b/vendor/github.com/cilium/ebpf/btf/types.go index a3397460b9d..dbcdf9dd7aa 100644 --- a/vendor/github.com/cilium/ebpf/btf/types.go +++ b/vendor/github.com/cilium/ebpf/btf/types.go @@ -67,7 +67,7 @@ var ( _ Type = (*Datasec)(nil) _ Type = (*Float)(nil) _ Type = (*declTag)(nil) - _ Type = (*typeTag)(nil) + _ Type = (*TypeTag)(nil) _ Type = (*cycle)(nil) ) @@ -169,6 +169,7 @@ type Struct struct { // The size of the struct including padding, in bytes Size uint32 Members []Member + Tags []string } func (s *Struct) Format(fs fmt.State, verb rune) { @@ -182,6 +183,7 @@ func (s *Struct) size() uint32 { return s.Size } func (s *Struct) copy() Type { cpy := *s cpy.Members = copyMembers(s.Members) + cpy.Tags = copyTags(cpy.Tags) return &cpy } @@ -195,6 +197,7 @@ type Union struct { // The size of the union including padding, in bytes. Size uint32 Members []Member + Tags []string } func (u *Union) Format(fs fmt.State, verb rune) { @@ -208,6 +211,7 @@ func (u *Union) size() uint32 { return u.Size } func (u *Union) copy() Type { cpy := *u cpy.Members = copyMembers(u.Members) + cpy.Tags = copyTags(cpy.Tags) return &cpy } @@ -218,6 +222,18 @@ func (u *Union) members() []Member { func copyMembers(orig []Member) []Member { cpy := make([]Member, len(orig)) copy(cpy, orig) + for i, member := range cpy { + cpy[i].Tags = copyTags(member.Tags) + } + return cpy +} + +func copyTags(orig []string) []string { + if orig == nil { // preserve nil vs zero-len slice distinction + return nil + } + cpy := make([]string, len(orig)) + copy(cpy, orig) return cpy } @@ -247,6 +263,7 @@ type Member struct { Type Type Offset Bits BitfieldSize Bits + Tags []string } // Enum lists possible values. @@ -334,6 +351,7 @@ func (f *Fwd) matches(typ Type) bool { type Typedef struct { Name string Type Type + Tags []string } func (td *Typedef) Format(fs fmt.State, verb rune) { @@ -344,6 +362,7 @@ func (td *Typedef) TypeName() string { return td.Name } func (td *Typedef) copy() Type { cpy := *td + cpy.Tags = copyTags(td.Tags) return &cpy } @@ -403,6 +422,12 @@ type Func struct { Name string Type Type Linkage FuncLinkage + Tags []string + // ParamTags holds a list of tags for each parameter of the FuncProto to which `Type` points. + // If no tags are present for any param, the outer slice will be nil/len(ParamTags)==0. + // If at least 1 param has a tag, the outer slice will have the same length as the number of params. + // The inner slice contains the tags and may be nil/len(ParamTags[i])==0 if no tags are present for that param. + ParamTags [][]string } func FuncMetadata(ins *asm.Instruction) *Func { @@ -424,6 +449,14 @@ func (f *Func) TypeName() string { return f.Name } func (f *Func) copy() Type { cpy := *f + cpy.Tags = copyTags(f.Tags) + if f.ParamTags != nil { // preserve nil vs zero-len slice distinction + ptCopy := make([][]string, len(f.ParamTags)) + for i, tags := range f.ParamTags { + ptCopy[i] = copyTags(tags) + } + cpy.ParamTags = ptCopy + } return &cpy } @@ -456,6 +489,7 @@ type Var struct { Name string Type Type Linkage VarLinkage + Tags []string } func (v *Var) Format(fs fmt.State, verb rune) { @@ -466,6 +500,7 @@ func (v *Var) TypeName() string { return v.Name } func (v *Var) copy() Type { cpy := *v + cpy.Tags = copyTags(v.Tags) return &cpy } @@ -540,19 +575,25 @@ func (dt *declTag) copy() Type { return &cpy } -// typeTag associates metadata with a type. -type typeTag struct { +// TypeTag associates metadata with a pointer type. Tag types act as a custom +// modifier(const, restrict, volatile) for the target type. Unlike declTags, +// TypeTags are ordered so the order in which they are added matters. +// +// One of their uses is to mark pointers as `__kptr` meaning a pointer points +// to kernel memory. Adding a `__kptr` to pointers in map values allows you +// to store pointers to kernel memory in maps. +type TypeTag struct { Type Type Value string } -func (tt *typeTag) Format(fs fmt.State, verb rune) { +func (tt *TypeTag) Format(fs fmt.State, verb rune) { formatType(fs, verb, tt, "type=", tt.Type, "value=", tt.Value) } -func (tt *typeTag) TypeName() string { return "" } -func (tt *typeTag) qualify() Type { return tt.Type } -func (tt *typeTag) copy() Type { +func (tt *TypeTag) TypeName() string { return "" } +func (tt *TypeTag) qualify() Type { return tt.Type } +func (tt *TypeTag) copy() Type { cpy := *tt return &cpy } @@ -591,7 +632,7 @@ var ( _ qualifier = (*Const)(nil) _ qualifier = (*Restrict)(nil) _ qualifier = (*Volatile)(nil) - _ qualifier = (*typeTag)(nil) + _ qualifier = (*TypeTag)(nil) ) var errUnsizedType = errors.New("type is unsized") @@ -918,7 +959,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt if err != nil { return nil, fmt.Errorf("struct %s (id %d): %w", name, id, err) } - typ = &Struct{name, header.Size(), members} + typ = &Struct{name, header.Size(), members, nil} case kindUnion: vlen := header.Vlen() @@ -935,7 +976,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt if err != nil { return nil, fmt.Errorf("union %s (id %d): %w", name, id, err) } - typ = &Union{name, header.Size(), members} + typ = &Union{name, header.Size(), members, nil} case kindEnum: vlen := header.Vlen() @@ -968,7 +1009,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt typ = &Fwd{name, header.FwdKind()} case kindTypedef: - typedef := &Typedef{name, nil} + typedef := &Typedef{name, nil, nil} fixup(header.Type(), &typedef.Type) typ = typedef @@ -988,7 +1029,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt typ = restrict case kindFunc: - fn := &Func{name, nil, header.Linkage()} + fn := &Func{name, nil, header.Linkage(), nil, nil} fixup(header.Type(), &fn.Type) typ = fn @@ -1030,7 +1071,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt return nil, fmt.Errorf("can't read btfVariable, id: %d: %w", id, err) } - v := &Var{name, nil, VarLinkage(bVariable.Linkage)} + v := &Var{name, nil, VarLinkage(bVariable.Linkage), nil} fixup(header.Type(), &v.Type) typ = v @@ -1081,7 +1122,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt declTags = append(declTags, dt) case kindTypeTag: - tt := &typeTag{nil, name} + tt := &TypeTag{nil, name} fixup(header.Type(), &tt.Type) typ = tt @@ -1142,26 +1183,69 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt for _, dt := range declTags { switch t := dt.Type.(type) { - case *Var, *Typedef: + case *Var: + if dt.Index != -1 { + return nil, fmt.Errorf("type %s: component idx %d is not -1", dt, dt.Index) + } + t.Tags = append(t.Tags, dt.Value) + + case *Typedef: if dt.Index != -1 { - return nil, fmt.Errorf("type %s: index %d is not -1", dt, dt.Index) + return nil, fmt.Errorf("type %s: component idx %d is not -1", dt, dt.Index) } + t.Tags = append(t.Tags, dt.Value) case composite: - if dt.Index >= len(t.members()) { - return nil, fmt.Errorf("type %s: index %d exceeds members of %s", dt, dt.Index, t) + if dt.Index >= 0 { + members := t.members() + if dt.Index >= len(members) { + return nil, fmt.Errorf("type %s: component idx %d exceeds members of %s", dt, dt.Index, t) + } + + members[dt.Index].Tags = append(members[dt.Index].Tags, dt.Value) + continue + } + + if dt.Index == -1 { + switch t2 := t.(type) { + case *Struct: + t2.Tags = append(t2.Tags, dt.Value) + case *Union: + t2.Tags = append(t2.Tags, dt.Value) + } + + continue } + return nil, fmt.Errorf("type %s: decl tag for type %s has invalid component idx", dt, t) + case *Func: fp, ok := t.Type.(*FuncProto) if !ok { return nil, fmt.Errorf("type %s: %s is not a FuncProto", dt, t.Type) } - if dt.Index >= len(fp.Params) { - return nil, fmt.Errorf("type %s: index %d exceeds params of %s", dt, dt.Index, t) + // Ensure the number of argument tag lists equals the number of arguments + if len(t.ParamTags) == 0 { + t.ParamTags = make([][]string, len(fp.Params)) + } + + if dt.Index >= 0 { + if dt.Index >= len(fp.Params) { + return nil, fmt.Errorf("type %s: component idx %d exceeds params of %s", dt, dt.Index, t) + } + + t.ParamTags[dt.Index] = append(t.ParamTags[dt.Index], dt.Value) + continue } + if dt.Index == -1 { + t.Tags = append(t.Tags, dt.Value) + continue + } + + return nil, fmt.Errorf("type %s: decl tag for type %s has invalid component idx", dt, t) + default: return nil, fmt.Errorf("type %s: decl tag for type %s is not supported", dt, t) } @@ -1207,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/btf/workarounds.go b/vendor/github.com/cilium/ebpf/btf/workarounds.go index 12a89b87eed..eb09047fb30 100644 --- a/vendor/github.com/cilium/ebpf/btf/workarounds.go +++ b/vendor/github.com/cilium/ebpf/btf/workarounds.go @@ -12,7 +12,7 @@ func datasecResolveWorkaround(b *Builder, ds *Datasec) error { } switch v.Type.(type) { - case *Typedef, *Volatile, *Const, *Restrict, *typeTag: + case *Typedef, *Volatile, *Const, *Restrict, *TypeTag: // NB: We must never call Add on a Datasec, otherwise we risk // infinite recursion. _, err := b.Add(v.Type) diff --git a/vendor/github.com/cilium/ebpf/collection.go b/vendor/github.com/cilium/ebpf/collection.go index b2cb214adce..1bda110a401 100644 --- a/vendor/github.com/cilium/ebpf/collection.go +++ b/vendor/github.com/cilium/ebpf/collection.go @@ -10,8 +10,10 @@ import ( "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/kallsyms" "github.com/cilium/ebpf/internal/kconfig" - "github.com/cilium/ebpf/internal/sysenc" + "github.com/cilium/ebpf/internal/linux" + "github.com/cilium/ebpf/internal/sys" ) // CollectionOptions control loading a collection into the kernel. @@ -38,6 +40,11 @@ type CollectionSpec struct { Maps map[string]*MapSpec Programs map[string]*ProgramSpec + // Variables refer to global variables declared in the ELF. They can be read + // and modified freely before loading the Collection. Modifying them after + // loading has no effect on a running eBPF program. + Variables map[string]*VariableSpec + // Types holds type information about Maps and Programs. // Modifications to Types are currently undefined behaviour. Types *btf.Spec @@ -56,6 +63,7 @@ func (cs *CollectionSpec) Copy() *CollectionSpec { cpy := CollectionSpec{ Maps: make(map[string]*MapSpec, len(cs.Maps)), Programs: make(map[string]*ProgramSpec, len(cs.Programs)), + Variables: make(map[string]*VariableSpec, len(cs.Variables)), ByteOrder: cs.ByteOrder, Types: cs.Types.Copy(), } @@ -68,6 +76,10 @@ func (cs *CollectionSpec) Copy() *CollectionSpec { cpy.Programs[name] = spec.Copy() } + for name, spec := range cs.Variables { + cpy.Variables[name] = spec.copy(&cpy) + } + return &cpy } @@ -134,65 +146,24 @@ func (m *MissingConstantsError) Error() string { // From Linux 5.5 the verifier will use constants to eliminate dead code. // // Returns an error wrapping [MissingConstantsError] if a constant doesn't exist. +// +// Deprecated: Use [CollectionSpec.Variables] to interact with constants instead. +// RewriteConstants is now a wrapper around the VariableSpec API. func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error { - replaced := make(map[string]bool) - - for name, spec := range cs.Maps { - if !strings.HasPrefix(name, ".rodata") { + var missing []string + for n, c := range consts { + v, ok := cs.Variables[n] + if !ok { + missing = append(missing, n) continue } - b, ds, err := spec.dataSection() - if errors.Is(err, errMapNoBTFValue) { - // Data sections without a BTF Datasec are valid, but don't support - // constant replacements. - continue - } - if err != nil { - return fmt.Errorf("map %s: %w", name, err) + if !v.Constant() { + return fmt.Errorf("variable %s is not a constant", n) } - // MapSpec.Copy() performs a shallow copy. Fully copy the byte slice - // to avoid any changes affecting other copies of the MapSpec. - cpy := make([]byte, len(b)) - copy(cpy, b) - - for _, v := range ds.Vars { - vname := v.Type.TypeName() - replacement, ok := consts[vname] - if !ok { - continue - } - - if _, ok := v.Type.(*btf.Var); !ok { - return fmt.Errorf("section %s: unexpected type %T for variable %s", name, v.Type, vname) - } - - if replaced[vname] { - return fmt.Errorf("section %s: duplicate variable %s", name, vname) - } - - if int(v.Offset+v.Size) > len(cpy) { - return fmt.Errorf("section %s: offset %d(+%d) for variable %s is out of bounds", name, v.Offset, v.Size, vname) - } - - b, err := sysenc.Marshal(replacement, int(v.Size)) - if err != nil { - return fmt.Errorf("marshaling constant replacement %s: %w", vname, err) - } - - b.CopyTo(cpy[v.Offset : v.Offset+v.Size]) - - replaced[vname] = true - } - - spec.Contents[0] = MapKV{Key: uint32(0), Value: cpy} - } - - var missing []string - for c := range consts { - if !replaced[c] { - missing = append(missing, c) + if err := v.Set(c); err != nil { + return fmt.Errorf("rewriting constant %s: %w", n, err) } } @@ -210,25 +181,23 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error // if this sounds useful. // // 'to' must be a pointer to a struct. A field of the -// struct is updated with values from Programs or Maps if it -// has an `ebpf` tag and its type is *ProgramSpec or *MapSpec. +// struct is updated with values from Programs, Maps or Variables if it +// has an `ebpf` tag and its type is *ProgramSpec, *MapSpec or *VariableSpec. // The tag's value specifies the name of the program or map as // found in the CollectionSpec. // // struct { -// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` -// Bar *ebpf.MapSpec `ebpf:"bar_map"` +// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` +// Bar *ebpf.MapSpec `ebpf:"bar_map"` +// Var *ebpf.VariableSpec `ebpf:"some_var"` // Ignored int // } // // Returns an error if any of the eBPF objects can't be found, or -// if the same MapSpec or ProgramSpec is assigned multiple times. +// if the same Spec is assigned multiple times. func (cs *CollectionSpec) Assign(to interface{}) error { - // Assign() only supports assigning ProgramSpecs and MapSpecs, - // so doesn't load any resources into the kernel. getValue := func(typ reflect.Type, name string) (interface{}, error) { switch typ { - case reflect.TypeOf((*ProgramSpec)(nil)): if p := cs.Programs[name]; p != nil { return p, nil @@ -241,6 +210,12 @@ func (cs *CollectionSpec) Assign(to interface{}) error { } return nil, fmt.Errorf("missing map %q", name) + case reflect.TypeOf((*VariableSpec)(nil)): + if v := cs.Variables[name]; v != nil { + return v, nil + } + return nil, fmt.Errorf("missing variable %q", name) + default: return nil, fmt.Errorf("unsupported type %s", typ) } @@ -286,6 +261,7 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) // Support assigning Programs and Maps, lazy-loading the required objects. assignedMaps := make(map[string]bool) assignedProgs := make(map[string]bool) + assignedVars := make(map[string]bool) getValue := func(typ reflect.Type, name string) (interface{}, error) { switch typ { @@ -298,6 +274,10 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) assignedMaps[name] = true return loader.loadMap(name) + case reflect.TypeOf((*Variable)(nil)): + assignedVars[name] = true + return loader.loadVariable(name) + default: return nil, fmt.Errorf("unsupported type %s", typ) } @@ -338,15 +318,22 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) for p := range assignedProgs { delete(loader.programs, p) } + for p := range assignedVars { + delete(loader.vars, p) + } return nil } -// Collection is a collection of Programs and Maps associated -// with their symbols +// Collection is a collection of live BPF resources present in the kernel. type Collection struct { Programs map[string]*Program Maps map[string]*Map + + // Variables contains global variables used by the Collection's program(s). On + // kernels older than 5.5, most interactions with Variables return + // [ErrNotSupported]. + Variables map[string]*Variable } // NewCollection creates a Collection from the given spec, creating and @@ -387,19 +374,26 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Co } } + for varName := range spec.Variables { + if _, err := loader.loadVariable(varName); err != nil { + return nil, err + } + } + // Maps can contain Program and Map stubs, so populate them after // all Maps and Programs have been successfully loaded. if err := loader.populateDeferredMaps(); err != nil { return nil, err } - // Prevent loader.cleanup from closing maps and programs. - maps, progs := loader.maps, loader.programs - loader.maps, loader.programs = nil, nil + // Prevent loader.cleanup from closing maps, programs and vars. + maps, progs, vars := loader.maps, loader.programs, loader.vars + loader.maps, loader.programs, loader.vars = nil, nil, nil return &Collection{ progs, maps, + vars, }, nil } @@ -408,6 +402,7 @@ type collectionLoader struct { opts *CollectionOptions maps map[string]*Map programs map[string]*Program + vars map[string]*Variable } func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) { @@ -427,14 +422,58 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec } } + if err := populateKallsyms(coll.Programs); err != nil { + return nil, fmt.Errorf("populating kallsyms caches: %w", err) + } + return &collectionLoader{ coll, opts, make(map[string]*Map), make(map[string]*Program), + make(map[string]*Variable), }, nil } +// populateKallsyms populates kallsyms caches, making lookups cheaper later on +// during individual program loading. Since we have less context available +// at those stages, we batch the lookups here instead to avoid redundant work. +func populateKallsyms(progs map[string]*ProgramSpec) error { + // Look up associated kernel modules for all symbols referenced by + // ProgramSpec.AttachTo for program types that support attaching to kmods. + mods := make(map[string]string) + for _, p := range progs { + if p.AttachTo != "" && p.targetsKernelModule() { + mods[p.AttachTo] = "" + } + } + if len(mods) != 0 { + if err := kallsyms.AssignModules(mods); err != nil { + return fmt.Errorf("getting modules from kallsyms: %w", err) + } + } + + // Look up addresses of all kernel symbols referenced by all programs. + addrs := make(map[string]uint64) + for _, p := range progs { + iter := p.Instructions.Iterate() + for iter.Next() { + ins := iter.Ins + meta, _ := ins.Metadata.Get(ksymMetaKey{}).(*ksymMeta) + if meta != nil { + addrs[meta.Name] = 0 + } + } + } + if len(addrs) != 0 { + if err := kallsyms.AssignAddresses(addrs); err != nil { + return fmt.Errorf("getting addresses from kallsyms: %w", err) + } + } + + return nil +} + // close all resources left over in the collectionLoader. func (cl *collectionLoader) close() { for _, m := range cl.maps { @@ -466,6 +505,13 @@ func (cl *collectionLoader) loadMap(mapName string) (*Map, error) { return m, nil } + // Defer setting the mmapable flag on maps until load time. This avoids the + // MapSpec having different flags on some kernel versions. Also avoid running + // syscalls during ELF loading, so platforms like wasm can also parse an ELF. + if isDataSection(mapSpec.Name) && haveMmapableMaps() == nil { + mapSpec.Flags |= sys.BPF_F_MMAPABLE + } + m, err := newMapWithOptions(mapSpec, cl.opts.Maps) if err != nil { return nil, fmt.Errorf("map %s: %w", mapName, err) @@ -537,6 +583,58 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) { return prog, nil } +func (cl *collectionLoader) loadVariable(varName string) (*Variable, error) { + if v := cl.vars[varName]; v != nil { + return v, nil + } + + varSpec := cl.coll.Variables[varName] + if varSpec == nil { + return nil, fmt.Errorf("unknown variable %s", varName) + } + + // Get the key of the VariableSpec's MapSpec in the CollectionSpec. + var mapName string + for n, ms := range cl.coll.Maps { + if ms == varSpec.m { + mapName = n + break + } + } + if mapName == "" { + return nil, fmt.Errorf("variable %s: underlying MapSpec %s was removed from CollectionSpec", varName, varSpec.m.Name) + } + + m, err := cl.loadMap(mapName) + if err != nil { + return nil, fmt.Errorf("variable %s: %w", varName, err) + } + + // If the kernel is too old or the underlying map was created without + // BPF_F_MMAPABLE, [Map.Memory] will return ErrNotSupported. In this case, + // emit a Variable with a nil Memory. This keeps Collection{Spec}.Variables + // consistent across systems with different feature sets without breaking + // LoadAndAssign. + mm, err := m.Memory() + if err != nil && !errors.Is(err, ErrNotSupported) { + return nil, fmt.Errorf("variable %s: getting memory for map %s: %w", varName, mapName, err) + } + + v, err := newVariable( + varSpec.name, + varSpec.offset, + varSpec.size, + varSpec.t, + mm, + ) + if err != nil { + return nil, fmt.Errorf("variable %s: %w", varName, err) + } + + cl.vars[varName] = v + return v, nil +} + // populateDeferredMaps iterates maps holding programs or other maps and loads // any dependencies. Populates all maps in cl and freezes them if specified. func (cl *collectionLoader) populateDeferredMaps() error { @@ -603,6 +701,7 @@ func resolveKconfig(m *MapSpec) error { type configInfo struct { offset uint32 + size uint32 typ btf.Type } @@ -619,7 +718,7 @@ func resolveKconfig(m *MapSpec) error { return fmt.Errorf("variable %s must be a 32 bits integer, got %s", n, v.Type) } - kv, err := internal.KernelVersion() + kv, err := linux.KernelVersion() if err != nil { return fmt.Errorf("getting kernel version: %w", err) } @@ -644,6 +743,7 @@ func resolveKconfig(m *MapSpec) error { default: // Catch CONFIG_*. configs[n] = configInfo{ offset: vsi.Offset, + size: vsi.Size, typ: v.Type, } } @@ -651,7 +751,7 @@ func resolveKconfig(m *MapSpec) error { // We only parse kconfig file if a CONFIG_* variable was found. if len(configs) > 0 { - f, err := kconfig.Find() + f, err := linux.FindKConfig() if err != nil { return fmt.Errorf("cannot find a kconfig file: %w", err) } @@ -670,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) } @@ -723,6 +823,7 @@ func LoadCollection(file string) (*Collection, error) { func (coll *Collection) Assign(to interface{}) error { assignedMaps := make(map[string]bool) assignedProgs := make(map[string]bool) + assignedVars := make(map[string]bool) // Assign() only transfers already-loaded Maps and Programs. No extra // loading is done. @@ -743,6 +844,13 @@ func (coll *Collection) Assign(to interface{}) error { } return nil, fmt.Errorf("missing map %q", name) + case reflect.TypeOf((*Variable)(nil)): + if v := coll.Variables[name]; v != nil { + assignedVars[name] = true + return v, nil + } + return nil, fmt.Errorf("missing variable %q", name) + default: return nil, fmt.Errorf("unsupported type %s", typ) } @@ -759,6 +867,9 @@ func (coll *Collection) Assign(to interface{}) error { for m := range assignedMaps { delete(coll.Maps, m) } + for s := range assignedVars { + delete(coll.Variables, s) + } return nil } diff --git a/vendor/github.com/cilium/ebpf/elf_reader.go b/vendor/github.com/cilium/ebpf/elf_reader.go index 620037d80a8..9e8dbc7ae5a 100644 --- a/vendor/github.com/cilium/ebpf/elf_reader.go +++ b/vendor/github.com/cilium/ebpf/elf_reader.go @@ -16,7 +16,6 @@ import ( "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" - "github.com/cilium/ebpf/internal/unix" ) type kconfigMetaKey struct{} @@ -33,6 +32,13 @@ type kfuncMeta struct { Func *btf.Func } +type ksymMetaKey struct{} + +type ksymMeta struct { + Binding elf.SymBind + Name string +} + // elfCode is a convenience to reduce the amount of arguments that have to // be passed around explicitly. You should treat its contents as immutable. type elfCode struct { @@ -43,7 +49,9 @@ type elfCode struct { btf *btf.Spec extInfo *btf.ExtInfos maps map[string]*MapSpec + vars map[string]*VariableSpec kfuncs map[string]*btf.Func + ksyms map[string]struct{} kconfig *MapSpec } @@ -71,7 +79,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { // Checks if the ELF file is for BPF data. // Old LLVM versions set e_machine to EM_NONE. - if f.File.Machine != unix.EM_NONE && f.File.Machine != elf.EM_BPF { + if f.File.Machine != elf.EM_NONE && f.File.Machine != elf.EM_BPF { return nil, fmt.Errorf("unexpected machine type for BPF ELF: %s", f.File.Machine) } @@ -101,7 +109,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { sections[idx] = newElfSection(sec, mapSection) case sec.Name == ".maps": sections[idx] = newElfSection(sec, btfMapSection) - case sec.Name == ".bss" || sec.Name == ".data" || strings.HasPrefix(sec.Name, ".rodata"): + case isDataSection(sec.Name): sections[idx] = newElfSection(sec, dataSection) case sec.Type == elf.SHT_REL: // Store relocations under the section index of the target @@ -134,7 +142,9 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { btf: btfSpec, extInfo: btfExtInfo, maps: make(map[string]*MapSpec), + vars: make(map[string]*VariableSpec), kfuncs: make(map[string]*btf.Func), + ksyms: make(map[string]struct{}), } symbols, err := f.Symbols() @@ -174,7 +184,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { return nil, fmt.Errorf("load programs: %w", err) } - return &CollectionSpec{ec.maps, progs, btfSpec, ec.ByteOrder}, nil + return &CollectionSpec{ec.maps, progs, ec.vars, btfSpec, ec.ByteOrder}, nil } func loadLicense(sec *elf.Section) (string, error) { @@ -201,6 +211,18 @@ func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) { return version, nil } +func isDataSection(name string) bool { + return name == ".bss" || strings.HasPrefix(name, ".data") || strings.HasPrefix(name, ".rodata") +} + +func isConstantDataSection(name string) bool { + return strings.HasPrefix(name, ".rodata") +} + +func isKconfigSection(name string) bool { + return name == ".kconfig" +} + type elfSectionKind int const ( @@ -506,7 +528,7 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err case elf.STT_OBJECT: // LLVM 9 emits OBJECT-LOCAL symbols for anonymous constants. - if bind != elf.STB_GLOBAL && bind != elf.STB_LOCAL { + if bind != elf.STB_GLOBAL && bind != elf.STB_LOCAL && bind != elf.STB_WEAK { return fmt.Errorf("direct load: %s: %w: %s", name, errUnsupportedBinding, bind) } @@ -614,6 +636,8 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err } kf := ec.kfuncs[name] + _, ks := ec.ksyms[name] + switch { // If a Call / DWordLoad instruction is found and the datasec has a btf.Func with a Name // that matches the symbol name we mark the instruction as a referencing a kfunc. @@ -634,6 +658,15 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err ins.Constant = 0 + case ks && ins.OpCode.IsDWordLoad(): + if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK { + return fmt.Errorf("asm relocation: %s: %w: %s", name, errUnsupportedBinding, bind) + } + ins.Metadata.Set(ksymMetaKey{}, &ksymMeta{ + Binding: bind, + Name: name, + }) + // If no kconfig map is found, this must be a symbol reference from inline // asm (see testdata/loader.c:asm_relocation()) or a call to a forward // function declaration (see testdata/fwd_decl.c). Don't interfere, These @@ -980,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), @@ -1092,12 +1132,21 @@ func (ec *elfCode) loadDataSections() error { continue } - if sec.references == 0 { - // Prune data sections which are not referenced by any - // instructions. + // If a section has no references, it will be freed as soon as the + // Collection closes, so creating and populating it is wasteful. If it has + // no symbols, it is likely an ephemeral section used during compilation + // that wasn't sanitized by the bpf linker. (like .rodata.str1.1) + // + // No symbols means no VariableSpecs can be generated from it, making it + // pointless to emit a data section for. + if sec.references == 0 && len(sec.symbols) == 0 { continue } + if sec.Size > math.MaxUint32 { + return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name) + } + mapSpec := &MapSpec{ Name: SanitizeName(sec.Name, -1), Type: Array, @@ -1106,6 +1155,10 @@ func (ec *elfCode) loadDataSections() error { MaxEntries: 1, } + if isConstantDataSection(sec.Name) { + mapSpec.Flags = sys.BPF_F_RDONLY_PROG + } + switch sec.Type { // Only open the section if we know there's actual data to be read. case elf.SHT_PROGBITS: @@ -1113,20 +1166,56 @@ func (ec *elfCode) loadDataSections() error { if err != nil { return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err) } - - if uint64(len(data)) > math.MaxUint32 { - return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name) - } mapSpec.Contents = []MapKV{{uint32(0), data}} case elf.SHT_NOBITS: - // NOBITS sections like .bss contain only zeroes, and since data sections - // are Arrays, the kernel already preallocates them. Skip reading zeroes - // from the ELF. + // NOBITS sections like .bss contain only zeroes and are not allocated in + // the ELF. Since data sections are Arrays, the kernel can preallocate + // them. Don't attempt reading zeroes from the ELF, instead allocate the + // zeroed memory to support getting and setting VariableSpecs for sections + // like .bss. + mapSpec.Contents = []MapKV{{uint32(0), make([]byte, sec.Size)}} + default: return fmt.Errorf("data section %s: unknown section type %s", sec.Name, sec.Type) } + for off, sym := range sec.symbols { + // Skip symbols marked with the 'hidden' attribute. + if elf.ST_VISIBILITY(sym.Other) == elf.STV_HIDDEN || + elf.ST_VISIBILITY(sym.Other) == elf.STV_INTERNAL { + continue + } + + // Only accept symbols with global or weak bindings. The common + // alternative is STB_LOCAL, which are either function-scoped or declared + // 'static'. + if elf.ST_BIND(sym.Info) != elf.STB_GLOBAL && + elf.ST_BIND(sym.Info) != elf.STB_WEAK { + continue + } + + if ec.vars[sym.Name] != nil { + return fmt.Errorf("data section %s: duplicate variable %s", sec.Name, sym.Name) + } + + // Skip symbols starting with a dot, they are compiler-internal symbols + // emitted by clang 11 and earlier and are not cleaned up by the bpf + // compiler backend (e.g. symbols named .Lconstinit.1 in sections like + // .rodata.cst32). Variables in C cannot start with a dot, so filter these + // out. + if strings.HasPrefix(sym.Name, ".") { + continue + } + + ec.vars[sym.Name] = &VariableSpec{ + name: sym.Name, + offset: off, + size: sym.Size, + m: mapSpec, + } + } + // It is possible for a data section to exist without a corresponding BTF Datasec // if it only contains anonymous values like macro-defined arrays. if ec.btf != nil { @@ -1135,12 +1224,38 @@ func (ec *elfCode) loadDataSections() error { // Assign the spec's key and BTF only if the Datasec lookup was successful. mapSpec.Key = &btf.Void{} mapSpec.Value = ds - } - } - if strings.HasPrefix(sec.Name, ".rodata") { - mapSpec.Flags = unix.BPF_F_RDONLY_PROG - mapSpec.Freeze = true + // Populate VariableSpecs with type information, if available. + for _, v := range ds.Vars { + name := v.Type.TypeName() + if name == "" { + return fmt.Errorf("data section %s: anonymous variable %v", sec.Name, v) + } + + vt, ok := v.Type.(*btf.Var) + if !ok { + return fmt.Errorf("data section %s: unexpected type %T for variable %s", sec.Name, v.Type, name) + } + + ev := ec.vars[name] + if ev == nil { + // Hidden symbols appear in the BTF Datasec but don't receive a VariableSpec. + continue + } + + if uint64(v.Offset) != ev.offset { + return fmt.Errorf("data section %s: variable %s datasec offset (%d) doesn't match ELF symbol offset (%d)", sec.Name, name, v.Offset, ev.offset) + } + + if uint64(v.Size) != ev.size { + return fmt.Errorf("data section %s: variable %s size in datasec (%d) doesn't match ELF symbol size (%d)", sec.Name, name, v.Size, ev.size) + } + + // Decouple the Var in the VariableSpec from the underlying DataSec in + // the MapSpec to avoid modifications from affecting map loads later on. + ev.t = btf.Copy(vt).(*btf.Var) + } + } } ec.maps[sec.Name] = mapSpec @@ -1175,8 +1290,7 @@ func (ec *elfCode) loadKconfigSection() error { KeySize: uint32(4), ValueSize: ds.Size, MaxEntries: 1, - Flags: unix.BPF_F_RDONLY_PROG, - Freeze: true, + Flags: sys.BPF_F_RDONLY_PROG, Key: &btf.Int{Size: 4}, Value: ds, } @@ -1201,8 +1315,14 @@ func (ec *elfCode) loadKsymsSection() error { } for _, v := range ds.Vars { - // we have already checked the .ksyms Datasec to only contain Func Vars. - ec.kfuncs[v.Type.TypeName()] = v.Type.(*btf.Func) + switch t := v.Type.(type) { + case *btf.Func: + ec.kfuncs[t.TypeName()] = t + case *btf.Var: + ec.ksyms[t.TypeName()] = struct{}{} + default: + return fmt.Errorf("unexpected variable type in .ksyms: %T", v) + } } return nil @@ -1266,10 +1386,10 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) { var flags uint32 if t.flags&_SEC_SLEEPABLE > 0 { - flags |= unix.BPF_F_SLEEPABLE + flags |= sys.BPF_F_SLEEPABLE } if t.flags&_SEC_XDP_FRAGS > 0 { - flags |= unix.BPF_F_XDP_HAS_FRAGS + flags |= sys.BPF_F_XDP_HAS_FRAGS } if t.flags&_SEC_EXP_ATTACH_OPT > 0 { if programType == XDP { diff --git a/vendor/github.com/cilium/ebpf/info.go b/vendor/github.com/cilium/ebpf/info.go index 04c60c64b89..56a1f1e9a31 100644 --- a/vendor/github.com/cilium/ebpf/info.go +++ b/vendor/github.com/cilium/ebpf/info.go @@ -8,10 +8,10 @@ import ( "fmt" "io" "os" + "reflect" "strings" "syscall" "time" - "unsafe" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" @@ -39,53 +39,83 @@ import ( // MapInfo describes a map. type MapInfo struct { - Type MapType - id MapID - KeySize uint32 - ValueSize uint32 + // Type of the map. + Type MapType + // KeySize is the size of the map key in bytes. + KeySize uint32 + // ValueSize is the size of the map value in bytes. + ValueSize uint32 + // MaxEntries is the maximum number of entries the map can hold. Its meaning + // is map-specific. MaxEntries uint32 - Flags uint32 + // Flags used during map creation. + Flags uint32 // Name as supplied by user space at load time. Available from 4.15. Name string - btf btf.ID + id MapID + btf btf.ID + mapExtra uint64 + memlock uint64 + frozen bool } +// newMapInfoFromFd queries map information about the given fd. [sys.ObjInfo] is +// attempted first, supplementing any missing values with information from +// /proc/self/fdinfo. Ignores EINVAL from ObjInfo as well as ErrNotSupported +// from reading fdinfo (indicating the file exists, but no fields of interest +// were found). If both fail, an error is always returned. func newMapInfoFromFd(fd *sys.FD) (*MapInfo, error) { var info sys.MapInfo - err := sys.ObjInfo(fd, &info) - if errors.Is(err, syscall.EINVAL) { - return newMapInfoFromProc(fd) - } - if err != nil { - return nil, err + err1 := sys.ObjInfo(fd, &info) + // EINVAL means the kernel doesn't support BPF_OBJ_GET_INFO_BY_FD. Continue + // with fdinfo if that's the case. + if err1 != nil && !errors.Is(err1, unix.EINVAL) { + return nil, fmt.Errorf("getting object info: %w", err1) } - return &MapInfo{ + mi := &MapInfo{ MapType(info.Type), - MapID(info.Id), info.KeySize, info.ValueSize, info.MaxEntries, uint32(info.MapFlags), unix.ByteSliceToString(info.Name[:]), + MapID(info.Id), btf.ID(info.BtfId), - }, nil + info.MapExtra, + 0, + false, + } + + // Supplement OBJ_INFO with data from /proc/self/fdinfo. It contains fields + // like memlock and frozen that are not present in OBJ_INFO. + err2 := readMapInfoFromProc(fd, mi) + if err2 != nil && !errors.Is(err2, ErrNotSupported) { + return nil, fmt.Errorf("getting map info from fdinfo: %w", err2) + } + + if err1 != nil && err2 != nil { + return nil, fmt.Errorf("ObjInfo and fdinfo both failed: objinfo: %w, fdinfo: %w", err1, err2) + } + + return mi, nil } -func newMapInfoFromProc(fd *sys.FD) (*MapInfo, error) { - var mi MapInfo - err := scanFdInfo(fd, map[string]interface{}{ +// readMapInfoFromProc queries map information about the given fd from +// /proc/self/fdinfo. It only writes data into fields that have a zero value. +func readMapInfoFromProc(fd *sys.FD, mi *MapInfo) error { + return scanFdInfo(fd, map[string]interface{}{ "map_type": &mi.Type, + "map_id": &mi.id, "key_size": &mi.KeySize, "value_size": &mi.ValueSize, "max_entries": &mi.MaxEntries, "map_flags": &mi.Flags, + "map_extra": &mi.mapExtra, + "memlock": &mi.memlock, + "frozen": &mi.frozen, }) - if err != nil { - return nil, err - } - return &mi, nil } // ID returns the map ID. @@ -109,6 +139,35 @@ func (mi *MapInfo) BTFID() (btf.ID, bool) { return mi.btf, mi.btf > 0 } +// MapExtra returns an opaque field whose meaning is map-specific. +// +// Available from 5.16. +// +// The bool return value indicates whether this optional field is available and +// populated, if it was specified during Map creation. +func (mi *MapInfo) MapExtra() (uint64, bool) { + return mi.mapExtra, mi.mapExtra > 0 +} + +// Memlock returns an approximate number of bytes allocated to this map. +// +// Available from 4.10. +// +// The bool return value indicates whether this optional field is available. +func (mi *MapInfo) Memlock() (uint64, bool) { + return mi.memlock, mi.memlock > 0 +} + +// Frozen indicates whether [Map.Freeze] was called on this map. If true, +// modifications from user space are not allowed. +// +// Available from 5.2. Requires access to procfs. +// +// If the kernel doesn't support map freezing, this field will always be false. +func (mi *MapInfo) Frozen() bool { + return mi.frozen +} + // programStats holds statistics of a program. type programStats struct { // Total accumulated runtime of the program ins ns. @@ -120,6 +179,40 @@ type programStats struct { recursionMisses uint64 } +// programJitedInfo holds information about JITed info of a program. +type programJitedInfo struct { + // ksyms holds the ksym addresses of the BPF program, including those of its + // subprograms. + // + // Available from 4.18. + ksyms []uintptr + numKsyms uint32 + + // insns holds the JITed machine native instructions of the program, + // including those of its subprograms. + // + // Available from 4.13. + insns []byte + numInsns uint32 + + // lineInfos holds the JITed line infos, which are kernel addresses. + // + // Available from 5.0. + lineInfos []uint64 + numLineInfos uint32 + + // lineInfoRecSize is the size of a single line info record. + // + // Available from 5.0. + lineInfoRecSize uint32 + + // funcLens holds the insns length of each function. + // + // Available from 4.18. + funcLens []uint32 + numFuncLens uint32 +} + // ProgramInfo describes a program. type ProgramInfo struct { Type ProgramType @@ -133,9 +226,14 @@ type ProgramInfo struct { haveCreatedByUID bool btf btf.ID stats *programStats + loadTime time.Duration + + maps []MapID + insns []byte + jitedSize uint32 + verifiedInstructions uint32 - maps []MapID - insns []byte + jitedInfo programJitedInfo lineInfos []byte numLineInfos uint32 @@ -164,6 +262,9 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) { runCount: info.RunCnt, recursionMisses: info.RecursionMisses, }, + jitedSize: info.JitedProgLen, + loadTime: time.Duration(info.LoadTime), + verifiedInstructions: info.VerifiedInsns, } // Start with a clean struct for the second call, otherwise we may get EFAULT. @@ -174,7 +275,7 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) { if info.NrMapIds > 0 { pi.maps = make([]MapID, info.NrMapIds) info2.NrMapIds = info.NrMapIds - info2.MapIds = sys.NewPointer(unsafe.Pointer(&pi.maps[0])) + info2.MapIds = sys.NewSlicePointer(pi.maps) makeSecondCall = true } else if haveProgramInfoMapIDs() == nil { // This program really has no associated maps. @@ -215,6 +316,40 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) { makeSecondCall = true } + pi.jitedInfo.lineInfoRecSize = info.JitedLineInfoRecSize + if info.JitedProgLen > 0 { + pi.jitedInfo.numInsns = info.JitedProgLen + pi.jitedInfo.insns = make([]byte, info.JitedProgLen) + info2.JitedProgLen = info.JitedProgLen + info2.JitedProgInsns = sys.NewSlicePointer(pi.jitedInfo.insns) + makeSecondCall = true + } + + if info.NrJitedFuncLens > 0 { + pi.jitedInfo.numFuncLens = info.NrJitedFuncLens + pi.jitedInfo.funcLens = make([]uint32, info.NrJitedFuncLens) + info2.NrJitedFuncLens = info.NrJitedFuncLens + info2.JitedFuncLens = sys.NewSlicePointer(pi.jitedInfo.funcLens) + makeSecondCall = true + } + + if info.NrJitedLineInfo > 0 { + pi.jitedInfo.numLineInfos = info.NrJitedLineInfo + pi.jitedInfo.lineInfos = make([]uint64, info.NrJitedLineInfo) + info2.NrJitedLineInfo = info.NrJitedLineInfo + info2.JitedLineInfo = sys.NewSlicePointer(pi.jitedInfo.lineInfos) + info2.JitedLineInfoRecSize = info.JitedLineInfoRecSize + makeSecondCall = true + } + + if info.NrJitedKsyms > 0 { + pi.jitedInfo.numKsyms = info.NrJitedKsyms + pi.jitedInfo.ksyms = make([]uintptr, info.NrJitedKsyms) + info2.JitedKsyms = sys.NewSlicePointer(pi.jitedInfo.ksyms) + info2.NrJitedKsyms = info.NrJitedKsyms + makeSecondCall = true + } + if makeSecondCall { if err := sys.ObjInfo(fd, &info2); err != nil { return nil, err @@ -230,7 +365,7 @@ func newProgramInfoFromProc(fd *sys.FD) (*ProgramInfo, error) { "prog_type": &info.Type, "prog_tag": &info.Tag, }) - if errors.Is(err, errMissingFields) { + if errors.Is(err, ErrNotSupported) { return nil, &internal.UnsupportedFeatureError{ Name: "reading program info from /proc/self/fdinfo", MinimumVersion: internal.Version{4, 10, 0}, @@ -305,6 +440,52 @@ func (pi *ProgramInfo) RecursionMisses() (uint64, bool) { return 0, false } +// btfSpec returns the BTF spec associated with the program. +func (pi *ProgramInfo) btfSpec() (*btf.Spec, error) { + id, ok := pi.BTFID() + if !ok { + return nil, fmt.Errorf("program created without BTF or unsupported kernel: %w", ErrNotSupported) + } + + h, err := btf.NewHandleFromID(id) + if err != nil { + return nil, fmt.Errorf("get BTF handle: %w", err) + } + defer h.Close() + + spec, err := h.Spec(nil) + if err != nil { + return nil, fmt.Errorf("get BTF spec: %w", err) + } + + return spec, nil +} + +// LineInfos returns the BTF line information of the program. +// +// Available from 5.0. +// +// Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns +// ErrNotSupported if the program was created without BTF or if the kernel +// doesn't support the field. +func (pi *ProgramInfo) LineInfos() (btf.LineOffsets, error) { + if len(pi.lineInfos) == 0 { + return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported) + } + + spec, err := pi.btfSpec() + if err != nil { + return nil, err + } + + return btf.LoadLineInfos( + bytes.NewReader(pi.lineInfos), + internal.NativeEndian, + pi.numLineInfos, + spec, + ) +} + // Instructions returns the 'xlated' instruction stream of the program // after it has been verified and rewritten by the kernel. These instructions // cannot be loaded back into the kernel as-is, this is mainly used for @@ -391,6 +572,29 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) { return insns, nil } +// JitedSize returns the size of the program's JIT-compiled machine code in bytes, which is the +// actual code executed on the host's CPU. This field requires the BPF JIT compiler to be enabled. +// +// Available from 4.13. Reading this metadata requires CAP_BPF or equivalent. +func (pi *ProgramInfo) JitedSize() (uint32, error) { + if pi.jitedSize == 0 { + return 0, fmt.Errorf("insufficient permissions, unsupported kernel, or JIT compiler disabled: %w", ErrNotSupported) + } + return pi.jitedSize, nil +} + +// TranslatedSize returns the size of the program's translated instructions in bytes, after it has +// been verified and rewritten by the kernel. +// +// Available from 4.13. Reading this metadata requires CAP_BPF or equivalent. +func (pi *ProgramInfo) TranslatedSize() (int, error) { + insns := len(pi.insns) + if insns == 0 { + return 0, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported) + } + return insns, nil +} + // MapIDs returns the maps related to the program. // // Available from 4.15. @@ -400,6 +604,89 @@ func (pi *ProgramInfo) MapIDs() ([]MapID, bool) { return pi.maps, pi.maps != nil } +// LoadTime returns when the program was loaded since boot time. +// +// Available from 4.15. +// +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) LoadTime() (time.Duration, bool) { + // loadTime and NrMapIds were introduced in the same kernel version. + return pi.loadTime, pi.loadTime > 0 +} + +// VerifiedInstructions returns the number verified instructions in the program. +// +// Available from 5.16. +// +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) VerifiedInstructions() (uint32, bool) { + return pi.verifiedInstructions, pi.verifiedInstructions > 0 +} + +// JitedKsymAddrs returns the ksym addresses of the BPF program, including its +// subprograms. The addresses correspond to their symbols in /proc/kallsyms. +// +// Available from 4.18. Note that before 5.x, this field can be empty for +// programs without subprograms (bpf2bpf calls). +// +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) JitedKsymAddrs() ([]uintptr, bool) { + return pi.jitedInfo.ksyms, len(pi.jitedInfo.ksyms) > 0 +} + +// JitedInsns returns the JITed machine native instructions of the program. +// +// Available from 4.13. +// +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) JitedInsns() ([]byte, bool) { + return pi.jitedInfo.insns, len(pi.jitedInfo.insns) > 0 +} + +// JitedLineInfos returns the JITed line infos of the program. +// +// Available from 5.0. +// +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) JitedLineInfos() ([]uint64, bool) { + return pi.jitedInfo.lineInfos, len(pi.jitedInfo.lineInfos) > 0 +} + +// JitedFuncLens returns the insns length of each function in the JITed program. +// +// Available from 4.18. +// +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) JitedFuncLens() ([]uint32, bool) { + return pi.jitedInfo.funcLens, len(pi.jitedInfo.funcLens) > 0 +} + +// FuncInfos returns the offset and function information of all (sub)programs in +// a BPF program. +// +// Available from 5.0. +// +// Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns +// ErrNotSupported if the program was created without BTF or if the kernel +// doesn't support the field. +func (pi *ProgramInfo) FuncInfos() (btf.FuncOffsets, error) { + if len(pi.funcInfos) == 0 { + return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported) + } + + spec, err := pi.btfSpec() + if err != nil { + return nil, err + } + + return btf.LoadFuncInfos( + bytes.NewReader(pi.funcInfos), + internal.NativeEndian, + pi.numFuncInfos, + spec, + ) +} + func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error { fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", fd.Int())) if err != nil { @@ -413,8 +700,6 @@ func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error { return nil } -var errMissingFields = errors.New("missing fields") - func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { var ( scanner = bufio.NewScanner(r) @@ -433,26 +718,37 @@ func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { continue } - if n, err := fmt.Sscanln(parts[1], field); err != nil || n != 1 { - return fmt.Errorf("can't parse field %s: %v", name, err) + // If field already contains a non-zero value, don't overwrite it with fdinfo. + if zero(field) { + if n, err := fmt.Sscanln(parts[1], field); err != nil || n != 1 { + return fmt.Errorf("can't parse field %s: %v", name, err) + } } scanned++ } if err := scanner.Err(); err != nil { - return err + return fmt.Errorf("scanning fdinfo: %w", err) } if len(fields) > 0 && scanned == 0 { return ErrNotSupported } - if scanned != len(fields) { - return errMissingFields + return nil +} + +func zero(arg any) bool { + v := reflect.ValueOf(arg) + + // Unwrap pointers and interfaces. + for v.Kind() == reflect.Pointer || + v.Kind() == reflect.Interface { + v = v.Elem() } - return nil + return v.IsZero() } // EnableStats starts the measuring of the runtime @@ -471,7 +767,7 @@ func EnableStats(which uint32) (io.Closer, error) { return fd, nil } -var haveProgramInfoMapIDs = internal.NewFeatureTest("map IDs in program info", "4.15", func() error { +var haveProgramInfoMapIDs = internal.NewFeatureTest("map IDs in program info", func() error { prog, err := progLoad(asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), @@ -496,4 +792,4 @@ var haveProgramInfoMapIDs = internal.NewFeatureTest("map IDs in program info", " } return err -}) +}, "4.15") diff --git a/vendor/github.com/cilium/ebpf/internal/errors.go b/vendor/github.com/cilium/ebpf/internal/errors.go index 83a371ad35d..19d5294ca04 100644 --- a/vendor/github.com/cilium/ebpf/internal/errors.go +++ b/vendor/github.com/cilium/ebpf/internal/errors.go @@ -23,7 +23,7 @@ func ErrorWithLog(source string, err error, log []byte) *VerifierError { log = bytes.Trim(log, whitespace) if len(log) == 0 { - return &VerifierError{source, err, nil, false} + return &VerifierError{source, err, nil} } logLines := bytes.Split(log, []byte{'\n'}) @@ -34,7 +34,7 @@ func ErrorWithLog(source string, err error, log []byte) *VerifierError { lines = append(lines, string(bytes.TrimRight(line, whitespace))) } - return &VerifierError{source, err, lines, false} + return &VerifierError{source, err, lines} } // VerifierError includes information from the eBPF verifier. @@ -46,8 +46,6 @@ type VerifierError struct { Cause error // The verifier output split into lines. Log []string - // Deprecated: the log is never truncated anymore. - Truncated bool } func (le *VerifierError) Unwrap() error { diff --git a/vendor/github.com/cilium/ebpf/internal/feature.go b/vendor/github.com/cilium/ebpf/internal/feature.go index 2b856c735e7..6399be08514 100644 --- a/vendor/github.com/cilium/ebpf/internal/feature.go +++ b/vendor/github.com/cilium/ebpf/internal/feature.go @@ -3,15 +3,25 @@ package internal import ( "errors" "fmt" + "runtime" + "strings" "sync" ) -// ErrNotSupported indicates that a feature is not supported by the current kernel. +// ErrNotSupported indicates that a feature is not supported. var ErrNotSupported = errors.New("not supported") +// ErrNotSupportedOnOS indicates that a feature is not supported on the current +// operating system. +var ErrNotSupportedOnOS = fmt.Errorf("%w on %s", ErrNotSupported, runtime.GOOS) + // UnsupportedFeatureError is returned by FeatureTest() functions. type UnsupportedFeatureError struct { - // The minimum Linux mainline version required for this feature. + // The minimum version required for this feature. + // + // On Linux this refers to the mainline kernel version, on other platforms + // to the version of the runtime. + // // Used for the error string, and for sanity checking during testing. MinimumVersion Version @@ -58,11 +68,44 @@ type FeatureTest struct { type FeatureTestFn func() error // NewFeatureTest is a convenient way to create a single [FeatureTest]. -func NewFeatureTest(name, version string, fn FeatureTestFn) func() error { +// +// versions specifies in which version of a BPF runtime a feature appeared. +// The format is "GOOS:Major.Minor[.Patch]". GOOS may be omitted when targeting +// Linux. Returns [ErrNotSupportedOnOS] if there is no version specified for the +// current OS. +func NewFeatureTest(name string, fn FeatureTestFn, versions ...string) func() error { + const nativePrefix = runtime.GOOS + ":" + + if len(versions) == 0 { + return func() error { + return fmt.Errorf("feature test %q: no versions specified", name) + } + } + ft := &FeatureTest{ - Name: name, - Version: version, - Fn: fn, + Name: name, + Fn: fn, + } + + for _, version := range versions { + if strings.HasPrefix(version, nativePrefix) { + ft.Version = strings.TrimPrefix(version, nativePrefix) + break + } + + if runtime.GOOS == "linux" && !strings.ContainsRune(version, ':') { + // Allow version numbers without a GOOS prefix on Linux. + ft.Version = version + break + } + } + + if ft.Version == "" { + return func() error { + // We don't return an UnsupportedFeatureError here, since that will + // trigger version checks which don't make sense. + return fmt.Errorf("%s: %w", name, ErrNotSupportedOnOS) + } } return ft.execute diff --git a/vendor/github.com/cilium/ebpf/internal/kallsyms/cache.go b/vendor/github.com/cilium/ebpf/internal/kallsyms/cache.go new file mode 100644 index 00000000000..b7f3e0b7819 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/kallsyms/cache.go @@ -0,0 +1,20 @@ +package kallsyms + +import "sync" + +type cache[K, V comparable] struct { + m sync.Map +} + +func (c *cache[K, V]) Load(key K) (value V, _ bool) { + v, ok := c.m.Load(key) + if !ok { + return value, false + } + value = v.(V) + return value, true +} + +func (c *cache[K, V]) Store(key K, value V) { + c.m.Store(key, value) +} diff --git a/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go b/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go index 776c7a10a28..f93d785849c 100644 --- a/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go +++ b/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go @@ -1,74 +1,277 @@ package kallsyms import ( - "bufio" - "bytes" + "errors" + "fmt" "io" "os" - "sync" + "slices" + "strconv" + "strings" ) -var kernelModules struct { - sync.RWMutex - // function to kernel module mapping - kmods map[string]string -} +var errAmbiguousKsym = errors.New("multiple kernel symbols with the same name") -// KernelModule returns the kernel module, if any, a probe-able function is contained in. -func KernelModule(fn string) (string, error) { - kernelModules.RLock() - kmods := kernelModules.kmods - kernelModules.RUnlock() +var symAddrs cache[string, uint64] +var symModules cache[string, string] - if kmods == nil { - kernelModules.Lock() - defer kernelModules.Unlock() - kmods = kernelModules.kmods +// Module returns the kernel module providing the given symbol in the kernel, if +// any. Returns an empty string and no error if the symbol is not present in the +// kernel. Only function symbols are considered. Returns an error if multiple +// symbols with the same name were found. +// +// Consider [AssignModules] if you need to resolve multiple symbols, as it will +// only perform one iteration over /proc/kallsyms. +func Module(name string) (string, error) { + if name == "" { + return "", nil } - if kmods != nil { - return kmods[fn], nil + if mod, ok := symModules.Load(name); ok { + return mod, nil } - f, err := os.Open("/proc/kallsyms") - if err != nil { + request := map[string]string{name: ""} + if err := AssignModules(request); err != nil { return "", err } - defer f.Close() - kmods, err = loadKernelModuleMapping(f) + + return request[name], nil +} + +// AssignModules looks up the kernel module providing each given symbol, if any, +// and assigns them to their corresponding values in the symbols map. Only +// function symbols are considered. Results of all lookups are cached, +// successful or otherwise. +// +// Any symbols missing in the kernel are ignored. Returns an error if multiple +// symbols with a given name were found. +func AssignModules(symbols map[string]string) error { + if len(symbols) == 0 { + return nil + } + + // Attempt to fetch symbols from cache. + request := make(map[string]string) + for name := range symbols { + if mod, ok := symModules.Load(name); ok { + symbols[name] = mod + continue + } + + // Mark the symbol to be read from /proc/kallsyms. + request[name] = "" + } + if len(request) == 0 { + // All symbols satisfied from cache. + return nil + } + + f, err := os.Open("/proc/kallsyms") if err != nil { - return "", err + return err + } + + if err := assignModules(f, request); err != nil { + return fmt.Errorf("assigning symbol modules: %w", err) + } + + // Update the cache with the new symbols. Cache all requested symbols, even if + // they're missing or don't belong to a module. + for name, mod := range request { + symModules.Store(name, mod) + symbols[name] = mod + } + + return nil +} + +// assignModules assigns kernel symbol modules read from f to values requested +// by symbols. Always scans the whole input to make sure the user didn't request +// an ambiguous symbol. +func assignModules(f io.Reader, symbols map[string]string) error { + if len(symbols) == 0 { + return nil + } + + found := make(map[string]struct{}) + r := newReader(f) + for r.Line() { + // Only look for function symbols in the kernel's text section (tT). + s, err, skip := parseSymbol(r, []rune{'t', 'T'}) + if err != nil { + return fmt.Errorf("parsing kallsyms line: %w", err) + } + if skip { + continue + } + + if _, requested := symbols[s.name]; !requested { + continue + } + + if _, ok := found[s.name]; ok { + // We've already seen this symbol. Return an error to avoid silently + // attaching to a symbol in the wrong module. libbpf also rejects + // referring to ambiguous symbols. + // + // We can't simply check if we already have a value for the given symbol, + // since many won't have an associated kernel module. + return fmt.Errorf("symbol %s: duplicate found at address 0x%x (module %q): %w", + s.name, s.addr, s.mod, errAmbiguousKsym) + } + + symbols[s.name] = s.mod + found[s.name] = struct{}{} + } + if err := r.Err(); err != nil { + return fmt.Errorf("reading kallsyms: %w", err) } - kernelModules.kmods = kmods - return kmods[fn], nil + return nil } -// FlushKernelModuleCache removes any cached information about function to kernel module mapping. -func FlushKernelModuleCache() { - kernelModules.Lock() - defer kernelModules.Unlock() +// Address returns the address of the given symbol in the kernel. Returns 0 and +// no error if the symbol is not present. Returns an error if multiple addresses +// were found for a symbol. +// +// Consider [AssignAddresses] if you need to resolve multiple symbols, as it +// will only perform one iteration over /proc/kallsyms. +func Address(symbol string) (uint64, error) { + if symbol == "" { + return 0, nil + } + + if addr, ok := symAddrs.Load(symbol); ok { + return addr, nil + } + + request := map[string]uint64{symbol: 0} + if err := AssignAddresses(request); err != nil { + return 0, err + } - kernelModules.kmods = nil + return request[symbol], nil } -func loadKernelModuleMapping(f io.Reader) (map[string]string, error) { - mods := make(map[string]string) - scanner := bufio.NewScanner(f) - for scanner.Scan() { - fields := bytes.Fields(scanner.Bytes()) - if len(fields) < 4 { +// AssignAddresses looks up the addresses of the requested symbols in the kernel +// and assigns them to their corresponding values in the symbols map. Results +// of all lookups are cached, successful or otherwise. +// +// Any symbols missing in the kernel are ignored. Returns an error if multiple +// addresses were found for a symbol. +func AssignAddresses(symbols map[string]uint64) error { + if len(symbols) == 0 { + return nil + } + + // Attempt to fetch symbols from cache. + request := make(map[string]uint64) + for name := range symbols { + if addr, ok := symAddrs.Load(name); ok { + symbols[name] = addr continue } - switch string(fields[1]) { - case "t", "T": - mods[string(fields[2])] = string(bytes.Trim(fields[3], "[]")) - default: + + // Mark the symbol to be read from /proc/kallsyms. + request[name] = 0 + } + if len(request) == 0 { + // All symbols satisfied from cache. + return nil + } + + f, err := os.Open("/proc/kallsyms") + if err != nil { + return err + } + + if err := assignAddresses(f, request); err != nil { + return fmt.Errorf("loading symbol addresses: %w", err) + } + + // Update the cache with the new symbols. Cache all requested symbols even if + // they weren't found, to avoid repeated lookups. + for name, addr := range request { + symAddrs.Store(name, addr) + symbols[name] = addr + } + + return nil +} + +// assignAddresses assigns kernel symbol addresses read from f to values +// requested by symbols. Always scans the whole input to make sure the user +// didn't request an ambiguous symbol. +func assignAddresses(f io.Reader, symbols map[string]uint64) error { + if len(symbols) == 0 { + return nil + } + r := newReader(f) + for r.Line() { + s, err, skip := parseSymbol(r, nil) + if err != nil { + return fmt.Errorf("parsing kallsyms line: %w", err) + } + if skip { continue } + + existing, requested := symbols[s.name] + if existing != 0 { + // Multiple addresses for a symbol have been found. Return a friendly + // error to avoid silently attaching to the wrong symbol. libbpf also + // rejects referring to ambiguous symbols. + return fmt.Errorf("symbol %s(0x%x): duplicate found at address 0x%x: %w", s.name, existing, s.addr, errAmbiguousKsym) + } + if requested { + symbols[s.name] = s.addr + } } - if scanner.Err() != nil { - return nil, scanner.Err() + if err := r.Err(); err != nil { + return fmt.Errorf("reading kallsyms: %w", err) } - return mods, nil + + return nil +} + +type ksym struct { + addr uint64 + name string + mod string +} + +// parseSymbol parses a line from /proc/kallsyms into an address, type, name and +// module. Skip will be true if the symbol doesn't match any of the given symbol +// types. See `man 1 nm` for all available types. +// +// Example line: `ffffffffc1682010 T nf_nat_init [nf_nat]` +func parseSymbol(r *reader, types []rune) (s ksym, err error, skip bool) { + for i := 0; r.Word(); i++ { + switch i { + // Address of the symbol. + case 0: + s.addr, err = strconv.ParseUint(r.Text(), 16, 64) + if err != nil { + return s, fmt.Errorf("parsing address: %w", err), false + } + // Type of the symbol. Assume the character is ASCII-encoded by converting + // it directly to a rune, since it's a fixed field controlled by the kernel. + case 1: + if len(types) > 0 && !slices.Contains(types, rune(r.Bytes()[0])) { + return s, nil, true + } + // Name of the symbol. + case 2: + s.name = r.Text() + // Kernel module the symbol is provided by. + case 3: + s.mod = strings.Trim(r.Text(), "[]") + // Ignore any future fields. + default: + break + } + } + + return } diff --git a/vendor/github.com/cilium/ebpf/internal/kallsyms/reader.go b/vendor/github.com/cilium/ebpf/internal/kallsyms/reader.go new file mode 100644 index 00000000000..2bd4f8eafcb --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/kallsyms/reader.go @@ -0,0 +1,118 @@ +package kallsyms + +import ( + "bufio" + "io" + "unicode" + "unicode/utf8" +) + +// reader is a line and word-oriented reader built for reading /proc/kallsyms. +// It takes an io.Reader and iterates its contents line by line, then word by +// word. +// +// It's designed to allow partial reading of lines without paying the cost of +// allocating objects that will never be accessed, resulting in less work for +// the garbage collector. +type reader struct { + s *bufio.Scanner + line []byte + word []byte + + err error +} + +func newReader(r io.Reader) *reader { + return &reader{ + s: bufio.NewScanner(r), + } +} + +// Bytes returns the current word as a byte slice. +func (r *reader) Bytes() []byte { + return r.word +} + +// Text returns the output of Bytes as a string. +func (r *reader) Text() string { + return string(r.Bytes()) +} + +// Line advances the reader to the next line in the input. Calling Line resets +// the current word, making [reader.Bytes] and [reader.Text] return empty +// values. Follow this up with a call to [reader.Word]. +// +// Like [bufio.Scanner], [reader.Err] needs to be checked after Line returns +// false to determine if an error occurred during reading. +// +// Returns true if Line can be called again. Returns false if all lines in the +// input have been read. +func (r *reader) Line() bool { + for r.s.Scan() { + line := r.s.Bytes() + if len(line) == 0 { + continue + } + + r.line = line + r.word = nil + + return true + } + if err := r.s.Err(); err != nil { + r.err = err + } + + return false +} + +// Word advances the reader to the next word in the current line. +// +// Returns true if a word is found and Word should be called again. Returns +// false when all words on the line have been read. +func (r *reader) Word() bool { + if len(r.line) == 0 { + return false + } + + // Find next word start, skipping leading spaces. + start := 0 + for width := 0; start < len(r.line); start += width { + var c rune + c, width = utf8.DecodeRune(r.line[start:]) + if !unicode.IsSpace(c) { + break + } + } + + // Whitespace scanning reached the end of the line due to trailing whitespace, + // meaning there are no more words to read + if start == len(r.line) { + return false + } + + // Find next word end. + for width, i := 0, start; i < len(r.line); i += width { + var c rune + c, width = utf8.DecodeRune(r.line[i:]) + if unicode.IsSpace(c) { + r.word = r.line[start:i] + r.line = r.line[i:] + return true + } + } + + // The line contains data, but no end-of-word boundary was found. This is the + // last, unterminated word in the line. + if len(r.line) > start { + r.word = r.line[start:] + r.line = nil + return true + } + + return false +} + +func (r *reader) Err() error { + return r.err +} diff --git a/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go b/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go index 1921e4f15ad..29c62b6266e 100644 --- a/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go +++ b/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go @@ -1,3 +1,4 @@ +// Package kconfig implements a parser for the format of Linux's .config file. package kconfig import ( @@ -7,7 +8,6 @@ import ( "fmt" "io" "math" - "os" "strconv" "strings" @@ -15,30 +15,6 @@ import ( "github.com/cilium/ebpf/internal" ) -// Find find a kconfig file on the host. -// It first reads from /boot/config- of the current running kernel and tries -// /proc/config.gz if nothing was found in /boot. -// If none of the file provide a kconfig, it returns an error. -func Find() (*os.File, error) { - kernelRelease, err := internal.KernelRelease() - if err != nil { - return nil, fmt.Errorf("cannot get kernel release: %w", err) - } - - path := "/boot/config-" + kernelRelease - f, err := os.Open(path) - if err == nil { - return f, nil - } - - f, err = os.Open("/proc/config.gz") - if err == nil { - return f, nil - } - - return nil, fmt.Errorf("neither %s nor /proc/config.gz provide a kconfig", path) -} - // Parse parses the kconfig file for which a reader is given. // All the CONFIG_* which are in filter and which are set set will be // put in the returned map as key with their corresponding value as map value. @@ -127,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: @@ -169,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": @@ -178,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/auxv.go b/vendor/github.com/cilium/ebpf/internal/linux/auxv.go similarity index 98% rename from vendor/github.com/cilium/ebpf/internal/auxv.go rename to vendor/github.com/cilium/ebpf/internal/linux/auxv.go index 45fd0d37f13..98bb5d83cad 100644 --- a/vendor/github.com/cilium/ebpf/internal/auxv.go +++ b/vendor/github.com/cilium/ebpf/internal/linux/auxv.go @@ -1,4 +1,4 @@ -package internal +package linux import ( "errors" diff --git a/vendor/github.com/cilium/ebpf/internal/linux/doc.go b/vendor/github.com/cilium/ebpf/internal/linux/doc.go new file mode 100644 index 00000000000..064e75437d8 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/linux/doc.go @@ -0,0 +1,2 @@ +// Package linux contains OS specific wrappers around package unix. +package linux diff --git a/vendor/github.com/cilium/ebpf/internal/linux/kconfig.go b/vendor/github.com/cilium/ebpf/internal/linux/kconfig.go new file mode 100644 index 00000000000..1488ecb35c3 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/linux/kconfig.go @@ -0,0 +1,31 @@ +package linux + +import ( + "fmt" + "os" +) + +// FindKConfig searches for a kconfig file on the host. +// +// It first reads from /boot/config- of the current running kernel and tries +// /proc/config.gz if nothing was found in /boot. +// If none of the file provide a kconfig, it returns an error. +func FindKConfig() (*os.File, error) { + kernelRelease, err := KernelRelease() + if err != nil { + return nil, fmt.Errorf("cannot get kernel release: %w", err) + } + + path := "/boot/config-" + kernelRelease + f, err := os.Open(path) + if err == nil { + return f, nil + } + + f, err = os.Open("/proc/config.gz") + if err == nil { + return f, nil + } + + return nil, fmt.Errorf("neither %s nor /proc/config.gz provide a kconfig", path) +} diff --git a/vendor/github.com/cilium/ebpf/internal/platform.go b/vendor/github.com/cilium/ebpf/internal/linux/platform.go similarity index 97% rename from vendor/github.com/cilium/ebpf/internal/platform.go rename to vendor/github.com/cilium/ebpf/internal/linux/platform.go index 6e90f2ef714..39bdcc51f9a 100644 --- a/vendor/github.com/cilium/ebpf/internal/platform.go +++ b/vendor/github.com/cilium/ebpf/internal/linux/platform.go @@ -1,4 +1,4 @@ -package internal +package linux import ( "runtime" diff --git a/vendor/github.com/cilium/ebpf/internal/statfs.go b/vendor/github.com/cilium/ebpf/internal/linux/statfs.go similarity index 96% rename from vendor/github.com/cilium/ebpf/internal/statfs.go rename to vendor/github.com/cilium/ebpf/internal/linux/statfs.go index 44c02d676e6..e268c06fab6 100644 --- a/vendor/github.com/cilium/ebpf/internal/statfs.go +++ b/vendor/github.com/cilium/ebpf/internal/linux/statfs.go @@ -1,4 +1,4 @@ -package internal +package linux import ( "unsafe" diff --git a/vendor/github.com/cilium/ebpf/internal/vdso.go b/vendor/github.com/cilium/ebpf/internal/linux/vdso.go similarity index 93% rename from vendor/github.com/cilium/ebpf/internal/vdso.go rename to vendor/github.com/cilium/ebpf/internal/linux/vdso.go index 1049278554e..1d8d0ef6b11 100644 --- a/vendor/github.com/cilium/ebpf/internal/vdso.go +++ b/vendor/github.com/cilium/ebpf/internal/linux/vdso.go @@ -1,4 +1,4 @@ -package internal +package linux import ( "debug/elf" @@ -9,6 +9,7 @@ import ( "math" "os" + "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) @@ -82,7 +83,7 @@ type elfNoteHeader struct { // vdsoLinuxVersionCode returns the LINUX_VERSION_CODE embedded in // the ELF notes section of the binary provided by the reader. func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) { - hdr, err := NewSafeELFFile(r) + hdr, err := internal.NewSafeELFFile(r) if err != nil { return 0, fmt.Errorf("reading vDSO ELF: %w", err) } @@ -110,7 +111,7 @@ func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) { var name string if n.NameSize > 0 { // Read the note name, aligned to 4 bytes. - buf := make([]byte, Align(n.NameSize, 4)) + buf := make([]byte, internal.Align(n.NameSize, 4)) if err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil { return 0, fmt.Errorf("reading note name: %w", err) } @@ -132,7 +133,7 @@ func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) { } // Discard the note descriptor if it exists but we're not interested in it. - if _, err := io.CopyN(io.Discard, sr, int64(Align(n.DescSize, 4))); err != nil { + if _, err := io.CopyN(io.Discard, sr, int64(internal.Align(n.DescSize, 4))); err != nil { return 0, err } } diff --git a/vendor/github.com/cilium/ebpf/internal/linux/version.go b/vendor/github.com/cilium/ebpf/internal/linux/version.go new file mode 100644 index 00000000000..798dd3fed02 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/linux/version.go @@ -0,0 +1,34 @@ +package linux + +import ( + "fmt" + "sync" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/unix" +) + +// KernelVersion returns the version of the currently running kernel. +var KernelVersion = sync.OnceValues(detectKernelVersion) + +// detectKernelVersion returns the version of the running kernel. +func detectKernelVersion() (internal.Version, error) { + vc, err := vdsoVersion() + if err != nil { + return internal.Version{}, err + } + return internal.NewVersionFromCode(vc), nil +} + +// KernelRelease returns the release string of the running kernel. +// Its format depends on the Linux distribution and corresponds to directory +// names in /lib/modules by convention. Some examples are 5.15.17-1-lts and +// 4.19.0-16-amd64. +func KernelRelease() (string, error) { + var uname unix.Utsname + if err := unix.Uname(&uname); err != nil { + return "", fmt.Errorf("uname failed: %w", err) + } + + return unix.ByteSliceToString(uname.Release[:]), nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/math.go b/vendor/github.com/cilium/ebpf/internal/math.go index e95c8efde51..10cde66860d 100644 --- a/vendor/github.com/cilium/ebpf/internal/math.go +++ b/vendor/github.com/cilium/ebpf/internal/math.go @@ -1,13 +1,33 @@ package internal -import "golang.org/x/exp/constraints" - // Align returns 'n' updated to 'alignment' boundary. -func Align[I constraints.Integer](n, alignment I) I { +func Align[I Integer](n, alignment I) I { return (n + alignment - 1) / alignment * alignment } // IsPow returns true if n is a power of two. -func IsPow[I constraints.Integer](n I) bool { +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 { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +// List of integer types known by the Go compiler. Used by TestIntegerConstraint +// to warn if a new integer type is introduced. Remove when x/exp/constraints +// is moved to the standard library. +var integers = []string{"int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr"} diff --git a/vendor/github.com/cilium/ebpf/internal/sys/fd.go b/vendor/github.com/cilium/ebpf/internal/sys/fd.go index 941a56fb91b..e2ba43fd3b3 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/fd.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/fd.go @@ -4,9 +4,12 @@ import ( "fmt" "math" "os" + "path/filepath" "runtime" "strconv" + "strings" + "github.com/cilium/ebpf/internal/testutils/fdtrace" "github.com/cilium/ebpf/internal/unix" ) @@ -17,15 +20,7 @@ type FD struct { } func newFD(value int) *FD { - if onLeakFD != nil { - // Attempt to store the caller's stack for the given fd value. - // Panic if fds contains an existing stack for the fd. - old, exist := fds.LoadOrStore(value, callersFrames()) - if exist { - f := old.(*runtime.Frames) - panic(fmt.Sprintf("found existing stack for fd %d:\n%s", value, FormatFrames(f))) - } - } + fdtrace.TraceFD(value, 1) fd := &FD{value} runtime.SetFinalizer(fd, (*FD).finalize) @@ -39,13 +34,7 @@ func (fd *FD) finalize() { return } - // Invoke the fd leak callback. Calls LoadAndDelete to guarantee the callback - // is invoked at most once for one sys.FD allocation, runtime.Frames can only - // be unwound once. - f, ok := fds.LoadAndDelete(fd.Int()) - if ok && onLeakFD != nil { - onLeakFD(f.(*runtime.Frames)) - } + fdtrace.LeakFD(fd.raw) _ = fd.Close() } @@ -92,12 +81,15 @@ func (fd *FD) Close() error { return nil } - return unix.Close(fd.disown()) + return unix.Close(fd.Disown()) } -func (fd *FD) disown() int { - value := int(fd.raw) - fds.Delete(int(value)) +// 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 runtime.SetFinalizer(fd, nil) @@ -129,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/fd_trace.go b/vendor/github.com/cilium/ebpf/internal/sys/fd_trace.go deleted file mode 100644 index cd50dd1f642..00000000000 --- a/vendor/github.com/cilium/ebpf/internal/sys/fd_trace.go +++ /dev/null @@ -1,93 +0,0 @@ -package sys - -import ( - "bytes" - "fmt" - "runtime" - "sync" -) - -// OnLeakFD controls tracing [FD] lifetime to detect resources that are not -// closed by Close(). -// -// If fn is not nil, tracing is enabled for all FDs created going forward. fn is -// invoked for all FDs that are closed by the garbage collector instead of an -// explicit Close() by a caller. Calling OnLeakFD twice with a non-nil fn -// (without disabling tracing in the meantime) will cause a panic. -// -// If fn is nil, tracing will be disabled. Any FDs that have not been closed are -// considered to be leaked, fn will be invoked for them, and the process will be -// terminated. -// -// fn will be invoked at most once for every unique sys.FD allocation since a -// runtime.Frames can only be unwound once. -func OnLeakFD(fn func(*runtime.Frames)) { - // Enable leak tracing if new fn is provided. - if fn != nil { - if onLeakFD != nil { - panic("OnLeakFD called twice with non-nil fn") - } - - onLeakFD = fn - return - } - - // fn is nil past this point. - - if onLeakFD == nil { - return - } - - // Call onLeakFD for all open fds. - if fs := flushFrames(); len(fs) != 0 { - for _, f := range fs { - onLeakFD(f) - } - } - - onLeakFD = nil -} - -var onLeakFD func(*runtime.Frames) - -// fds is a registry of all file descriptors wrapped into sys.fds that were -// created while an fd tracer was active. -var fds sync.Map // map[int]*runtime.Frames - -// flushFrames removes all elements from fds and returns them as a slice. This -// deals with the fact that a runtime.Frames can only be unwound once using -// Next(). -func flushFrames() []*runtime.Frames { - var frames []*runtime.Frames - fds.Range(func(key, value any) bool { - frames = append(frames, value.(*runtime.Frames)) - fds.Delete(key) - return true - }) - return frames -} - -func callersFrames() *runtime.Frames { - c := make([]uintptr, 32) - - // Skip runtime.Callers and this function. - i := runtime.Callers(2, c) - if i == 0 { - return nil - } - - return runtime.CallersFrames(c) -} - -// FormatFrames formats a runtime.Frames as a human-readable string. -func FormatFrames(fs *runtime.Frames) string { - var b bytes.Buffer - for { - f, more := fs.Next() - b.WriteString(fmt.Sprintf("\t%s+%#x\n\t\t%s:%d\n", f.Function, f.PC-f.Entry, f.File, f.Line)) - if !more { - break - } - } - return b.String() -} diff --git a/vendor/github.com/cilium/ebpf/internal/sys/mapflags_string.go b/vendor/github.com/cilium/ebpf/internal/sys/mapflags_string.go deleted file mode 100644 index d9fe217222b..00000000000 --- a/vendor/github.com/cilium/ebpf/internal/sys/mapflags_string.go +++ /dev/null @@ -1,53 +0,0 @@ -// Code generated by "stringer -type MapFlags"; DO NOT EDIT. - -package sys - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[BPF_F_NO_PREALLOC-1] - _ = x[BPF_F_NO_COMMON_LRU-2] - _ = x[BPF_F_NUMA_NODE-4] - _ = x[BPF_F_RDONLY-8] - _ = x[BPF_F_WRONLY-16] - _ = x[BPF_F_STACK_BUILD_ID-32] - _ = x[BPF_F_ZERO_SEED-64] - _ = x[BPF_F_RDONLY_PROG-128] - _ = x[BPF_F_WRONLY_PROG-256] - _ = x[BPF_F_CLONE-512] - _ = x[BPF_F_MMAPABLE-1024] - _ = x[BPF_F_PRESERVE_ELEMS-2048] - _ = x[BPF_F_INNER_MAP-4096] - _ = x[BPF_F_LINK-8192] - _ = x[BPF_F_PATH_FD-16384] -} - -const _MapFlags_name = "BPF_F_NO_PREALLOCBPF_F_NO_COMMON_LRUBPF_F_NUMA_NODEBPF_F_RDONLYBPF_F_WRONLYBPF_F_STACK_BUILD_IDBPF_F_ZERO_SEEDBPF_F_RDONLY_PROGBPF_F_WRONLY_PROGBPF_F_CLONEBPF_F_MMAPABLEBPF_F_PRESERVE_ELEMSBPF_F_INNER_MAPBPF_F_LINKBPF_F_PATH_FD" - -var _MapFlags_map = map[MapFlags]string{ - 1: _MapFlags_name[0:17], - 2: _MapFlags_name[17:36], - 4: _MapFlags_name[36:51], - 8: _MapFlags_name[51:63], - 16: _MapFlags_name[63:75], - 32: _MapFlags_name[75:95], - 64: _MapFlags_name[95:110], - 128: _MapFlags_name[110:127], - 256: _MapFlags_name[127:144], - 512: _MapFlags_name[144:155], - 1024: _MapFlags_name[155:169], - 2048: _MapFlags_name[169:189], - 4096: _MapFlags_name[189:204], - 8192: _MapFlags_name[204:214], - 16384: _MapFlags_name[214:227], -} - -func (i MapFlags) String() string { - if str, ok := _MapFlags_map[i]; ok { - return str - } - return "MapFlags(" + strconv.FormatInt(int64(i), 10) + ")" -} diff --git a/vendor/github.com/cilium/ebpf/internal/pinning.go b/vendor/github.com/cilium/ebpf/internal/sys/pinning.go similarity index 77% rename from vendor/github.com/cilium/ebpf/internal/pinning.go rename to vendor/github.com/cilium/ebpf/internal/sys/pinning.go index 01d892f9344..9a4c6c7a153 100644 --- a/vendor/github.com/cilium/ebpf/internal/pinning.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/pinning.go @@ -1,4 +1,4 @@ -package internal +package sys import ( "errors" @@ -7,11 +7,11 @@ import ( "path/filepath" "runtime" - "github.com/cilium/ebpf/internal/sys" + "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/unix" ) -func Pin(currentPath, newPath string, fd *sys.FD) error { +func Pin(currentPath, newPath string, fd *FD) error { if newPath == "" { return errors.New("given pinning path cannot be empty") } @@ -19,7 +19,7 @@ func Pin(currentPath, newPath string, fd *sys.FD) error { return nil } - fsType, err := FSType(filepath.Dir(newPath)) + fsType, err := linux.FSType(filepath.Dir(newPath)) if err != nil { return err } @@ -30,8 +30,8 @@ func Pin(currentPath, newPath string, fd *sys.FD) error { defer runtime.KeepAlive(fd) if currentPath == "" { - return sys.ObjPin(&sys.ObjPinAttr{ - Pathname: sys.NewStringPointer(newPath), + return ObjPin(&ObjPinAttr{ + Pathname: NewStringPointer(newPath), BpfFd: fd.Uint(), }) } @@ -47,8 +47,8 @@ func Pin(currentPath, newPath string, fd *sys.FD) error { return fmt.Errorf("unable to move pinned object to new path %v: %w", newPath, err) } // Internal state not in sync with the file system so let's fix it. - return sys.ObjPin(&sys.ObjPinAttr{ - Pathname: sys.NewStringPointer(newPath), + return ObjPin(&ObjPinAttr{ + Pathname: NewStringPointer(newPath), BpfFd: fd.Uint(), }) } diff --git a/vendor/github.com/cilium/ebpf/internal/sys/ptr.go b/vendor/github.com/cilium/ebpf/internal/sys/ptr.go index e9bb5905973..af0c014e3b9 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/ptr.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/ptr.go @@ -11,13 +11,13 @@ func NewPointer(ptr unsafe.Pointer) Pointer { return Pointer{ptr: ptr} } -// NewSlicePointer creates a 64-bit pointer from a byte slice. -func NewSlicePointer(buf []byte) Pointer { +// NewSlicePointer creates a 64-bit pointer from a slice. +func NewSlicePointer[T comparable](buf []T) Pointer { if len(buf) == 0 { return Pointer{} } - return Pointer{ptr: unsafe.Pointer(&buf[0])} + return Pointer{ptr: unsafe.Pointer(unsafe.SliceData(buf))} } // NewSlicePointerLen creates a 64-bit pointer from a byte slice. diff --git a/vendor/github.com/cilium/ebpf/internal/sys/syscall.go b/vendor/github.com/cilium/ebpf/internal/sys/syscall.go index f6b6e934580..e37f4cf6710 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/syscall.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/syscall.go @@ -133,12 +133,12 @@ func ObjInfo(fd *FD, info Info) error { // BPFObjName is a null-terminated string made up of // 'A-Za-z0-9_' characters. -type ObjName [unix.BPF_OBJ_NAME_LEN]byte +type ObjName [BPF_OBJ_NAME_LEN]byte // NewObjName truncates the result if it is too long. func NewObjName(name string) ObjName { var result ObjName - copy(result[:unix.BPF_OBJ_NAME_LEN-1], name) + copy(result[:BPF_OBJ_NAME_LEN-1], name) return result } @@ -160,29 +160,6 @@ type BTFID uint32 // TypeID identifies a type in a BTF blob. type TypeID uint32 -// MapFlags control map behaviour. -type MapFlags uint32 - -//go:generate go run golang.org/x/tools/cmd/stringer@latest -type MapFlags - -const ( - BPF_F_NO_PREALLOC MapFlags = 1 << iota - BPF_F_NO_COMMON_LRU - BPF_F_NUMA_NODE - BPF_F_RDONLY - BPF_F_WRONLY - BPF_F_STACK_BUILD_ID - BPF_F_ZERO_SEED - BPF_F_RDONLY_PROG - BPF_F_WRONLY_PROG - BPF_F_CLONE - BPF_F_MMAPABLE - BPF_F_PRESERVE_ELEMS - BPF_F_INNER_MAP - BPF_F_LINK - BPF_F_PATH_FD -) - // Flags used by bpf_mprog. const ( BPF_F_REPLACE = 1 << (iota + 2) @@ -192,6 +169,16 @@ const ( BPF_F_LINK_MPROG = 1 << 13 // aka BPF_F_LINK ) +// Flags used by BPF_PROG_LOAD. +const ( + BPF_F_SLEEPABLE = 1 << 4 + BPF_F_XDP_HAS_FRAGS = 1 << 5 + BPF_F_XDP_DEV_BOUND_ONLY = 1 << 6 +) + +const BPF_TAG_SIZE = 8 +const BPF_OBJ_NAME_LEN = 16 + // wrappedErrno wraps syscall.Errno to prevent direct comparisons with // syscall.E* or unix.E* constants. // diff --git a/vendor/github.com/cilium/ebpf/internal/sys/types.go b/vendor/github.com/cilium/ebpf/internal/sys/types.go index 70e754de71d..88001c319eb 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/types.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/types.go @@ -6,6 +6,170 @@ import ( "unsafe" ) +const ( + BPF_ADJ_ROOM_ENCAP_L2_MASK = 255 + BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 56 + BPF_ANY = 0 + BPF_CSUM_LEVEL_DEC = 2 + BPF_CSUM_LEVEL_INC = 1 + BPF_CSUM_LEVEL_QUERY = 0 + BPF_CSUM_LEVEL_RESET = 3 + BPF_EXIST = 2 + BPF_FIB_LKUP_RET_BLACKHOLE = 1 + BPF_FIB_LKUP_RET_FRAG_NEEDED = 8 + BPF_FIB_LKUP_RET_FWD_DISABLED = 5 + BPF_FIB_LKUP_RET_NOT_FWDED = 4 + BPF_FIB_LKUP_RET_NO_NEIGH = 7 + BPF_FIB_LKUP_RET_NO_SRC_ADDR = 9 + BPF_FIB_LKUP_RET_PROHIBIT = 3 + BPF_FIB_LKUP_RET_SUCCESS = 0 + BPF_FIB_LKUP_RET_UNREACHABLE = 2 + BPF_FIB_LKUP_RET_UNSUPP_LWT = 6 + BPF_FIB_LOOKUP_DIRECT = 1 + BPF_FIB_LOOKUP_OUTPUT = 2 + BPF_FIB_LOOKUP_SKIP_NEIGH = 4 + BPF_FIB_LOOKUP_SRC = 16 + BPF_FIB_LOOKUP_TBID = 8 + BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = 1 + BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = 4 + BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = 2 + BPF_F_ADJ_ROOM_DECAP_L3_IPV4 = 128 + BPF_F_ADJ_ROOM_DECAP_L3_IPV6 = 256 + BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 64 + BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 = 2 + BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 = 4 + BPF_F_ADJ_ROOM_ENCAP_L4_GRE = 8 + BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 16 + BPF_F_ADJ_ROOM_FIXED_GSO = 1 + BPF_F_ADJ_ROOM_NO_CSUM_RESET = 32 + BPF_F_BPRM_SECUREEXEC = 1 + BPF_F_BROADCAST = 8 + BPF_F_CLONE = 512 + BPF_F_CTXLEN_MASK = 4503595332403200 + BPF_F_CURRENT_CPU = 4294967295 + BPF_F_CURRENT_NETNS = 18446744073709551615 + BPF_F_DONT_FRAGMENT = 4 + BPF_F_EXCLUDE_INGRESS = 16 + BPF_F_FAST_STACK_CMP = 512 + BPF_F_GET_BRANCH_RECORDS_SIZE = 1 + BPF_F_HDR_FIELD_MASK = 15 + BPF_F_INDEX_MASK = 4294967295 + BPF_F_INGRESS = 1 + BPF_F_INNER_MAP = 4096 + BPF_F_INVALIDATE_HASH = 2 + BPF_F_KPROBE_MULTI_RETURN = 1 + BPF_F_LINK = 8192 + BPF_F_LOCK = 4 + BPF_F_MARK_ENFORCE = 64 + BPF_F_MARK_MANGLED_0 = 32 + BPF_F_MMAPABLE = 1024 + BPF_F_NEIGH = 2 + BPF_F_NEXTHOP = 8 + BPF_F_NO_COMMON_LRU = 2 + BPF_F_NO_PREALLOC = 1 + BPF_F_NO_TUNNEL_KEY = 16 + BPF_F_NUMA_NODE = 4 + BPF_F_PATH_FD = 16384 + BPF_F_PEER = 4 + BPF_F_PRESERVE_ELEMS = 2048 + BPF_F_PSEUDO_HDR = 16 + BPF_F_RDONLY = 8 + BPF_F_RDONLY_PROG = 128 + BPF_F_RECOMPUTE_CSUM = 1 + BPF_F_REUSE_STACKID = 1024 + BPF_F_SEQ_NUMBER = 8 + BPF_F_SKIP_FIELD_MASK = 255 + BPF_F_STACK_BUILD_ID = 32 + BPF_F_SYSCTL_BASE_NAME = 1 + BPF_F_TIMER_ABS = 1 + BPF_F_TIMER_CPU_PIN = 2 + BPF_F_TUNINFO_FLAGS = 16 + BPF_F_TUNINFO_IPV6 = 1 + BPF_F_UPROBE_MULTI_RETURN = 1 + BPF_F_USER_BUILD_ID = 2048 + BPF_F_USER_STACK = 256 + BPF_F_WRONLY = 16 + BPF_F_WRONLY_PROG = 256 + BPF_F_ZERO_CSUM_TX = 2 + BPF_F_ZERO_SEED = 64 + BPF_LOAD_HDR_OPT_TCP_SYN = 1 + BPF_LOCAL_STORAGE_GET_F_CREATE = 1 + BPF_MAX_LOOPS = 8388608 + BPF_MAX_TRAMP_LINKS = 38 + BPF_NOEXIST = 1 + BPF_RB_AVAIL_DATA = 0 + BPF_RB_CONS_POS = 2 + BPF_RB_FORCE_WAKEUP = 2 + BPF_RB_NO_WAKEUP = 1 + BPF_RB_PROD_POS = 3 + BPF_RB_RING_SIZE = 1 + BPF_REG_0 = 0 + BPF_REG_1 = 1 + BPF_REG_10 = 10 + BPF_REG_2 = 2 + BPF_REG_3 = 3 + BPF_REG_4 = 4 + BPF_REG_5 = 5 + BPF_REG_6 = 6 + BPF_REG_7 = 7 + BPF_REG_8 = 8 + BPF_REG_9 = 9 + BPF_RINGBUF_BUSY_BIT = 2147483648 + BPF_RINGBUF_DISCARD_BIT = 1073741824 + BPF_RINGBUF_HDR_SZ = 8 + BPF_SKB_TSTAMP_DELIVERY_MONO = 1 + BPF_SKB_TSTAMP_UNSPEC = 0 + BPF_SK_LOOKUP_F_NO_REUSEPORT = 2 + BPF_SK_LOOKUP_F_REPLACE = 1 + BPF_SK_STORAGE_GET_F_CREATE = 1 + BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB = 4 + BPF_SOCK_OPS_ALL_CB_FLAGS = 127 + BPF_SOCK_OPS_BASE_RTT = 7 + BPF_SOCK_OPS_HDR_OPT_LEN_CB = 14 + BPF_SOCK_OPS_NEEDS_ECN = 6 + BPF_SOCK_OPS_PARSE_ALL_HDR_OPT_CB_FLAG = 16 + BPF_SOCK_OPS_PARSE_HDR_OPT_CB = 13 + BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG = 32 + BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB = 5 + BPF_SOCK_OPS_RETRANS_CB = 9 + BPF_SOCK_OPS_RETRANS_CB_FLAG = 2 + BPF_SOCK_OPS_RTO_CB = 8 + BPF_SOCK_OPS_RTO_CB_FLAG = 1 + BPF_SOCK_OPS_RTT_CB = 12 + BPF_SOCK_OPS_RTT_CB_FLAG = 8 + BPF_SOCK_OPS_RWND_INIT = 2 + BPF_SOCK_OPS_STATE_CB = 10 + BPF_SOCK_OPS_STATE_CB_FLAG = 4 + BPF_SOCK_OPS_TCP_CONNECT_CB = 3 + BPF_SOCK_OPS_TCP_LISTEN_CB = 11 + BPF_SOCK_OPS_TIMEOUT_INIT = 1 + BPF_SOCK_OPS_VOID = 0 + BPF_SOCK_OPS_WRITE_HDR_OPT_CB = 15 + BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG = 64 + BPF_STRUCT_OPS_TYPE_bpf_dummy_ops = 0 + BPF_STRUCT_OPS_TYPE_tcp_congestion_ops = 1 + BPF_TASK_ITER_ALL_PROCS = 0 + BPF_TASK_ITER_ALL_THREADS = 1 + BPF_TASK_ITER_PROC_THREADS = 2 + BPF_TCP_BOUND_INACTIVE = 13 + BPF_TCP_CLOSE = 7 + BPF_TCP_CLOSE_WAIT = 8 + BPF_TCP_CLOSING = 11 + BPF_TCP_ESTABLISHED = 1 + BPF_TCP_FIN_WAIT1 = 4 + BPF_TCP_FIN_WAIT2 = 5 + BPF_TCP_LAST_ACK = 9 + BPF_TCP_LISTEN = 10 + BPF_TCP_MAX_STATES = 14 + BPF_TCP_NEW_SYN_RECV = 12 + BPF_TCP_SYN_RECV = 3 + BPF_TCP_SYN_SENT = 2 + BPF_TCP_TIME_WAIT = 6 + BPF_WRITE_HDR_TCP_CURRENT_MSS = 1 + BPF_WRITE_HDR_TCP_SYNACK_COOKIE = 2 + BPF_XFRM_STATE_OPTS_SZ = 36 +) + type AdjRoomMode uint32 const ( @@ -402,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 ( @@ -537,7 +710,7 @@ type MapInfo struct { KeySize uint32 ValueSize uint32 MaxEntries uint32 - MapFlags MapFlags + MapFlags uint32 Name ObjName Ifindex uint32 BtfVmlinuxValueTypeId TypeID @@ -556,7 +729,7 @@ type ProgInfo struct { Tag [8]uint8 JitedProgLen uint32 XlatedProgLen uint32 - JitedProgInsns uint64 + JitedProgInsns Pointer XlatedProgInsns Pointer LoadTime uint64 CreatedByUid uint32 @@ -569,15 +742,15 @@ type ProgInfo struct { NetnsIno uint64 NrJitedKsyms uint32 NrJitedFuncLens uint32 - JitedKsyms uint64 - JitedFuncLens uint64 + JitedKsyms Pointer + JitedFuncLens Pointer BtfId BTFID FuncInfoRecSize uint32 FuncInfo Pointer NrFuncInfo uint32 NrLineInfo uint32 LineInfo Pointer - JitedLineInfo uint64 + JitedLineInfo Pointer NrJitedLineInfo uint32 LineInfoRecSize uint32 JitedLineInfoRecSize uint32 @@ -886,7 +1059,7 @@ type MapCreateAttr struct { KeySize uint32 ValueSize uint32 MaxEntries uint32 - MapFlags MapFlags + MapFlags uint32 InnerMapFd uint32 NumaNode uint32 MapName ObjName diff --git a/vendor/github.com/cilium/ebpf/internal/sysenc/buffer.go b/vendor/github.com/cilium/ebpf/internal/sysenc/buffer.go index d184ea196ae..1b4f052ee2b 100644 --- a/vendor/github.com/cilium/ebpf/internal/sysenc/buffer.go +++ b/vendor/github.com/cilium/ebpf/internal/sysenc/buffer.go @@ -51,12 +51,12 @@ func SyscallOutput(dst any, size int) Buffer { // // Returns the number of copied bytes. func (b Buffer) CopyTo(dst []byte) int { - return copy(dst, b.unsafeBytes()) + return copy(dst, b.Bytes()) } // AppendTo appends the buffer onto dst. func (b Buffer) AppendTo(dst []byte) []byte { - return append(dst, b.unsafeBytes()...) + return append(dst, b.Bytes()...) } // Pointer returns the location where a syscall should write. @@ -72,10 +72,12 @@ func (b Buffer) Unmarshal(data any) error { return nil } - return Unmarshal(data, b.unsafeBytes()) + return Unmarshal(data, b.Bytes()) } -func (b Buffer) unsafeBytes() []byte { +// Bytes returns the buffer as a byte slice. Returns nil if the Buffer was +// created using UnsafeBuffer or by zero-copy unmarshaling. +func (b Buffer) Bytes() []byte { if b.size == syscallPointerOnly { return nil } diff --git a/vendor/github.com/cilium/ebpf/internal/testutils/fdtrace/fd_trace.go b/vendor/github.com/cilium/ebpf/internal/testutils/fdtrace/fd_trace.go new file mode 100644 index 00000000000..562df2cc0c2 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/testutils/fdtrace/fd_trace.go @@ -0,0 +1,103 @@ +package fdtrace + +import ( + "bytes" + "fmt" + "os" + "runtime" + "sync" + "sync/atomic" +) + +// foundLeak is atomic since the GC may collect objects in parallel. +var foundLeak atomic.Bool + +func onLeakFD(fs *runtime.Frames) { + foundLeak.Store(true) + fmt.Fprintln(os.Stderr, "leaked fd created at:") + fmt.Fprintln(os.Stderr, formatFrames(fs)) +} + +// fds is a registry of all file descriptors wrapped into sys.fds that were +// created while an fd tracer was active. +var fds *sync.Map // map[int]*runtime.Frames + +// TraceFD associates raw with the current execution stack. +// +// skip controls how many entries of the stack the function should skip. +func TraceFD(raw int, skip int) { + if fds == nil { + return + } + + // Attempt to store the caller's stack for the given fd value. + // Panic if fds contains an existing stack for the fd. + old, exist := fds.LoadOrStore(raw, callersFrames(skip)) + if exist { + f := old.(*runtime.Frames) + panic(fmt.Sprintf("found existing stack for fd %d:\n%s", raw, formatFrames(f))) + } +} + +// ForgetFD removes any existing association for raw. +func ForgetFD(raw int) { + if fds != nil { + fds.Delete(raw) + } +} + +// LeakFD indicates that raw was leaked. +// +// Calling the function with a value that was not passed to [TraceFD] before +// is undefined. +func LeakFD(raw int) { + if fds == nil { + return + } + + // Invoke the fd leak callback. Calls LoadAndDelete to guarantee the callback + // is invoked at most once for one sys.FD allocation, runtime.Frames can only + // be unwound once. + f, ok := fds.LoadAndDelete(raw) + if ok { + onLeakFD(f.(*runtime.Frames)) + } +} + +// flushFrames removes all elements from fds and returns them as a slice. This +// deals with the fact that a runtime.Frames can only be unwound once using +// Next(). +func flushFrames() []*runtime.Frames { + var frames []*runtime.Frames + fds.Range(func(key, value any) bool { + frames = append(frames, value.(*runtime.Frames)) + fds.Delete(key) + return true + }) + return frames +} + +func callersFrames(skip int) *runtime.Frames { + c := make([]uintptr, 32) + + // Skip runtime.Callers and this function. + i := runtime.Callers(skip+2, c) + if i == 0 { + return nil + } + + return runtime.CallersFrames(c) +} + +// formatFrames formats a runtime.Frames as a human-readable string. +func formatFrames(fs *runtime.Frames) string { + var b bytes.Buffer + for { + f, more := fs.Next() + b.WriteString(fmt.Sprintf("\t%s+%#x\n\t\t%s:%d\n", f.Function, f.PC-f.Entry, f.File, f.Line)) + if !more { + break + } + } + return b.String() +} diff --git a/vendor/github.com/cilium/ebpf/internal/testutils/fdtrace/main.go b/vendor/github.com/cilium/ebpf/internal/testutils/fdtrace/main.go new file mode 100644 index 00000000000..c1f7b42d91d --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/testutils/fdtrace/main.go @@ -0,0 +1,31 @@ +package fdtrace + +import ( + "os" + "sync" +) + +type testingM interface { + Run() int +} + +// TestMain runs m with fd tracing enabled. +// +// The function calls [os.Exit] and does not return. +func TestMain(m testingM) { + fds = new(sync.Map) + + ret := m.Run() + + if fs := flushFrames(); len(fs) != 0 { + for _, f := range fs { + onLeakFD(f) + } + } + + if foundLeak.Load() { + ret = 99 + } + + os.Exit(ret) +} diff --git a/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go b/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go index 897740fec0c..062bef9ec37 100644 --- a/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go +++ b/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go @@ -12,6 +12,7 @@ import ( "syscall" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/unix" ) @@ -121,7 +122,7 @@ var getTracefsPath = sync.OnceValues(func() (string, error) { // RHEL/CentOS {"/sys/kernel/debug/tracing", unix.DEBUGFS_MAGIC}, } { - if fsType, err := internal.FSType(p.path); err == nil && fsType == p.fsType { + if fsType, err := linux.FSType(p.path); err == nil && fsType == p.fsType { return p.path, nil } } @@ -213,7 +214,10 @@ func NewEvent(args ProbeArgs) (*Event, error) { if err == nil { return nil, fmt.Errorf("trace event %s/%s: %w", args.Group, eventName, os.ErrExist) } - if err != nil && !errors.Is(err, os.ErrNotExist) { + if errors.Is(err, unix.EINVAL) { + return nil, fmt.Errorf("trace event %s/%s: %w (unknown symbol?)", args.Group, eventName, err) + } + if !errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("checking trace event %s/%s: %w", args.Group, eventName, err) } diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go index d725cfaa394..144e608d1c7 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go @@ -81,15 +81,16 @@ const ( SO_DETACH_BPF = linux.SO_DETACH_BPF SOL_SOCKET = linux.SOL_SOCKET SIGPROF = linux.SIGPROF + SIGUSR1 = linux.SIGUSR1 SIG_BLOCK = linux.SIG_BLOCK SIG_UNBLOCK = linux.SIG_UNBLOCK - EM_NONE = linux.EM_NONE - EM_BPF = linux.EM_BPF BPF_FS_MAGIC = linux.BPF_FS_MAGIC TRACEFS_MAGIC = linux.TRACEFS_MAGIC DEBUGFS_MAGIC = linux.DEBUGFS_MAGIC BPF_RB_NO_WAKEUP = linux.BPF_RB_NO_WAKEUP BPF_RB_FORCE_WAKEUP = linux.BPF_RB_FORCE_WAKEUP + AF_UNSPEC = linux.AF_UNSPEC + IFF_UP = linux.IFF_UP ) type Statfs_t = linux.Statfs_t diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go index 3ff8962716a..06cc3a09663 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go @@ -84,16 +84,17 @@ const ( SO_DETACH_BPF SOL_SOCKET SIGPROF + SIGUSR1 SIG_BLOCK SIG_UNBLOCK - EM_NONE - EM_BPF BPF_FS_MAGIC TRACEFS_MAGIC DEBUGFS_MAGIC BPF_RB_NO_WAKEUP BPF_RB_FORCE_WAKEUP BPF_F_LOCK + AF_UNSPEC + IFF_UP ) type Statfs_t struct { diff --git a/vendor/github.com/cilium/ebpf/internal/version.go b/vendor/github.com/cilium/ebpf/internal/version.go index acd4650af73..a230830b013 100644 --- a/vendor/github.com/cilium/ebpf/internal/version.go +++ b/vendor/github.com/cilium/ebpf/internal/version.go @@ -2,9 +2,6 @@ package internal import ( "fmt" - "sync" - - "github.com/cilium/ebpf/internal/unix" ) const ( @@ -78,30 +75,3 @@ func (v Version) Kernel() uint32 { // each other when overflowing 8 bits. return uint32(uint8(v[0]))<<16 | uint32(uint8(v[1]))<<8 | uint32(uint8(s)) } - -// KernelVersion returns the version of the currently running kernel. -var KernelVersion = sync.OnceValues(func() (Version, error) { - return detectKernelVersion() -}) - -// detectKernelVersion returns the version of the running kernel. -func detectKernelVersion() (Version, error) { - vc, err := vdsoVersion() - if err != nil { - return Version{}, err - } - return NewVersionFromCode(vc), nil -} - -// KernelRelease returns the release string of the running kernel. -// Its format depends on the Linux distribution and corresponds to directory -// names in /lib/modules by convention. Some examples are 5.15.17-1-lts and -// 4.19.0-16-amd64. -func KernelRelease() (string, error) { - var uname unix.Utsname - if err := unix.Uname(&uname); err != nil { - return "", fmt.Errorf("uname failed: %w", err) - } - - return unix.ByteSliceToString(uname.Release[:]), nil -} diff --git a/vendor/github.com/cilium/ebpf/link/kprobe.go b/vendor/github.com/cilium/ebpf/link/kprobe.go index fe3f17c3717..6f93a27a254 100644 --- a/vendor/github.com/cilium/ebpf/link/kprobe.go +++ b/vendor/github.com/cilium/ebpf/link/kprobe.go @@ -10,6 +10,7 @@ import ( "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" @@ -60,6 +61,9 @@ func (ko *KprobeOptions) cookie() uint64 { // platform's syscall prefix (e.g. __x64_) to support attaching to syscalls // in a portable fashion. // +// On kernels 6.11 and later, setting a kprobe on a nonexistent symbol using +// tracefs incorrectly returns [unix.EINVAL] instead of [os.ErrNotExist]. +// // The returned Link may implement [PerfEvent]. func Kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) { k, err := kprobe(symbol, prog, opts, false) @@ -91,7 +95,7 @@ func Kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error // in a portable fashion. // // On kernels 5.10 and earlier, setting a kretprobe on a nonexistent symbol -// incorrectly returns unix.EINVAL instead of os.ErrNotExist. +// incorrectly returns [unix.EINVAL] instead of [os.ErrNotExist]. // // The returned Link may implement [PerfEvent]. func Kretprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) { @@ -169,7 +173,7 @@ func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (* // Use kprobe PMU if the kernel has it available. tp, err := pmuProbe(args) if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { - if prefix := internal.PlatformPrefix(); prefix != "" { + if prefix := linux.PlatformPrefix(); prefix != "" { args.Symbol = prefix + symbol tp, err = pmuProbe(args) } @@ -177,7 +181,7 @@ func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (* if err == nil { return tp, nil } - if err != nil && !errors.Is(err, ErrNotSupported) { + if !errors.Is(err, ErrNotSupported) { return nil, fmt.Errorf("creating perf_kprobe PMU (arch-specific fallback for %q): %w", symbol, err) } @@ -185,7 +189,7 @@ func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (* args.Symbol = symbol tp, err = tracefsProbe(args) if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { - if prefix := internal.PlatformPrefix(); prefix != "" { + if prefix := linux.PlatformPrefix(); prefix != "" { args.Symbol = prefix + symbol tp, err = tracefsProbe(args) } diff --git a/vendor/github.com/cilium/ebpf/link/kprobe_multi.go b/vendor/github.com/cilium/ebpf/link/kprobe_multi.go index f7a8291f945..094cb0538cb 100644 --- a/vendor/github.com/cilium/ebpf/link/kprobe_multi.go +++ b/vendor/github.com/cilium/ebpf/link/kprobe_multi.go @@ -60,7 +60,7 @@ func KprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) { // // Requires at least Linux 5.18. func KretprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) { - return kprobeMulti(prog, opts, unix.BPF_F_KPROBE_MULTI_RETURN) + return kprobeMulti(prog, opts, sys.BPF_F_KPROBE_MULTI_RETURN) } func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Link, error) { @@ -126,7 +126,7 @@ type kprobeMultiLink struct { var _ Link = (*kprobeMultiLink)(nil) -func (kml *kprobeMultiLink) Update(prog *ebpf.Program) error { +func (kml *kprobeMultiLink) Update(_ *ebpf.Program) error { return fmt.Errorf("update kprobe_multi: %w", ErrNotSupported) } @@ -149,7 +149,7 @@ func (kml *kprobeMultiLink) Info() (*Info, error) { }, nil } -var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", "5.18", func() error { +var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "probe_kpm_link", Type: ebpf.Kprobe, @@ -188,4 +188,4 @@ var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", "5 fd.Close() return nil -}) +}, "5.18") diff --git a/vendor/github.com/cilium/ebpf/link/link.go b/vendor/github.com/cilium/ebpf/link/link.go index 9c34616c9a9..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 } @@ -380,7 +387,7 @@ func (l *RawLink) Close() error { // Calling Close on a pinned Link will not break the link // until the pin is removed. func (l *RawLink) Pin(fileName string) error { - if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil { + if err := sys.Pin(l.pinnedPath, fileName, l.fd); err != nil { return err } l.pinnedPath = fileName @@ -389,7 +396,7 @@ func (l *RawLink) Pin(fileName string) error { // Unpin implements the Link interface. func (l *RawLink) Unpin() error { - if err := internal.Unpin(l.pinnedPath); err != nil { + if err := sys.Unpin(l.pinnedPath); err != nil { return err } l.pinnedPath = "" diff --git a/vendor/github.com/cilium/ebpf/link/netfilter.go b/vendor/github.com/cilium/ebpf/link/netfilter.go index 34be3908597..9436d11df93 100644 --- a/vendor/github.com/cilium/ebpf/link/netfilter.go +++ b/vendor/github.com/cilium/ebpf/link/netfilter.go @@ -63,7 +63,7 @@ func AttachNetfilter(opts NetfilterOptions) (Link, error) { return &netfilterLink{RawLink{fd, ""}}, nil } -func (*netfilterLink) Update(new *ebpf.Program) error { +func (*netfilterLink) Update(_ *ebpf.Program) error { return fmt.Errorf("netfilter update: %w", ErrNotSupported) } diff --git a/vendor/github.com/cilium/ebpf/link/perf_event.go b/vendor/github.com/cilium/ebpf/link/perf_event.go index 1d8feb58c1c..7440e8b292a 100644 --- a/vendor/github.com/cilium/ebpf/link/perf_event.go +++ b/vendor/github.com/cilium/ebpf/link/perf_event.go @@ -115,7 +115,7 @@ func (pl *perfEventLink) Close() error { return nil } -func (pl *perfEventLink) Update(prog *ebpf.Program) error { +func (pl *perfEventLink) Update(_ *ebpf.Program) error { return fmt.Errorf("perf event link update: %w", ErrNotSupported) } @@ -185,7 +185,7 @@ func (pi *perfEventIoctl) isLink() {} // // Detaching a program from a perf event is currently not possible, so a // program replacement mechanism cannot be implemented for perf events. -func (pi *perfEventIoctl) Update(prog *ebpf.Program) error { +func (pi *perfEventIoctl) Update(_ *ebpf.Program) error { return fmt.Errorf("perf event ioctl update: %w", ErrNotSupported) } @@ -303,7 +303,7 @@ func openTracepointPerfEvent(tid uint64, pid int) (*sys.FD, error) { // // https://elixir.bootlin.com/linux/v5.16.8/source/kernel/bpf/syscall.c#L4307 // https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e -var haveBPFLinkPerfEvent = internal.NewFeatureTest("bpf_link_perf_event", "5.15", func() error { +var haveBPFLinkPerfEvent = internal.NewFeatureTest("bpf_link_perf_event", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "probe_bpf_perf_link", Type: ebpf.Kprobe, @@ -329,4 +329,4 @@ var haveBPFLinkPerfEvent = internal.NewFeatureTest("bpf_link_perf_event", "5.15" return nil } return err -}) +}, "5.15") diff --git a/vendor/github.com/cilium/ebpf/link/syscalls.go b/vendor/github.com/cilium/ebpf/link/syscalls.go index d09b5acb0f3..25951b017dd 100644 --- a/vendor/github.com/cilium/ebpf/link/syscalls.go +++ b/vendor/github.com/cilium/ebpf/link/syscalls.go @@ -30,7 +30,7 @@ const ( NetkitType = sys.BPF_LINK_TYPE_NETKIT ) -var haveProgAttach = internal.NewFeatureTest("BPF_PROG_ATTACH", "4.10", func() error { +var haveProgAttach = internal.NewFeatureTest("BPF_PROG_ATTACH", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.CGroupSKB, License: "MIT", @@ -48,9 +48,9 @@ var haveProgAttach = internal.NewFeatureTest("BPF_PROG_ATTACH", "4.10", func() e // have the syscall. prog.Close() return nil -}) +}, "4.10") -var haveProgAttachReplace = internal.NewFeatureTest("BPF_PROG_ATTACH atomic replacement of MULTI progs", "5.5", func() error { +var haveProgAttachReplace = internal.NewFeatureTest("BPF_PROG_ATTACH atomic replacement of MULTI progs", func() error { if err := haveProgAttach(); err != nil { return err } @@ -90,9 +90,9 @@ var haveProgAttachReplace = internal.NewFeatureTest("BPF_PROG_ATTACH atomic repl return nil } return err -}) +}, "5.5") -var haveBPFLink = internal.NewFeatureTest("bpf_link", "5.7", func() error { +var haveBPFLink = internal.NewFeatureTest("bpf_link", func() error { attr := sys.LinkCreateAttr{ // This is a hopefully invalid file descriptor, which triggers EBADF. TargetFd: ^uint32(0), @@ -107,9 +107,9 @@ var haveBPFLink = internal.NewFeatureTest("bpf_link", "5.7", func() error { return nil } return err -}) +}, "5.7") -var haveProgQuery = internal.NewFeatureTest("BPF_PROG_QUERY", "4.15", func() error { +var haveProgQuery = internal.NewFeatureTest("BPF_PROG_QUERY", func() error { attr := sys.ProgQueryAttr{ // We rely on this being checked during the syscall. // With an otherwise correct payload we expect EBADF here @@ -127,9 +127,9 @@ var haveProgQuery = internal.NewFeatureTest("BPF_PROG_QUERY", "4.15", func() err return ErrNotSupported } return errors.New("syscall succeeded unexpectedly") -}) +}, "4.15") -var haveTCX = internal.NewFeatureTest("tcx", "6.6", func() error { +var haveTCX = internal.NewFeatureTest("tcx", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.SchedCLS, License: "MIT", @@ -162,9 +162,9 @@ var haveTCX = internal.NewFeatureTest("tcx", "6.6", func() error { return ErrNotSupported } return errors.New("syscall succeeded unexpectedly") -}) +}, "6.6") -var haveNetkit = internal.NewFeatureTest("netkit", "6.7", func() error { +var haveNetkit = internal.NewFeatureTest("netkit", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.SchedCLS, License: "MIT", @@ -197,4 +197,4 @@ var haveNetkit = internal.NewFeatureTest("netkit", "6.7", func() error { return ErrNotSupported } return errors.New("syscall succeeded unexpectedly") -}) +}, "6.7") diff --git a/vendor/github.com/cilium/ebpf/link/tracing.go b/vendor/github.com/cilium/ebpf/link/tracing.go index 9e570afc96a..a461007f9f5 100644 --- a/vendor/github.com/cilium/ebpf/link/tracing.go +++ b/vendor/github.com/cilium/ebpf/link/tracing.go @@ -14,7 +14,7 @@ type tracing struct { RawLink } -func (f *tracing) Update(new *ebpf.Program) error { +func (f *tracing) Update(_ *ebpf.Program) error { return fmt.Errorf("tracing update: %w", ErrNotSupported) } diff --git a/vendor/github.com/cilium/ebpf/link/uprobe.go b/vendor/github.com/cilium/ebpf/link/uprobe.go index 194d1d319a7..1852a3fadd2 100644 --- a/vendor/github.com/cilium/ebpf/link/uprobe.go +++ b/vendor/github.com/cilium/ebpf/link/uprobe.go @@ -16,7 +16,7 @@ var ( uprobeRefCtrOffsetPMUPath = "/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset" // elixir.bootlin.com/linux/v5.15-rc7/source/kernel/events/core.c#L9799 uprobeRefCtrOffsetShift = 32 - haveRefCtrOffsetPMU = internal.NewFeatureTest("RefCtrOffsetPMU", "4.20", func() error { + haveRefCtrOffsetPMU = internal.NewFeatureTest("RefCtrOffsetPMU", func() error { _, err := os.Stat(uprobeRefCtrOffsetPMUPath) if errors.Is(err, os.ErrNotExist) { return internal.ErrNotSupported @@ -25,7 +25,7 @@ var ( return err } return nil - }) + }, "4.20") // ErrNoSymbol indicates that the given symbol was not found // in the ELF symbols table. @@ -321,7 +321,7 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti if err == nil { return tp, nil } - if err != nil && !errors.Is(err, ErrNotSupported) { + if !errors.Is(err, ErrNotSupported) { return nil, fmt.Errorf("creating perf_uprobe PMU: %w", err) } diff --git a/vendor/github.com/cilium/ebpf/link/uprobe_multi.go b/vendor/github.com/cilium/ebpf/link/uprobe_multi.go index aea807b329a..49dc18b4492 100644 --- a/vendor/github.com/cilium/ebpf/link/uprobe_multi.go +++ b/vendor/github.com/cilium/ebpf/link/uprobe_multi.go @@ -47,7 +47,7 @@ func (ex *Executable) UretprobeMulti(symbols []string, prog *ebpf.Program, opts // The return probe is not limited for symbols entry, so there's no special // setup for return uprobes (other than the extra flag). The symbols, opts.Offsets // and opts.Addresses arrays follow the same logic as for entry uprobes. - return ex.uprobeMulti(symbols, prog, opts, unix.BPF_F_UPROBE_MULTI_RETURN) + return ex.uprobeMulti(symbols, prog, opts, sys.BPF_F_UPROBE_MULTI_RETURN) } func (ex *Executable) uprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions, flags uint32) (Link, error) { @@ -99,8 +99,11 @@ func (ex *Executable) uprobeMulti(symbols []string, prog *ebpf.Program, opts *Up if errors.Is(err, unix.ESRCH) { return nil, fmt.Errorf("%w (specified pid not found?)", os.ErrNotExist) } + // Since Linux commit 46ba0e49b642 ("bpf: fix multi-uprobe PID filtering + // logic"), if the provided pid overflows MaxInt32 (turning it negative), the + // kernel will return EINVAL instead of ESRCH. if errors.Is(err, unix.EINVAL) { - return nil, fmt.Errorf("%w (missing symbol or prog's AttachType not AttachTraceUprobeMulti?)", err) + return nil, fmt.Errorf("%w (invalid pid, missing symbol or prog's AttachType not AttachTraceUprobeMulti?)", err) } if err != nil { @@ -168,11 +171,11 @@ type uprobeMultiLink struct { var _ Link = (*uprobeMultiLink)(nil) -func (kml *uprobeMultiLink) Update(prog *ebpf.Program) error { +func (kml *uprobeMultiLink) Update(_ *ebpf.Program) error { return fmt.Errorf("update uprobe_multi: %w", ErrNotSupported) } -var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", "6.6", func() error { +var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "probe_upm_link", Type: ebpf.Kprobe, @@ -213,4 +216,4 @@ var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", "6 // should not happen fd.Close() return errors.New("successfully attached uprobe_multi to /, kernel bug?") -}) +}, "6.6") diff --git a/vendor/github.com/cilium/ebpf/linker.go b/vendor/github.com/cilium/ebpf/linker.go index 788f21b7b6f..6f97af27840 100644 --- a/vendor/github.com/cilium/ebpf/linker.go +++ b/vendor/github.com/cilium/ebpf/linker.go @@ -9,10 +9,12 @@ import ( "io/fs" "math" "slices" + "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/kallsyms" ) // handles stores handle objects to avoid gc cleanup @@ -457,3 +459,42 @@ func resolveKconfigReferences(insns asm.Instructions) (_ *Map, err error) { return kconfig, nil } + +func resolveKsymReferences(insns asm.Instructions) error { + var missing []string + + iter := insns.Iterate() + for iter.Next() { + ins := iter.Ins + meta, _ := ins.Metadata.Get(ksymMetaKey{}).(*ksymMeta) + if meta == nil { + continue + } + + addr, err := kallsyms.Address(meta.Name) + if err != nil { + return fmt.Errorf("resolve ksym %s: %w", meta.Name, err) + } + if addr != 0 { + ins.Constant = int64(addr) + continue + } + + if meta.Binding == elf.STB_WEAK { + // A weak ksym variable in eBPF C means its resolution is optional. + // Set a zero constant explicitly for clarity. + ins.Constant = 0 + continue + } + + if !slices.Contains(missing, meta.Name) { + missing = append(missing, meta.Name) + } + } + + if len(missing) > 0 { + return fmt.Errorf("kernel is missing symbol: %s", strings.Join(missing, ",")) + } + + return nil +} diff --git a/vendor/github.com/cilium/ebpf/map.go b/vendor/github.com/cilium/ebpf/map.go index 0b62101c3cb..e5d8bd78095 100644 --- a/vendor/github.com/cilium/ebpf/map.go +++ b/vendor/github.com/cilium/ebpf/map.go @@ -66,16 +66,13 @@ type MapSpec struct { Pinning PinType // Specify numa node during map creation - // (effective only if unix.BPF_F_NUMA_NODE flag is set, + // (effective only if sys.BPF_F_NUMA_NODE flag is set, // which can be imported from golang.org/x/sys/unix) NumaNode uint32 // The initial contents of the map. May be nil. Contents []MapKV - // Whether to freeze a map after setting its initial contents. - Freeze bool - // InnerMap is used as a template for ArrayOfMaps and HashOfMaps InnerMap *MapSpec @@ -161,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 @@ -190,6 +198,14 @@ func (ms *MapSpec) dataSection() ([]byte, *btf.Datasec, error) { return value, ds, nil } +func (ms *MapSpec) readOnly() bool { + return (ms.Flags & sys.BPF_F_RDONLY_PROG) > 0 +} + +func (ms *MapSpec) writeOnly() bool { + return (ms.Flags & sys.BPF_F_WRONLY_PROG) > 0 +} + // MapKV is used to initialize the contents of a Map. type MapKV struct { Key interface{} @@ -222,7 +238,7 @@ func (ms *MapSpec) Compatible(m *Map) error { // BPF_F_RDONLY_PROG is set unconditionally for devmaps. Explicitly allow this // mismatch. - if !((ms.Type == DevMap || ms.Type == DevMapHash) && m.flags^ms.Flags == unix.BPF_F_RDONLY_PROG) && + if !((ms.Type == DevMap || ms.Type == DevMapHash) && m.flags^ms.Flags == sys.BPF_F_RDONLY_PROG) && m.flags != ms.Flags { diffs = append(diffs, fmt.Sprintf("Flags: %d changed to %d", m.flags, ms.Flags)) } @@ -254,6 +270,8 @@ type Map struct { pinnedPath string // Per CPU maps return values larger than the size in the spec fullValueSize int + + memory *Memory } // NewMapFromFD creates a map from a raw fd. @@ -359,7 +377,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) { return nil, errors.New("inner maps cannot be pinned") } - template, err := spec.InnerMap.createMap(nil, opts) + template, err := spec.InnerMap.createMap(nil) if err != nil { return nil, fmt.Errorf("inner map: %w", err) } @@ -371,7 +389,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) { innerFd = template.fd } - m, err := spec.createMap(innerFd, opts) + m, err := spec.createMap(innerFd) if err != nil { return nil, err } @@ -387,9 +405,54 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) { return m, nil } +// Memory returns a memory-mapped region for the Map. The Map must have been +// created with the BPF_F_MMAPABLE flag. Repeated calls to Memory return the +// same mapping. Callers are responsible for coordinating access to Memory. +func (m *Map) Memory() (*Memory, error) { + if m.memory != nil { + return m.memory, nil + } + + if m.flags&sys.BPF_F_MMAPABLE == 0 { + return nil, fmt.Errorf("Map was not created with the BPF_F_MMAPABLE flag: %w", ErrNotSupported) + } + + size, err := m.memorySize() + if err != nil { + return nil, err + } + + mm, err := newMemory(m.FD(), size) + if err != nil { + return nil, fmt.Errorf("creating new Memory: %w", err) + } + + m.memory = mm + + return mm, nil +} + +func (m *Map) memorySize() (int, error) { + switch m.Type() { + case Array: + // In Arrays, values are always laid out on 8-byte boundaries regardless of + // architecture. Multiply by MaxEntries and align the result to the host's + // page size. + size := int(internal.Align(m.ValueSize(), 8) * m.MaxEntries()) + size = internal.Align(size, os.Getpagesize()) + return size, nil + case Arena: + // For Arenas, MaxEntries denotes the maximum number of pages available to + // the arena. + return int(m.MaxEntries()) * os.Getpagesize(), nil + } + + return 0, fmt.Errorf("determine memory size of map type %s: %w", m.Type(), ErrNotSupported) +} + // createMap validates the spec's properties and creates the map in the kernel // using the given opts. It does not populate or freeze the map. -func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions) (_ *Map, err error) { +func (spec *MapSpec) createMap(inner *sys.FD) (_ *Map, err error) { closeOnError := func(closer io.Closer) { if err != nil { closer.Close() @@ -416,7 +479,7 @@ func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions) (_ *Map, err erro KeySize: spec.KeySize, ValueSize: spec.ValueSize, MaxEntries: spec.MaxEntries, - MapFlags: sys.MapFlags(spec.Flags), + MapFlags: spec.Flags, NumaNode: spec.NumaNode, } @@ -474,32 +537,32 @@ func handleMapCreateError(attr sys.MapCreateAttr, spec *MapSpec, err error) erro if errors.Is(err, unix.EINVAL) && spec.Type == UnspecifiedMap { return fmt.Errorf("map create: cannot use type %s", UnspecifiedMap) } - if errors.Is(err, unix.EINVAL) && spec.Flags&unix.BPF_F_NO_PREALLOC > 0 { + if errors.Is(err, unix.EINVAL) && spec.Flags&sys.BPF_F_NO_PREALLOC > 0 { return fmt.Errorf("map create: %w (noPrealloc flag may be incompatible with map type %s)", err, spec.Type) } - switch spec.Type { - case ArrayOfMaps, HashOfMaps: + if spec.Type.canStoreMap() { if haveFeatErr := haveNestedMaps(); haveFeatErr != nil { return fmt.Errorf("map create: %w", haveFeatErr) } } - if spec.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze { + + if spec.readOnly() || spec.writeOnly() { if haveFeatErr := haveMapMutabilityModifiers(); haveFeatErr != nil { return fmt.Errorf("map create: %w", haveFeatErr) } } - if spec.Flags&unix.BPF_F_MMAPABLE > 0 { + if spec.Flags&sys.BPF_F_MMAPABLE > 0 { if haveFeatErr := haveMmapableMaps(); haveFeatErr != nil { return fmt.Errorf("map create: %w", haveFeatErr) } } - if spec.Flags&unix.BPF_F_INNER_MAP > 0 { + if spec.Flags&sys.BPF_F_INNER_MAP > 0 { if haveFeatErr := haveInnerMaps(); haveFeatErr != nil { return fmt.Errorf("map create: %w", haveFeatErr) } } - if spec.Flags&unix.BPF_F_NO_PREALLOC > 0 { + if spec.Flags&sys.BPF_F_NO_PREALLOC > 0 { if haveFeatErr := haveNoPreallocMaps(); haveFeatErr != nil { return fmt.Errorf("map create: %w", haveFeatErr) } @@ -530,6 +593,7 @@ func newMap(fd *sys.FD, name string, typ MapType, keySize, valueSize, maxEntries flags, "", int(valueSize), + nil, } if !typ.hasPerCPUValue() { @@ -577,7 +641,12 @@ func (m *Map) Flags() uint32 { return m.flags } -// Info returns metadata about the map. +// Info returns metadata about the map. This was first introduced in Linux 4.5, +// but newer kernels support more MapInfo fields with the introduction of more +// features. See [MapInfo] and its methods for more details. +// +// Returns an error wrapping ErrNotSupported if the kernel supports neither +// BPF_OBJ_GET_INFO_BY_FD nor reading map information from /proc/self/fdinfo. func (m *Map) Info() (*MapInfo, error) { return newMapInfoFromFd(m.fd) } @@ -604,7 +673,7 @@ func (m *Map) Handle() (*btf.Handle, error) { type MapLookupFlags uint64 // LookupLock look up the value of a spin-locked map. -const LookupLock MapLookupFlags = unix.BPF_F_LOCK +const LookupLock MapLookupFlags = sys.BPF_F_LOCK // Lookup retrieves a value from a Map. // @@ -1336,6 +1405,7 @@ func (m *Map) Clone() (*Map, error) { m.flags, "", m.fullValueSize, + nil, }, nil } @@ -1349,7 +1419,7 @@ func (m *Map) Clone() (*Map, error) { // This requires bpffs to be mounted above fileName. // See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd func (m *Map) Pin(fileName string) error { - if err := internal.Pin(m.pinnedPath, fileName, m.fd); err != nil { + if err := sys.Pin(m.pinnedPath, fileName, m.fd); err != nil { return err } m.pinnedPath = fileName @@ -1362,7 +1432,7 @@ func (m *Map) Pin(fileName string) error { // // Unpinning an unpinned Map returns nil. func (m *Map) Unpin() error { - if err := internal.Unpin(m.pinnedPath); err != nil { + if err := sys.Unpin(m.pinnedPath); err != nil { return err } m.pinnedPath = "" @@ -1400,7 +1470,7 @@ func (m *Map) finalize(spec *MapSpec) error { } } - if spec.Freeze { + if isConstantDataSection(spec.Name) || isKconfigSection(spec.Name) { if err := m.Freeze(); err != nil { return fmt.Errorf("freezing map: %w", err) } @@ -1501,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(), }) @@ -1511,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 @@ -1530,6 +1607,10 @@ func unmarshalMap(buf sysenc.Buffer) (*Map, error) { // marshalMap marshals the fd of a map into a buffer in host endianness. func marshalMap(m *Map, length int) ([]byte, error) { + if m == nil { + return nil, errors.New("can't marshal a nil Map") + } + if length != 4 { return nil, fmt.Errorf("can't marshal map to %d bytes", length) } diff --git a/vendor/github.com/cilium/ebpf/memory.go b/vendor/github.com/cilium/ebpf/memory.go new file mode 100644 index 00000000000..312c967131a --- /dev/null +++ b/vendor/github.com/cilium/ebpf/memory.go @@ -0,0 +1,145 @@ +package ebpf + +import ( + "errors" + "fmt" + "io" + "runtime" + + "github.com/cilium/ebpf/internal/unix" +) + +// Memory is the building block for accessing the memory of specific bpf map +// types (Array and Arena at the time of writing) without going through the bpf +// syscall interface. +// +// Given the fd of a bpf map created with the BPF_F_MMAPABLE flag, a shared +// 'file'-based memory-mapped region can be allocated in the process' address +// space, exposing the bpf map's memory by simply accessing a memory location. + +var ErrReadOnly = errors.New("resource is read-only") + +// Memory implements accessing a Map's memory without making any syscalls. +// Pay attention to the difference between Go and C struct alignment rules. Use +// [structs.HostLayout] on supported Go versions to help with alignment. +// +// Note on memory coherence: avoid using packed structs in memory shared between +// user space and eBPF C programs. This drops a struct's memory alignment to 1, +// forcing the compiler to use single-byte loads and stores for field accesses. +// This may lead to partially-written data to be observed from user space. +// +// On most architectures, the memmove implementation used by Go's copy() will +// access data in word-sized chunks. If paired with a matching access pattern on +// the eBPF C side (and if using default memory alignment), accessing shared +// memory without atomics or other synchronization primitives should be sound +// for individual values. For accesses beyond a single value, the usual +// concurrent programming rules apply. +type Memory struct { + b []byte + ro bool +} + +func newMemory(fd, size int) (*Memory, error) { + // Typically, maps created with BPF_F_RDONLY_PROG remain writable from user + // space until frozen. As a security precaution, the kernel doesn't allow + // mapping bpf map memory as read-write into user space if the bpf map was + // frozen, or if it was created using the RDONLY_PROG flag. + // + // The user would be able to write to the map after freezing (since the kernel + // can't change the protection mode of an already-mapped page), while the + // verifier assumes the contents to be immutable. + b, err := unix.Mmap(fd, 0, size, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED) + + // If the map is frozen when an rw mapping is requested, expect EPERM. If the + // map was created with BPF_F_RDONLY_PROG, expect EACCES. + var ro bool + if errors.Is(err, unix.EPERM) || errors.Is(err, unix.EACCES) { + ro = true + b, err = unix.Mmap(fd, 0, size, unix.PROT_READ, unix.MAP_SHARED) + } + if err != nil { + return nil, fmt.Errorf("setting up memory-mapped region: %w", err) + } + + mm := &Memory{ + b, + ro, + } + runtime.SetFinalizer(mm, (*Memory).close) + + return mm, nil +} + +func (mm *Memory) close() { + if err := unix.Munmap(mm.b); err != nil { + panic(fmt.Errorf("unmapping memory: %w", err)) + } + mm.b = nil +} + +// Size returns the size of the memory-mapped region in bytes. +func (mm *Memory) Size() int { + return len(mm.b) +} + +// ReadOnly returns true if the memory-mapped region is read-only. +func (mm *Memory) ReadOnly() bool { + return mm.ro +} + +// bounds returns true if an access at off of the given size is within bounds. +func (mm *Memory) bounds(off uint64, size uint64) bool { + return off+size < uint64(len(mm.b)) +} + +// ReadAt implements [io.ReaderAt]. Useful for creating a new [io.OffsetWriter]. +// +// See [Memory] for details around memory coherence. +func (mm *Memory) ReadAt(p []byte, off int64) (int, error) { + if mm.b == nil { + return 0, fmt.Errorf("memory-mapped region closed") + } + + if p == nil { + return 0, fmt.Errorf("input buffer p is nil") + } + + if off < 0 || off >= int64(len(mm.b)) { + return 0, fmt.Errorf("read offset out of range") + } + + n := copy(p, mm.b[off:]) + if n < len(p) { + return n, io.EOF + } + + return n, nil +} + +// WriteAt implements [io.WriterAt]. Useful for creating a new +// [io.SectionReader]. +// +// See [Memory] for details around memory coherence. +func (mm *Memory) WriteAt(p []byte, off int64) (int, error) { + if mm.b == nil { + return 0, fmt.Errorf("memory-mapped region closed") + } + if mm.ro { + return 0, fmt.Errorf("memory-mapped region not writable: %w", ErrReadOnly) + } + + if p == nil { + return 0, fmt.Errorf("output buffer p is nil") + } + + if off < 0 || off >= int64(len(mm.b)) { + return 0, fmt.Errorf("write offset out of range") + } + + n := copy(mm.b[off:], p) + if n < len(p) { + return n, io.EOF + } + + return n, nil +} diff --git a/vendor/github.com/cilium/ebpf/prog.go b/vendor/github.com/cilium/ebpf/prog.go index 9bc6325f887..3767f28736a 100644 --- a/vendor/github.com/cilium/ebpf/prog.go +++ b/vendor/github.com/cilium/ebpf/prog.go @@ -16,6 +16,7 @@ import ( "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/kallsyms" + "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/sysenc" "github.com/cilium/ebpf/internal/unix" @@ -46,14 +47,15 @@ const ( outputPad = 256 + 2 ) -// Deprecated: the correct log size is now detected automatically and this -// constant is unused. -const DefaultVerifierLogSize = 64 * 1024 - // minVerifierLogSize is the default number of bytes allocated for the // 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. @@ -73,9 +75,10 @@ type ProgramOptions struct { // attempt at loading the program. LogLevel LogLevel - // Deprecated: the correct log buffer size is determined automatically - // and this field is ignored. - LogSize int + // 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 @@ -162,26 +165,35 @@ func (ps *ProgramSpec) Tag() (string, error) { return ps.Instructions.Tag(internal.NativeEndian) } -// KernelModule returns the kernel module, if any, the AttachTo function is contained in. -func (ps *ProgramSpec) KernelModule() (string, error) { +// kernelModule returns the kernel module providing the symbol in +// ProgramSpec.AttachTo, if any. Returns an empty string if the symbol is not +// present or not part of a kernel module. +func (ps *ProgramSpec) kernelModule() (string, error) { + if ps.AttachTo == "" && ps.targetsKernelModule() { + return kallsyms.Module(ps.AttachTo) + } + + return "", nil +} + +// targetsKernelModule returns true if the program supports being attached to a +// symbol provided by a kernel module. +func (ps *ProgramSpec) targetsKernelModule() bool { if ps.AttachTo == "" { - return "", nil + return false } switch ps.Type { - default: - return "", nil case Tracing: switch ps.AttachType { - default: - return "", nil - case AttachTraceFEntry: - case AttachTraceFExit: + case AttachTraceFEntry, AttachTraceFExit: + return true } - fallthrough case Kprobe: - return kallsyms.KernelModule(ps.AttachTo) + return true } + + return false } // VerifierError is returned by [NewProgram] and [NewProgramWithOptions] if a @@ -261,7 +273,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er // Overwrite Kprobe program version if set to zero or the magic version constant. kv := spec.KernelVersion if spec.Type == Kprobe && (kv == 0 || kv == internal.MagicKernelVersion) { - v, err := internal.KernelVersion() + v, err := linux.KernelVersion() if err != nil { return nil, fmt.Errorf("detecting kernel version: %w", err) } @@ -283,7 +295,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er insns := make(asm.Instructions, len(spec.Instructions)) copy(insns, spec.Instructions) - kmodName, err := spec.KernelModule() + kmodName, err := spec.kernelModule() if err != nil { return nil, fmt.Errorf("kernel module search: %w", err) } @@ -344,6 +356,10 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er } defer kconfig.Close() + if err := resolveKsymReferences(insns); err != nil { + return nil, fmt.Errorf("resolve .ksyms: %w", err) + } + if err := fixupAndValidate(insns); err != nil { return nil, err } @@ -395,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) @@ -431,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. @@ -462,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 @@ -598,7 +620,7 @@ func (p *Program) Clone() (*Program, error) { // This requires bpffs to be mounted above fileName. // See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd func (p *Program) Pin(fileName string) error { - if err := internal.Pin(p.pinnedPath, fileName, p.fd); err != nil { + if err := sys.Pin(p.pinnedPath, fileName, p.fd); err != nil { return err } p.pinnedPath = fileName @@ -611,7 +633,7 @@ func (p *Program) Pin(fileName string) error { // // Unpinning an unpinned Program returns nil. func (p *Program) Unpin() error { - if err := internal.Unpin(p.pinnedPath); err != nil { + if err := sys.Unpin(p.pinnedPath); err != nil { return err } p.pinnedPath = "" @@ -699,6 +721,10 @@ func (p *Program) Test(in []byte) (uint32, []byte, error) { // // Note: the same restrictions from Test apply. func (p *Program) Run(opts *RunOptions) (uint32, error) { + if opts == nil { + opts = &RunOptions{} + } + ret, _, err := p.run(opts) if err != nil { return ret, fmt.Errorf("run program: %w", err) @@ -732,7 +758,7 @@ func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.D return ret, total, nil } -var haveProgRun = internal.NewFeatureTest("BPF_PROG_RUN", "4.12", func() error { +var haveProgRun = internal.NewFeatureTest("BPF_PROG_RUN", func() error { prog, err := NewProgram(&ProgramSpec{ // SocketFilter does not require privileges on newer kernels. Type: SocketFilter, @@ -774,7 +800,7 @@ var haveProgRun = internal.NewFeatureTest("BPF_PROG_RUN", "4.12", func() error { } return err -}) +}, "4.12") func (p *Program) run(opts *RunOptions) (uint32, time.Duration, error) { if uint(len(opts.Data)) > math.MaxUint32 { @@ -883,6 +909,10 @@ func unmarshalProgram(buf sysenc.Buffer) (*Program, error) { } func marshalProgram(p *Program, length int) ([]byte, error) { + if p == nil { + return nil, errors.New("can't marshal a nil Program") + } + if length != 4 { return nil, fmt.Errorf("can't marshal program to %d bytes", length) } @@ -892,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(), }) @@ -904,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/syscalls.go b/vendor/github.com/cilium/ebpf/syscalls.go index 4aef7faebc8..25c84c3c5c1 100644 --- a/vendor/github.com/cilium/ebpf/syscalls.go +++ b/vendor/github.com/cilium/ebpf/syscalls.go @@ -10,6 +10,7 @@ import ( "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" @@ -60,7 +61,7 @@ func progLoad(insns asm.Instructions, typ ProgramType, license string) (*sys.FD, }) } -var haveNestedMaps = internal.NewFeatureTest("nested maps", "4.12", func() error { +var haveNestedMaps = internal.NewFeatureTest("nested maps", func() error { _, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(ArrayOfMaps), KeySize: 4, @@ -76,9 +77,9 @@ var haveNestedMaps = internal.NewFeatureTest("nested maps", "4.12", func() error return nil } return err -}) +}, "4.12") -var haveMapMutabilityModifiers = internal.NewFeatureTest("read- and write-only maps", "5.2", func() error { +var haveMapMutabilityModifiers = internal.NewFeatureTest("read- and write-only maps", func() error { // This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since // BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check. m, err := sys.MapCreate(&sys.MapCreateAttr{ @@ -86,39 +87,39 @@ var haveMapMutabilityModifiers = internal.NewFeatureTest("read- and write-only m KeySize: 4, ValueSize: 4, MaxEntries: 1, - MapFlags: unix.BPF_F_RDONLY_PROG, + MapFlags: sys.BPF_F_RDONLY_PROG, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil -}) +}, "5.2") -var haveMmapableMaps = internal.NewFeatureTest("mmapable maps", "5.5", func() error { +var haveMmapableMaps = internal.NewFeatureTest("mmapable maps", func() error { // This checks BPF_F_MMAPABLE, which appeared in 5.5 for array maps. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, - MapFlags: unix.BPF_F_MMAPABLE, + MapFlags: sys.BPF_F_MMAPABLE, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil -}) +}, "5.5") -var haveInnerMaps = internal.NewFeatureTest("inner maps", "5.10", func() error { +var haveInnerMaps = internal.NewFeatureTest("inner maps", func() error { // This checks BPF_F_INNER_MAP, which appeared in 5.10. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, - MapFlags: unix.BPF_F_INNER_MAP, + MapFlags: sys.BPF_F_INNER_MAP, }) if err != nil { @@ -126,16 +127,16 @@ var haveInnerMaps = internal.NewFeatureTest("inner maps", "5.10", func() error { } _ = m.Close() return nil -}) +}, "5.10") -var haveNoPreallocMaps = internal.NewFeatureTest("prealloc maps", "4.6", func() error { +var haveNoPreallocMaps = internal.NewFeatureTest("prealloc maps", func() error { // This checks BPF_F_NO_PREALLOC, which appeared in 4.6. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Hash), KeySize: 4, ValueSize: 4, MaxEntries: 1, - MapFlags: unix.BPF_F_NO_PREALLOC, + MapFlags: sys.BPF_F_NO_PREALLOC, }) if err != nil { @@ -143,7 +144,7 @@ var haveNoPreallocMaps = internal.NewFeatureTest("prealloc maps", "4.6", func() } _ = m.Close() return nil -}) +}, "4.6") func wrapMapError(err error) error { if err == nil { @@ -169,7 +170,7 @@ func wrapMapError(err error) error { return err } -var haveObjName = internal.NewFeatureTest("object names", "4.15", func() error { +var haveObjName = internal.NewFeatureTest("object names", func() error { attr := sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, @@ -178,16 +179,24 @@ var haveObjName = internal.NewFeatureTest("object names", "4.15", func() error { MapName: sys.NewObjName("feature_test"), } + // Tolerate EPERM as this runs during ELF loading which is potentially + // unprivileged. Only EINVAL is conclusive, thrown from CHECK_ATTR. fd, err := sys.MapCreate(&attr) - if err != nil { + if errors.Is(err, unix.EPERM) { + return nil + } + if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } + if err != nil { + return err + } _ = fd.Close() return nil -}) +}, "4.15") -var objNameAllowsDot = internal.NewFeatureTest("dot in object names", "5.2", func() error { +var objNameAllowsDot = internal.NewFeatureTest("dot in object names", func() error { if err := haveObjName(); err != nil { return err } @@ -200,16 +209,25 @@ var objNameAllowsDot = internal.NewFeatureTest("dot in object names", "5.2", fun MapName: sys.NewObjName(".test"), } + // Tolerate EPERM, otherwise MapSpec.Name has its dots removed when run by + // unprivileged tools. (bpf2go, other code gen). Only EINVAL is conclusive, + // thrown from bpf_obj_name_cpy(). fd, err := sys.MapCreate(&attr) - if err != nil { + if errors.Is(err, unix.EPERM) { + return nil + } + if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } + if err != nil { + return err + } _ = fd.Close() return nil -}) +}, "5.2") -var haveBatchAPI = internal.NewFeatureTest("map batch api", "5.6", func() error { +var haveBatchAPI = internal.NewFeatureTest("map batch api", func() error { var maxEntries uint32 = 2 attr := sys.MapCreateAttr{ MapType: sys.MapType(Hash), @@ -239,9 +257,9 @@ var haveBatchAPI = internal.NewFeatureTest("map batch api", "5.6", func() error return internal.ErrNotSupported } return nil -}) +}, "5.6") -var haveProbeReadKernel = internal.NewFeatureTest("bpf_probe_read_kernel", "5.5", func() error { +var haveProbeReadKernel = internal.NewFeatureTest("bpf_probe_read_kernel", func() error { insns := asm.Instructions{ asm.Mov.Reg(asm.R1, asm.R10), asm.Add.Imm(asm.R1, -8), @@ -257,9 +275,9 @@ var haveProbeReadKernel = internal.NewFeatureTest("bpf_probe_read_kernel", "5.5" } _ = fd.Close() return nil -}) +}, "5.5") -var haveBPFToBPFCalls = internal.NewFeatureTest("bpf2bpf calls", "4.16", func() error { +var haveBPFToBPFCalls = internal.NewFeatureTest("bpf2bpf calls", func() error { insns := asm.Instructions{ asm.Call.Label("prog2").WithSymbol("prog1"), asm.Return(), @@ -273,10 +291,10 @@ var haveBPFToBPFCalls = internal.NewFeatureTest("bpf2bpf calls", "4.16", func() } _ = fd.Close() return nil -}) +}, "4.16") -var haveSyscallWrapper = internal.NewFeatureTest("syscall wrapper", "4.17", func() error { - prefix := internal.PlatformPrefix() +var haveSyscallWrapper = internal.NewFeatureTest("syscall wrapper", func() error { + prefix := linux.PlatformPrefix() if prefix == "" { return fmt.Errorf("unable to find the platform prefix for (%s)", runtime.GOARCH) } @@ -302,9 +320,9 @@ var haveSyscallWrapper = internal.NewFeatureTest("syscall wrapper", "4.17", func } return evt.Close() -}) +}, "4.17") -var haveProgramExtInfos = internal.NewFeatureTest("program ext_infos", "5.0", func() error { +var haveProgramExtInfos = internal.NewFeatureTest("program ext_infos", func() error { insns := asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), @@ -334,4 +352,4 @@ var haveProgramExtInfos = internal.NewFeatureTest("program ext_infos", "5.0", fu } return err -}) +}, "5.0") diff --git a/vendor/github.com/cilium/ebpf/types.go b/vendor/github.com/cilium/ebpf/types.go index 542c2397cab..211b308bbc7 100644 --- a/vendor/github.com/cilium/ebpf/types.go +++ b/vendor/github.com/cilium/ebpf/types.go @@ -2,7 +2,6 @@ package ebpf import ( "github.com/cilium/ebpf/internal/sys" - "github.com/cilium/ebpf/internal/unix" ) //go:generate go run golang.org/x/tools/cmd/stringer@latest -output types_string.go -type=MapType,ProgramType,PinType @@ -95,6 +94,14 @@ const ( InodeStorage // TaskStorage - Specialized local storage map for task_struct. TaskStorage + // BloomFilter - Space-efficient data structure to quickly test whether an element exists in a set. + BloomFilter + // UserRingbuf - The reverse of RingBuf, used to send messages from user space to BPF programs. + UserRingbuf + // CgroupStorage - Store data keyed on a cgroup. If the cgroup disappears, the key is automatically removed. + CgroupStorage + // Arena - Sparse shared memory region between a BPF program and user space. + Arena ) // hasPerCPUValue returns true if the Map stores a value per CPU. @@ -120,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 @@ -263,10 +285,10 @@ func (lpo *LoadPinOptions) Marshal() uint32 { flags := lpo.Flags if lpo.ReadOnly { - flags |= unix.BPF_F_RDONLY + flags |= sys.BPF_F_RDONLY } if lpo.WriteOnly { - flags |= unix.BPF_F_WRONLY + flags |= sys.BPF_F_WRONLY } return flags } diff --git a/vendor/github.com/cilium/ebpf/types_string.go b/vendor/github.com/cilium/ebpf/types_string.go index ee60b5be5b6..f06685112c2 100644 --- a/vendor/github.com/cilium/ebpf/types_string.go +++ b/vendor/github.com/cilium/ebpf/types_string.go @@ -38,11 +38,15 @@ func _() { _ = x[RingBuf-27] _ = x[InodeStorage-28] _ = x[TaskStorage-29] + _ = x[BloomFilter-30] + _ = x[UserRingbuf-31] + _ = x[CgroupStorage-32] + _ = x[Arena-33] } -const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStorage" +const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStorageBloomFilterUserRingbufCgroupStorageArena" -var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 260, 267, 279, 290} +var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 260, 267, 279, 290, 301, 312, 325, 330} func (i MapType) String() string { if i >= MapType(len(_MapType_index)-1) { diff --git a/vendor/github.com/cilium/ebpf/variable.go b/vendor/github.com/cilium/ebpf/variable.go new file mode 100644 index 00000000000..288b173a115 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/variable.go @@ -0,0 +1,230 @@ +package ebpf + +import ( + "fmt" + "io" + + "github.com/cilium/ebpf/btf" + "github.com/cilium/ebpf/internal/sysenc" +) + +// VariableSpec is a convenience wrapper for modifying global variables of a +// CollectionSpec before loading it into the kernel. +// +// All operations on a VariableSpec's underlying MapSpec are performed in the +// host's native endianness. +type VariableSpec struct { + name string + offset uint64 + size uint64 + + m *MapSpec + t *btf.Var +} + +// Set sets the value of the VariableSpec to the provided input using the host's +// native endianness. +func (s *VariableSpec) Set(in any) error { + buf, err := sysenc.Marshal(in, int(s.size)) + if err != nil { + return fmt.Errorf("marshaling value %s: %w", s.name, err) + } + + b, _, err := s.m.dataSection() + if err != nil { + return fmt.Errorf("getting data section of map %s: %w", s.m.Name, err) + } + + if int(s.offset+s.size) > len(b) { + return fmt.Errorf("offset %d(+%d) for variable %s is out of bounds", s.offset, s.size, s.name) + } + + // MapSpec.Copy() performs a shallow copy. Fully copy the byte slice + // to avoid any changes affecting other copies of the MapSpec. + cpy := make([]byte, len(b)) + copy(cpy, b) + + buf.CopyTo(cpy[s.offset : s.offset+s.size]) + + s.m.Contents[0] = MapKV{Key: uint32(0), Value: cpy} + + return nil +} + +// Get writes the value of the VariableSpec to the provided output using the +// host's native endianness. +func (s *VariableSpec) Get(out any) error { + b, _, err := s.m.dataSection() + if err != nil { + return fmt.Errorf("getting data section of map %s: %w", s.m.Name, err) + } + + if int(s.offset+s.size) > len(b) { + return fmt.Errorf("offset %d(+%d) for variable %s is out of bounds", s.offset, s.size, s.name) + } + + if err := sysenc.Unmarshal(out, b[s.offset:s.offset+s.size]); err != nil { + return fmt.Errorf("unmarshaling value: %w", err) + } + + return nil +} + +// Size returns the size of the variable in bytes. +func (s *VariableSpec) Size() uint64 { + return s.size +} + +// MapName returns the name of the underlying MapSpec. +func (s *VariableSpec) MapName() string { + return s.m.Name +} + +// Offset returns the offset of the variable in the underlying MapSpec. +func (s *VariableSpec) Offset() uint64 { + return s.offset +} + +// Constant returns true if the VariableSpec represents a variable that is +// read-only from the perspective of the BPF program. +func (s *VariableSpec) Constant() bool { + return s.m.readOnly() +} + +// Type returns the [btf.Var] representing the variable in its data section. +// This is useful for inspecting the variable's decl tags and the type +// information of the inner type. +// +// Returns nil if the original ELF object did not contain BTF information. +func (s *VariableSpec) Type() *btf.Var { + return s.t +} + +func (s *VariableSpec) String() string { + return fmt.Sprintf("%s (type=%v, map=%s, offset=%d, size=%d)", s.name, s.t, s.m.Name, s.offset, s.size) +} + +// copy returns a new VariableSpec with the same values as the original, +// but with a different underlying MapSpec. This is useful when copying a +// CollectionSpec. Returns nil if a MapSpec with the same name is not found. +func (s *VariableSpec) copy(cpy *CollectionSpec) *VariableSpec { + out := &VariableSpec{ + name: s.name, + offset: s.offset, + size: s.size, + } + if s.t != nil { + out.t = btf.Copy(s.t).(*btf.Var) + } + + // Attempt to find a MapSpec with the same name in the copied CollectionSpec. + for _, m := range cpy.Maps { + if m.Name == s.m.Name { + out.m = m + return out + } + } + + return nil +} + +// Variable is a convenience wrapper for modifying global variables of a +// Collection after loading it into the kernel. Operations on a Variable are +// performed using direct memory access, bypassing the BPF map syscall API. +// +// On kernels older than 5.5, most interactions with Variable return +// [ErrNotSupported]. +type Variable struct { + name string + offset uint64 + size uint64 + t *btf.Var + + mm *Memory +} + +func newVariable(name string, offset, size uint64, t *btf.Var, mm *Memory) (*Variable, error) { + if mm != nil { + if int(offset+size) > mm.Size() { + return nil, fmt.Errorf("offset %d(+%d) is out of bounds", offset, size) + } + } + + return &Variable{ + name: name, + offset: offset, + size: size, + t: t, + mm: mm, + }, nil +} + +// Size returns the size of the variable. +func (v *Variable) Size() uint64 { + return v.size +} + +// ReadOnly returns true if the Variable represents a variable that is read-only +// after loading the Collection into the kernel. +// +// On systems without BPF_F_MMAPABLE support, ReadOnly always returns true. +func (v *Variable) ReadOnly() bool { + if v.mm == nil { + return true + } + return v.mm.ReadOnly() +} + +// Type returns the [btf.Var] representing the variable in its data section. +// This is useful for inspecting the variable's decl tags and the type +// information of the inner type. +// +// Returns nil if the original ELF object did not contain BTF information. +func (v *Variable) Type() *btf.Var { + return v.t +} + +func (v *Variable) String() string { + return fmt.Sprintf("%s (type=%v)", v.name, v.t) +} + +// Set the value of the Variable to the provided input. The input must marshal +// to the same length as the size of the Variable. +func (v *Variable) Set(in any) error { + if v.mm == nil { + return fmt.Errorf("variable %s: direct access requires Linux 5.5 or later: %w", v.name, ErrNotSupported) + } + + if v.ReadOnly() { + return fmt.Errorf("variable %s: %w", v.name, ErrReadOnly) + } + + buf, err := sysenc.Marshal(in, int(v.size)) + if err != nil { + return fmt.Errorf("marshaling value %s: %w", v.name, err) + } + + if _, err := v.mm.WriteAt(buf.Bytes(), int64(v.offset)); err != nil { + return fmt.Errorf("writing value to %s: %w", v.name, err) + } + + return nil +} + +// Get writes the value of the Variable to the provided output. The output must +// be a pointer to a value whose size matches the Variable. +func (v *Variable) Get(out any) error { + if v.mm == nil { + return fmt.Errorf("variable %s: direct access requires Linux 5.5 or later: %w", v.name, ErrNotSupported) + } + + if !v.mm.bounds(v.offset, v.size) { + return fmt.Errorf("variable %s: access out of bounds: %w", v.name, io.EOF) + } + + if err := sysenc.Unmarshal(out, v.mm.b[v.offset:v.offset+v.size]); err != nil { + return fmt.Errorf("unmarshaling value %s: %w", v.name, err) + } + + return nil +} diff --git a/vendor/golang.org/x/exp/LICENSE b/vendor/golang.org/x/exp/LICENSE deleted file mode 100644 index 6a66aea5eaf..00000000000 --- a/vendor/golang.org/x/exp/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/exp/PATENTS b/vendor/golang.org/x/exp/PATENTS deleted file mode 100644 index 733099041f8..00000000000 --- a/vendor/golang.org/x/exp/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/exp/constraints/constraints.go b/vendor/golang.org/x/exp/constraints/constraints.go deleted file mode 100644 index 2c033dff47e..00000000000 --- a/vendor/golang.org/x/exp/constraints/constraints.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package constraints defines a set of useful constraints to be used -// with type parameters. -package constraints - -// Signed is a constraint that permits any signed integer type. -// If future releases of Go add new predeclared signed integer types, -// this constraint will be modified to include them. -type Signed interface { - ~int | ~int8 | ~int16 | ~int32 | ~int64 -} - -// Unsigned is a constraint that permits any unsigned integer type. -// If future releases of Go add new predeclared unsigned integer types, -// this constraint will be modified to include them. -type Unsigned interface { - ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr -} - -// Integer is a constraint that permits any integer type. -// If future releases of Go add new predeclared integer types, -// this constraint will be modified to include them. -type Integer interface { - Signed | Unsigned -} - -// Float is a constraint that permits any floating-point type. -// If future releases of Go add new predeclared floating-point types, -// this constraint will be modified to include them. -type Float interface { - ~float32 | ~float64 -} - -// Complex is a constraint that permits any complex numeric type. -// If future releases of Go add new predeclared complex numeric types, -// this constraint will be modified to include them. -type Complex interface { - ~complex64 | ~complex128 -} - -// Ordered is a constraint that permits any ordered type: any type -// that supports the operators < <= >= >. -// If future releases of Go add new ordered types, -// this constraint will be modified to include them. -type Ordered interface { - Integer | Float | ~string -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 6b081b67553..8409d2c3e47 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2,16 +2,18 @@ ## explicit; go 1.16 github.com/checkpoint-restore/go-criu/v6 github.com/checkpoint-restore/go-criu/v6/rpc -# github.com/cilium/ebpf v0.16.0 -## explicit; go 1.21 +# github.com/cilium/ebpf v0.17.0 +## explicit; go 1.22 github.com/cilium/ebpf github.com/cilium/ebpf/asm github.com/cilium/ebpf/btf github.com/cilium/ebpf/internal github.com/cilium/ebpf/internal/kallsyms github.com/cilium/ebpf/internal/kconfig +github.com/cilium/ebpf/internal/linux github.com/cilium/ebpf/internal/sys github.com/cilium/ebpf/internal/sysenc +github.com/cilium/ebpf/internal/testutils/fdtrace github.com/cilium/ebpf/internal/tracefs github.com/cilium/ebpf/internal/unix github.com/cilium/ebpf/link @@ -78,9 +80,6 @@ github.com/vishvananda/netlink/nl # github.com/vishvananda/netns v0.0.4 ## explicit; go 1.17 github.com/vishvananda/netns -# golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 -## explicit; go 1.18 -golang.org/x/exp/constraints # golang.org/x/net v0.33.0 ## explicit; go 1.18 golang.org/x/net/bpf