diff --git a/go.mod b/go.mod index 3b47a0e8..5ec09aa9 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.104.0 github.com/aws/smithy-go v1.27.2 github.com/cbergoon/merkletree v0.2.0 - github.com/cilium/ebpf v0.21.0 + github.com/cilium/ebpf v0.22.0 github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 github.com/containernetworking/cni v1.3.0 github.com/containernetworking/plugins v1.9.1 diff --git a/go.sum b/go.sum index d157e014..0ec6abaa 100644 --- a/go.sum +++ b/go.sum @@ -124,6 +124,8 @@ github.com/chigopher/pathlib v0.19.1 h1:RoLlUJc0CqBGwq239cilyhxPNLXTK+HXoASGyGzn github.com/chigopher/pathlib v0.19.1/go.mod h1:tzC1dZLW8o33UQpWkNkhvPwL5n4yyFRFm/jL1YGWFvY= github.com/cilium/ebpf v0.21.0 h1:4dpx1J/B/1apeTmWBH5BkVLayHTkFrMovVPnHEk+l3k= github.com/cilium/ebpf v0.21.0/go.mod h1:1kHKv6Kvh5a6TePP5vvvoMa1bclRyzUXELSs272fmIQ= +github.com/cilium/ebpf v0.22.0 h1:v2ktp0roffpMOj2MMf3idtCQZOsAoC4BJbAJN+ke2bY= +github.com/cilium/ebpf v0.22.0/go.mod h1:CDzZbe2hC5JjlDC+CY3KFCzlYwN4gbxppYM+Z10bQt4= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= diff --git a/vendor/github.com/cilium/ebpf/CODEOWNERS b/vendor/github.com/cilium/ebpf/CODEOWNERS index 268129f0..be87f422 100644 --- a/vendor/github.com/cilium/ebpf/CODEOWNERS +++ b/vendor/github.com/cilium/ebpf/CODEOWNERS @@ -1,14 +1,14 @@ * @cilium/ebpf-lib-maintainers /features/ @rgo3 -/link/ @mmat11 +/link/ @mmat11 @rgo3 /perf/ @florianl /ringbuf/ @florianl /btf/ @dylandreimerink -/docs/ @ti-mo +/docs/ @cilium/ebpf-go-reviewers @ti-mo # Windows specific code. /docs/**/windows*.md @cilium/ebpf-go-windows-reviewers diff --git a/vendor/github.com/cilium/ebpf/Makefile b/vendor/github.com/cilium/ebpf/Makefile index b108a959..d697871e 100644 --- a/vendor/github.com/cilium/ebpf/Makefile +++ b/vendor/github.com/cilium/ebpf/Makefile @@ -1,9 +1,9 @@ # The development version of clang is distributed as the 'clang' binary, # while stable/released versions have a version number attached. # Pin the default clang to a stable version. -CLANG ?= clang-20 -STRIP ?= llvm-strip-20 -OBJCOPY ?= llvm-objcopy-20 +CLANG ?= clang-22 +STRIP ?= llvm-strip-22 +OBJCOPY ?= llvm-objcopy-22 CFLAGS := -O2 -g -Wall -Werror -mcpu=v2 $(CFLAGS) CI_KERNEL_URL ?= https://github.com/cilium/ci-kernels/raw/master/ @@ -78,19 +78,24 @@ TARGETS := \ HEADERS := $(wildcard testdata/*.h) -.PHONY: all clean container-all container-shell generate +.PHONY: all clean godirs container-all container-shell generate format -.DEFAULT_TARGET = container-all +.DEFAULT_GOAL = container-all + +# Make sure Go-related dirs exist before starting containerized builds, +# otherwise podman will fail to start. +godirs: + mkdir -p $(shell go env GOCACHE) $(shell go env GOPATH) $(shell go env GOMODCACHE) # Build all ELF binaries using a containerized LLVM toolchain. -container-all: +container-all: godirs +${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \ "${IMAGE}:${VERSION}" \ $(MAKE) all # (debug) Drop the user into a shell inside the container as root. # Set BPF2GO_ envs to make 'make generate' just work. -container-shell: +container-shell: godirs ${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \ "${IMAGE}:${VERSION}" @@ -107,8 +112,8 @@ all: format testdata update-external-deps $(MAKE) generate generate: - go generate -run "stringer" ./... go generate -run "gentypes" ./... + go generate -run "stringer" ./... go generate -skip "(gentypes|stringer)" ./... testdata: $(addsuffix -el.elf,$(TARGETS)) $(addsuffix -eb.elf,$(TARGETS)) $(addsuffix -el.elf,$(TARGETS_EL)) diff --git a/vendor/github.com/cilium/ebpf/asm/instruction.go b/vendor/github.com/cilium/ebpf/asm/instruction.go index 627d403d..f966a234 100644 --- a/vendor/github.com/cilium/ebpf/asm/instruction.go +++ b/vendor/github.com/cilium/ebpf/asm/instruction.go @@ -396,13 +396,19 @@ func (ins Instruction) Format(f fmt.State, c rune) { } case cls.IsJump(): - fmt.Fprintf(f, "%v ", op) switch jop := op.JumpOp(); jop { case Call: + fmt.Fprintf(f, "%v ", op) switch ins.Src { case PseudoCall: - // bpf-to-bpf call - fmt.Fprint(f, ins.Constant) + // bpf-to-bpf call. For JIT-compiled programs, the kernel stores + // the PC offset in the 'off' field (ins.Offset) rather than 'imm' + // (ins.Constant). See kernel bpf_insn_prepare_dump in verifier.c. + if ins.Offset != 0 { + fmt.Fprint(f, ins.Offset) + } else { + fmt.Fprint(f, ins.Constant) + } case PseudoKfuncCall: // kfunc call fmt.Fprintf(f, "Kfunc(%d)", ins.Constant) @@ -411,13 +417,23 @@ func (ins Instruction) Format(f fmt.State, c rune) { } case Ja: + fmt.Fprintf(f, "%v ", op) if ins.OpCode.Class() == Jump32Class { fmt.Fprintf(f, "imm: %d", ins.Constant) } else { fmt.Fprintf(f, "off: %d", ins.Offset) } + case JCOND: + switch ins.Src { + case PseudoMayGoto: + fmt.Fprintf(f, "JCond may_goto off: %d", ins.Offset) + default: + fmt.Fprintf(f, "%v", op) + } + default: + fmt.Fprintf(f, "%v ", op) fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset) if op.Source() == ImmSource { fmt.Fprintf(f, "imm: %d", ins.Constant) diff --git a/vendor/github.com/cilium/ebpf/asm/jump.go b/vendor/github.com/cilium/ebpf/asm/jump.go index a14bc4c8..e8a78093 100644 --- a/vendor/github.com/cilium/ebpf/asm/jump.go +++ b/vendor/github.com/cilium/ebpf/asm/jump.go @@ -44,6 +44,8 @@ const ( JSLT JumpOp = 0xc0 // JSLE jumps by offset if signed r <= signed imm JSLE JumpOp = 0xd0 + // JCOND is a conditional pseudo jump to encode the may_goto instruction + JCOND JumpOp = 0xe0 ) // Return emits an exit instruction. diff --git a/vendor/github.com/cilium/ebpf/asm/jump_string.go b/vendor/github.com/cilium/ebpf/asm/jump_string.go index 85a4aaff..240cb7c0 100644 --- a/vendor/github.com/cilium/ebpf/asm/jump_string.go +++ b/vendor/github.com/cilium/ebpf/asm/jump_string.go @@ -23,9 +23,10 @@ func _() { _ = x[JLE-176] _ = x[JSLT-192] _ = x[JSLE-208] + _ = x[JCOND-224] } -const _JumpOp_name = "JaJEqJGTJGEJSetJNEJSGTJSGECallExitJLTJLEJSLTJSLEInvalidJumpOp" +const _JumpOp_name = "JaJEqJGTJGEJSetJNEJSGTJSGECallExitJLTJLEJSLTJSLEJCONDInvalidJumpOp" var _JumpOp_map = map[JumpOp]string{ 0: _JumpOp_name[0:2], @@ -42,7 +43,8 @@ var _JumpOp_map = map[JumpOp]string{ 176: _JumpOp_name[37:40], 192: _JumpOp_name[40:44], 208: _JumpOp_name[44:48], - 255: _JumpOp_name[48:61], + 224: _JumpOp_name[48:53], + 255: _JumpOp_name[53:66], } func (i JumpOp) String() string { diff --git a/vendor/github.com/cilium/ebpf/asm/metadata.go b/vendor/github.com/cilium/ebpf/asm/metadata.go index dd368a93..60b8fe31 100644 --- a/vendor/github.com/cilium/ebpf/asm/metadata.go +++ b/vendor/github.com/cilium/ebpf/asm/metadata.go @@ -7,13 +7,13 @@ type Metadata struct { type metaElement struct { next *metaElement - key, value interface{} + key, value any } // Find the element containing key. // // Returns nil if there is no such element. -func (m *Metadata) find(key interface{}) *metaElement { +func (m *Metadata) find(key any) *metaElement { for e := m.head; e != nil; e = e.next { if e.key == key { return e @@ -49,7 +49,7 @@ func (m *Metadata) remove(r *metaElement) { // // If value is nil, the key is removed. Avoids modifying old metadata by // copying if necessary. -func (m *Metadata) Set(key, value interface{}) { +func (m *Metadata) Set(key, value any) { if e := m.find(key); e != nil { if e.value == value { // Key is present and the value is the same. Nothing to do. @@ -72,7 +72,7 @@ func (m *Metadata) Set(key, value interface{}) { // Get the value of a key. // // Returns nil if no value with the given key is present. -func (m *Metadata) Get(key interface{}) interface{} { +func (m *Metadata) Get(key any) any { if e := m.find(key); e != nil { return e.value } diff --git a/vendor/github.com/cilium/ebpf/asm/register.go b/vendor/github.com/cilium/ebpf/asm/register.go index 457a3b8a..baf03aa4 100644 --- a/vendor/github.com/cilium/ebpf/asm/register.go +++ b/vendor/github.com/cilium/ebpf/asm/register.go @@ -40,6 +40,7 @@ const ( PseudoCall = R1 // BPF_PSEUDO_CALL PseudoFunc = R4 // BPF_PSEUDO_FUNC PseudoKfuncCall = R2 // BPF_PSEUDO_KFUNC_CALL + PseudoMayGoto = R0 // BPF_MAY_GOTO ) func (r Register) String() string { diff --git a/vendor/github.com/cilium/ebpf/btf/btf.go b/vendor/github.com/cilium/ebpf/btf/btf.go index 41e1f8a6..34115692 100644 --- a/vendor/github.com/cilium/ebpf/btf/btf.go +++ b/vendor/github.com/cilium/ebpf/btf/btf.go @@ -204,7 +204,7 @@ func loadRawSpec(btf []byte, base *Spec) (*Spec, error) { baseStrings = base.strings } - header, bo, err := parseBTFHeader(btf) + header, _, bo, err := parseBTFHeader(btf) if err != nil { return nil, fmt.Errorf("parsing .BTF header: %v", err) } @@ -214,7 +214,7 @@ func loadRawSpec(btf []byte, base *Spec) (*Spec, error) { } btf = btf[header.HdrLen:] - if int(header.StringOff+header.StringLen) > len(btf) { + if uint64(header.StringOff)+uint64(header.StringLen) > uint64(len(btf)) { return nil, fmt.Errorf("string table is out of bounds") } stringsSection := btf[header.StringOff : header.StringOff+header.StringLen] @@ -224,7 +224,7 @@ func loadRawSpec(btf []byte, base *Spec) (*Spec, error) { return nil, fmt.Errorf("read string section: %w", err) } - if int(header.TypeOff+header.TypeLen) > len(btf) { + if uint64(header.TypeOff)+uint64(header.TypeLen) > uint64(len(btf)) { return nil, fmt.Errorf("types section is out of bounds") } typesSection := btf[header.TypeOff : header.TypeOff+header.TypeLen] @@ -445,15 +445,18 @@ func (s *Spec) AnyTypeByName(name string) (Type, error) { // // Returns an error wrapping ErrNotFound if no matching Type exists in the Spec. // Returns an error wrapping ErrMultipleTypes if multiple candidates are found. -func (s *Spec) TypeByName(name string, typ interface{}) error { - typeInterface := reflect.TypeOf((*Type)(nil)).Elem() +func (s *Spec) TypeByName(name string, typ any) error { + if err := internal.IsNilPointer(typ); err != nil { + return fmt.Errorf("type argument: %w", err) + } + + typeInterface := reflect.TypeFor[Type]() // typ may be **T or *Type typValue := reflect.ValueOf(typ) - if typValue.Kind() != reflect.Ptr { + if typValue.Kind() != reflect.Pointer { return fmt.Errorf("%T is not a pointer", typ) } - typPtr := typValue.Elem() if !typPtr.CanSet() { return fmt.Errorf("%T cannot be set", typ) @@ -462,6 +465,9 @@ func (s *Spec) TypeByName(name string, typ interface{}) error { wanted := typPtr.Type() if wanted == typeInterface { // This is *Type. Unwrap the value's type. + if typPtr.IsNil() { + return fmt.Errorf("%T points to a nil Type", typ) + } wanted = typPtr.Elem().Type() } diff --git a/vendor/github.com/cilium/ebpf/btf/btf_types.go b/vendor/github.com/cilium/ebpf/btf/btf_types.go index c957f597..469baf60 100644 --- a/vendor/github.com/cilium/ebpf/btf/btf_types.go +++ b/vendor/github.com/cilium/ebpf/btf/btf_types.go @@ -84,14 +84,19 @@ type btfHeader struct { StringLen uint32 } +type btfLayout struct { + Off uint32 + Len uint32 +} + // parseBTFHeader parses the header of the .BTF section. -func parseBTFHeader(buf []byte) (*btfHeader, binary.ByteOrder, error) { +func parseBTFHeader(buf []byte) (*btfHeader, *btfLayout, binary.ByteOrder, error) { var header btfHeader var bo binary.ByteOrder for _, order := range []binary.ByteOrder{binary.LittleEndian, binary.BigEndian} { n, err := binary.Decode(buf, order, &header) if err != nil { - return nil, nil, fmt.Errorf("read header: %v", err) + return nil, nil, nil, fmt.Errorf("read header: %v", err) } if header.Magic != btfMagic { @@ -104,29 +109,43 @@ func parseBTFHeader(buf []byte) (*btfHeader, binary.ByteOrder, error) { } if bo == nil { - return nil, nil, fmt.Errorf("no valid BTF header") + return nil, nil, nil, fmt.Errorf("no valid BTF header") } if header.Version != 1 { - return nil, nil, fmt.Errorf("unexpected version %v", header.Version) + return nil, nil, nil, fmt.Errorf("unexpected version %v", header.Version) } if header.Flags != 0 { - return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags) + return nil, nil, nil, fmt.Errorf("unsupported flags %v", header.Flags) } remainder := int64(header.HdrLen) - int64(binary.Size(&header)) if remainder < 0 { - return nil, nil, errors.New("header length shorter than btfHeader size") + return nil, nil, nil, errors.New("header length shorter than minimum BTF header size") + } + + if len(buf) < int(remainder) { + return nil, nil, nil, errors.New("header length exceeds available data") + } + + var layout btfLayout + if remainder >= int64(binary.Size(&btfLayout{})) { + n, err := binary.Decode(buf, bo, &layout) + if err != nil { + return nil, nil, nil, fmt.Errorf("read layout offset and length: %v", err) + } + buf = buf[n:] + remainder -= int64(n) } for _, b := range buf[:remainder] { if b != 0 { - return nil, nil, errors.New("header contains non-zero trailer") + return nil, nil, nil, errors.New("header contains non-zero trailer") } } - return &header, bo, nil + return &header, &layout, bo, nil } // btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst. diff --git a/vendor/github.com/cilium/ebpf/btf/ext_info.go b/vendor/github.com/cilium/ebpf/btf/ext_info.go index 2054c1d7..2e2aa629 100644 --- a/vendor/github.com/cilium/ebpf/btf/ext_info.go +++ b/vendor/github.com/cilium/ebpf/btf/ext_info.go @@ -437,7 +437,7 @@ func parseFuncInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, r return nil, fmt.Errorf("expected FuncInfo record size %d, but BTF blob contains %d", exp, got) } - for i := uint32(0); i < recordNum; i++ { + for range recordNum { if err := binary.Read(r, bo, &fi); err != nil { return nil, fmt.Errorf("can't read function info: %v", err) } @@ -642,9 +642,15 @@ func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, r return nil, fmt.Errorf("expected LineInfo record size %d, but BTF blob contains %d", exp, got) } - out := make([]bpfLineInfo, recordNum) - if err := binary.Read(r, bo, out); err != nil { - return nil, fmt.Errorf("can't read line info: %v", err) + out := make([]bpfLineInfo, 0) + chunk := make([]bpfLineInfo, min(1024, recordNum)) + for remaining := recordNum; remaining > 0; { + n := min(uint32(1024), remaining) + if err := binary.Read(r, bo, chunk[:n]); err != nil { + return nil, fmt.Errorf("can't read line info: %v", err) + } + out = append(out, chunk[:n]...) + remaining -= n } if offsetInBytes { @@ -794,7 +800,7 @@ func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordNum uint32) ([ var out []bpfCORERelo var relo bpfCORERelo - for i := uint32(0); i < recordNum; i++ { + for range recordNum { if err := binary.Read(r, bo, &relo); err != nil { return nil, fmt.Errorf("can't read CO-RE relocation: %v", err) } diff --git a/vendor/github.com/cilium/ebpf/btf/handle.go b/vendor/github.com/cilium/ebpf/btf/handle.go index 89e09a3b..6b432f5a 100644 --- a/vendor/github.com/cilium/ebpf/btf/handle.go +++ b/vendor/github.com/cilium/ebpf/btf/handle.go @@ -99,6 +99,10 @@ func NewHandleFromRawBTF(btf []byte) (*Handle, error) { attr.BtfLogLevel = 1 } + if errors.Is(err, sys.ErrTokenCapabilities) { + return nil, fmt.Errorf("load btf: %w", err) + } + if err := haveBTF(); err != nil { return nil, err } @@ -171,6 +175,24 @@ func (h *Handle) FD() int { return h.fd.Int() } +// Clone creates a duplicate of the handle. +// +// Closing the duplicate does not affect the original, and vice versa. +// +// Cloning a nil Handle returns nil. +func (h *Handle) Clone() (*Handle, error) { + if h == nil { + return nil, nil + } + + dup, err := h.fd.Dup() + if err != nil { + return nil, fmt.Errorf("can't clone handle: %w", err) + } + + return &Handle{dup, h.size, h.needsKernelBase}, nil +} + // Info returns metadata about the handle. func (h *Handle) Info() (*HandleInfo, error) { return newHandleInfoFromFD(h.fd) diff --git a/vendor/github.com/cilium/ebpf/btf/kernel.go b/vendor/github.com/cilium/ebpf/btf/kernel.go index bb7368bf..8e9084b5 100644 --- a/vendor/github.com/cilium/ebpf/btf/kernel.go +++ b/vendor/github.com/cilium/ebpf/btf/kernel.go @@ -16,113 +16,14 @@ import ( "github.com/cilium/ebpf/internal/unix" ) -// globalCache amortises decoding BTF across all users of the library. -var globalCache = struct { - sync.RWMutex - kernel *Spec - modules map[string]*Spec -}{ - modules: make(map[string]*Spec), -} - -// FlushKernelSpec removes any cached kernel type information. -func FlushKernelSpec() { - globalCache.Lock() - defer globalCache.Unlock() - - globalCache.kernel = nil - globalCache.modules = make(map[string]*Spec) -} - // LoadKernelSpec returns the current kernel's BTF information. // -// Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file system -// for vmlinux ELFs. Returns an error wrapping ErrNotSupported if BTF is not enabled. +// Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file +// system for vmlinux ELFs. Returns an error wrapping [ErrNotSupported] if BTF +// is not enabled. // // Consider using [Cache] instead. func LoadKernelSpec() (*Spec, error) { - spec, err := loadCachedKernelSpec() - return spec.Copy(), err -} - -// load (and cache) the kernel spec. -// -// Does not copy Spec. -func loadCachedKernelSpec() (*Spec, error) { - globalCache.RLock() - spec := globalCache.kernel - globalCache.RUnlock() - - if spec != nil { - return spec, nil - } - - globalCache.Lock() - defer globalCache.Unlock() - - // check again, to prevent race between multiple callers - if globalCache.kernel != nil { - return globalCache.kernel, nil - } - - spec, err := loadKernelSpec() - if err != nil { - return nil, err - } - - globalCache.kernel = spec - return spec, nil -} - -// LoadKernelModuleSpec returns the BTF information for the named kernel module. -// -// Using [Cache.Module] is faster when loading BTF for more than one module. -// -// Defaults to /sys/kernel/btf/. -// Returns an error wrapping ErrNotSupported if BTF is not enabled. -// Returns an error wrapping fs.ErrNotExist if BTF for the specific module doesn't exist. -func LoadKernelModuleSpec(module string) (*Spec, error) { - spec, err := loadCachedKernelModuleSpec(module) - return spec.Copy(), err -} - -// load (and cache) a module spec. -// -// Does not copy Spec. -func loadCachedKernelModuleSpec(module string) (*Spec, error) { - globalCache.RLock() - spec := globalCache.modules[module] - globalCache.RUnlock() - - if spec != nil { - return spec, nil - } - - base, err := loadCachedKernelSpec() - if err != nil { - return nil, err - } - - // NB: This only allows a single module to be parsed at a time. Not sure - // it makes a difference. - globalCache.Lock() - defer globalCache.Unlock() - - // check again, to prevent race between multiple callers - if spec := globalCache.modules[module]; spec != nil { - return spec, nil - } - - spec, err = loadKernelModuleSpec(module, base) - if err != nil { - return nil, err - } - - globalCache.modules[module] = spec - return spec, nil -} - -func loadKernelSpec() (*Spec, error) { if platform.IsWindows { return nil, internal.ErrNotSupportedOnOS } @@ -168,6 +69,21 @@ func loadKernelSpec() (*Spec, error) { return spec, err } +// LoadKernelModuleSpec returns the BTF information for the named kernel module. +// +// Using [Cache.Module] is faster when loading BTF for more than one module. +// +// Defaults to /sys/kernel/btf/. +// Returns an error wrapping ErrNotSupported if BTF is not enabled. +// Returns an error wrapping fs.ErrNotExist if BTF for the specific module doesn't exist. +func LoadKernelModuleSpec(module string) (*Spec, error) { + base, err := LoadKernelSpec() + if err != nil { + return nil, err + } + return loadKernelModuleSpec(module, base) +} + func loadKernelModuleSpec(module string, base *Spec) (*Spec, error) { if platform.IsWindows { return nil, internal.ErrNotSupportedOnOS @@ -223,45 +139,41 @@ func findVMLinux() (*os.File, error) { // Cache allows to amortise the cost of decoding BTF across multiple call-sites. // -// It is not safe for concurrent use. +// It is safe for concurrent use. type Cache struct { + mu sync.RWMutex kernelTypes *Spec moduleTypes map[string]*Spec loadedModules []string } -// NewCache creates a new Cache. +// NewCache returns an empty Cache. // -// Opportunistically reuses a global cache if possible. +// Pass it to [CollectionOptions] to share kernel BTF across multiple +// Collection loads. func NewCache() *Cache { - globalCache.RLock() - defer globalCache.RUnlock() - - // This copy is either a no-op or very cheap, since the spec won't contain - // any inflated types. - kernel := globalCache.kernel.Copy() - if kernel == nil { - return &Cache{} - } - - modules := make(map[string]*Spec, len(globalCache.modules)) - for name, spec := range globalCache.modules { - decoder, _ := rebaseDecoder(spec.decoder, kernel.decoder) - // NB: Kernel module BTF can't contain ELF fixups because it is always - // read from sysfs. - modules[name] = &Spec{decoder: decoder} - } - - if len(modules) == 0 { - return &Cache{kernel, nil, nil} - } - - return &Cache{kernel, modules, nil} + return &Cache{} } // Kernel is equivalent to [LoadKernelSpec], except that repeated calls do // not copy the Spec. +// +// Returns an error wrapping [ErrNotSupported] if BTF is not enabled. func (c *Cache) Kernel() (*Spec, error) { + c.mu.RLock() + if c.kernelTypes != nil { + spec := c.kernelTypes + c.mu.RUnlock() + return spec, nil + } + c.mu.RUnlock() + + c.mu.Lock() + defer c.mu.Unlock() + return c.kernel() +} + +func (c *Cache) kernel() (*Spec, error) { if c.kernelTypes != nil { return c.kernelTypes, nil } @@ -276,40 +188,53 @@ func (c *Cache) Kernel() (*Spec, error) { // // All modules also share the return value of [Kernel] as their base. func (c *Cache) Module(name string) (*Spec, error) { + c.mu.RLock() if spec := c.moduleTypes[name]; spec != nil { + c.mu.RUnlock() return spec, nil } + c.mu.RUnlock() - if c.moduleTypes == nil { - c.moduleTypes = make(map[string]*Spec) + c.mu.Lock() + defer c.mu.Unlock() + + if spec := c.moduleTypes[name]; spec != nil { + return spec, nil } - base, err := c.Kernel() - if err != nil { - return nil, err + if c.moduleTypes == nil { + c.moduleTypes = make(map[string]*Spec) } - spec, err := loadCachedKernelModuleSpec(name) + base, err := c.kernel() if err != nil { return nil, err } - // Important: base is shared between modules. This allows inflating common - // types only once. - decoder, err := rebaseDecoder(spec.decoder, base.decoder) + spec, err := loadKernelModuleSpec(name, base) if err != nil { return nil, err } - spec = &Spec{decoder: decoder} c.moduleTypes[name] = spec - return spec, err + return spec, nil } // Modules returns a sorted list of all loaded modules. func (c *Cache) Modules() ([]string, error) { + c.mu.RLock() + if c.loadedModules != nil { + modules := slices.Clone(c.loadedModules) + c.mu.RUnlock() + return modules, nil + } + c.mu.RUnlock() + + c.mu.Lock() + defer c.mu.Unlock() + if c.loadedModules != nil { - return c.loadedModules, nil + return slices.Clone(c.loadedModules), nil } btfDir, err := os.Open("/sys/kernel/btf") @@ -329,5 +254,5 @@ func (c *Cache) Modules() ([]string, error) { sort.Strings(entries) c.loadedModules = entries - return entries, nil + return slices.Clone(entries), nil } diff --git a/vendor/github.com/cilium/ebpf/btf/marshal.go b/vendor/github.com/cilium/ebpf/btf/marshal.go index c336a695..10866050 100644 --- a/vendor/github.com/cilium/ebpf/btf/marshal.go +++ b/vendor/github.com/cilium/ebpf/btf/marshal.go @@ -129,6 +129,15 @@ func (b *Builder) Empty() bool { // // See [Type] for details on identity. func (b *Builder) Add(typ Type) (TypeID, error) { + if _, ok := typ.(*Void); ok { + // Equality is weird for void, since it is a zero sized type. + return 0, nil + } + + if err := internal.IsNilPointer(typ); err != nil { + return 0, fmt.Errorf("invalid type: %w", err) + } + if b.stableIDs == nil { b.stableIDs = make(map[Type]TypeID) } @@ -141,11 +150,6 @@ func (b *Builder) Add(typ Type) (TypeID, error) { } } - if _, ok := typ.(*Void); ok { - // Equality is weird for void, since it is a zero sized type. - return 0, nil - } - if ds, ok := typ.(*Datasec); ok { if err := datasecResolveWorkaround(b, ds); err != nil { return 0, err diff --git a/vendor/github.com/cilium/ebpf/btf/strings.go b/vendor/github.com/cilium/ebpf/btf/strings.go index 482f93be..3e3bd688 100644 --- a/vendor/github.com/cilium/ebpf/btf/strings.go +++ b/vendor/github.com/cilium/ebpf/btf/strings.go @@ -94,6 +94,9 @@ func (st *stringTable) lookupSlow(offset uint32) ([]byte, error) { } i := bytes.IndexByte(st.bytes[offset:], 0) + if i == -1 { + return nil, fmt.Errorf("string at offset %d is not null terminated", offset) + } return st.bytes[offset : offset+uint32(i)], nil } diff --git a/vendor/github.com/cilium/ebpf/btf/types.go b/vendor/github.com/cilium/ebpf/btf/types.go index 143bab60..f24beeb9 100644 --- a/vendor/github.com/cilium/ebpf/btf/types.go +++ b/vendor/github.com/cilium/ebpf/btf/types.go @@ -180,8 +180,15 @@ type Struct struct { Tags []string } +// Format supports the width option to %v to limit or extend the number of +// struct member names printed (default 5). +// +// For example, %1v will print only the first member name followed by '...': +// +// Struct:"struct"[fields=3 fieldNames=[a ...]] func (s *Struct) Format(fs fmt.State, verb rune) { - formatType(fs, verb, s, "fields=", len(s.Members)) + max, _ := fs.Width() + formatType(fs, verb, s, "fields=", len(s.Members), "fieldNames=", memberNames(s.Members, max)) } func (s *Struct) TypeName() string { return s.Name } @@ -208,8 +215,15 @@ type Union struct { Tags []string } +// Format supports the width option to %v to limit or extend the number of +// union member names printed (default 5). +// +// For example, %1v will print only the first member name followed by '...': +// +// Union:"union"[fields=3 fieldNames=[a ...]] func (u *Union) Format(fs fmt.State, verb rune) { - formatType(fs, verb, u, "fields=", len(u.Members)) + max, _ := fs.Width() + formatType(fs, verb, u, "fields=", len(u.Members), "fieldNames=", memberNames(u.Members, max)) } func (u *Union) TypeName() string { return u.Name } @@ -227,6 +241,29 @@ func (u *Union) members() []Member { return u.Members } +// memberNames returns the names of members, or its index in the list of members +// if the name is empty. +// +// Returns up to max entries (default 5), followed by '...'. +func memberNames(members []Member, max int) []string { + if max <= 0 { + max = 5 + } + names := make([]string, min(len(members), max+1)) + for i, m := range members { + if i >= max { + names[i] = "..." + break + } + if m.Name == "" { + names[i] = fmt.Sprintf("<%d>", i) + continue + } + names[i] = m.Name + } + return names +} + func copyMembers(orig []Member) []Member { cpy := make([]Member, len(orig)) copy(cpy, orig) @@ -656,7 +693,7 @@ func Sizeof(typ Type) (int, error) { elem int64 ) - for i := 0; i < maxResolveDepth; i++ { + for range maxResolveDepth { switch v := typ.(type) { case *Array: if n > 0 && int64(v.Nelems) > math.MaxInt64/n { @@ -852,7 +889,7 @@ type formattableType interface { // Handles cyclical types by only printing cycles up to a certain depth. Elements // in extra are separated by spaces unless the preceding element is a string // ending in '='. -func formatType(f fmt.State, verb rune, t formattableType, extra ...interface{}) { +func formatType(f fmt.State, verb rune, t formattableType, extra ...any) { if verb != 'v' && verb != 's' { fmt.Fprintf(f, "{UNRECOGNIZED: %c}", verb) return diff --git a/vendor/github.com/cilium/ebpf/btf/unmarshal.go b/vendor/github.com/cilium/ebpf/btf/unmarshal.go index 26ae320d..b56b5a06 100644 --- a/vendor/github.com/cilium/ebpf/btf/unmarshal.go +++ b/vendor/github.com/cilium/ebpf/btf/unmarshal.go @@ -174,31 +174,6 @@ func allBtfTypeOffsets(buf []byte, bo binary.ByteOrder, header *btfType) iter.Se } } -func rebaseDecoder(d *decoder, base *decoder) (*decoder, error) { - if d.base == nil { - return nil, fmt.Errorf("rebase split spec: not a split spec") - } - - if len(d.base.raw) != len(base.raw) || (len(d.base.raw) > 0 && &d.base.raw[0] != &base.raw[0]) { - return nil, fmt.Errorf("rebase split spec: raw BTF differs") - } - - return &decoder{ - base, - d.byteOrder, - d.sharedBuf, - d.strings, - d.firstTypeID, - d.offsets, - d.declTags, - d.namedTypes, - sync.Mutex{}, - make(map[TypeID]Type), - make(map[Type]TypeID), - make(map[TypeID][2]Bits), - }, nil -} - // Copy performs a deep copy of a decoder and its base. func (d *decoder) Copy() *decoder { if d == nil { @@ -573,7 +548,7 @@ func (d *decoder) inflateType(id TypeID) (typ Type, err error) { vlen := header.Vlen() vars := make([]VarSecinfo, 0, vlen) var bSecInfo btfVarSecinfo - for i := 0; i < vlen; i++ { + for i := range vlen { n, err := unmarshalBtfVarSecInfo(&bSecInfo, pos, d.byteOrder) if err != nil { return nil, fmt.Errorf("can't unmarshal btfVarSecinfo %d, id: %d: %w", i, id, err) diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/gen/output.go b/vendor/github.com/cilium/ebpf/cmd/bpf2go/gen/output.go index 56ab312c..66685989 100644 --- a/vendor/github.com/cilium/ebpf/cmd/bpf2go/gen/output.go +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/gen/output.go @@ -221,7 +221,7 @@ func sortTypes(typeNames map[btf.Type]string) ([]btf.Type, error) { } if names[i] == name { - return nil, fmt.Errorf("type name %q is used multiple times", name) + return nil, fmt.Errorf("type name %q is used multiple times (remove its --type flag?)", name) } types = append(types[:i], append([]btf.Type{typ}, types[i:]...)...) diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/gen/output.tpl b/vendor/github.com/cilium/ebpf/cmd/bpf2go/gen/output.tpl index 350b0cab..dbd858ee 100644 --- a/vendor/github.com/cilium/ebpf/cmd/bpf2go/gen/output.tpl +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/gen/output.tpl @@ -22,6 +22,23 @@ import ( {{ end }} {{- end }} +{{- if or .Maps (or .Variables .Programs) }} +// Names of all BPF objects in the ELF. +// +// Used for safe lookups in a Collection or CollectionSpec. +const ( +{{- range $name, $id := .Maps }} + {{ $.Name }}Map{{ $id }} = "{{ $name }}" +{{- end }} +{{- range $name, $id := .Programs }} + {{ $.Name }}Prog{{ $id }} = "{{ $name }}" +{{- end }} +{{- range $name, $id := .Variables }} + {{ $.Name }}Var{{ $id }} = "{{ $name }}" +{{- end }} +) +{{- end }} + // {{ .Name.Load }} returns the embedded CollectionSpec for {{ .Name }}. func {{ .Name.Load }}() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader({{ .Name.Bytes }}) @@ -42,7 +59,7 @@ func {{ .Name.Load }}() (*ebpf.CollectionSpec, error) { // *{{ .Name.Maps }} // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. -func {{ .Name.LoadObjects }}(obj interface{}, opts *ebpf.CollectionOptions) (error) { +func {{ .Name.LoadObjects }}(obj any, opts *ebpf.CollectionOptions) (error) { spec, err := {{ .Name.Load }}() if err != nil { return err diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/main.go b/vendor/github.com/cilium/ebpf/cmd/bpf2go/main.go index a0da0838..cdfdd693 100644 --- a/vendor/github.com/cilium/ebpf/cmd/bpf2go/main.go +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/main.go @@ -211,7 +211,7 @@ func newB2G(stdout io.Writer, args []string) (*bpf2go, error) { } targetArches := make(map[gen.Target]gen.GoArches) - for _, tgt := range strings.Split(*flagTarget, ",") { + for tgt := range strings.SplitSeq(*flagTarget, ",") { target, goarches, err := gen.FindTarget(tgt) if err != nil { if errors.Is(err, gen.ErrInvalidTarget) { @@ -530,7 +530,7 @@ func collectCTypes(types *btf.Spec, names []string) ([]btf.Type, error) { for _, cType := range names { typ, err := types.AnyTypeByName(cType) if err != nil { - return nil, err + return nil, fmt.Errorf("looking up type %s: %w", cType, err) } result = append(result, typ) } diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/makedep.go b/vendor/github.com/cilium/ebpf/cmd/bpf2go/makedep.go index b5d34b7c..da7f64ea 100644 --- a/vendor/github.com/cilium/ebpf/cmd/bpf2go/makedep.go +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/makedep.go @@ -86,7 +86,7 @@ func parseDependencies(baseDir string, in io.Reader) ([]dependency, error) { // NB: This doesn't handle filenames with spaces in them. // It seems like make doesn't do that either, so oh well. var prereqs []string - for _, prereq := range strings.Fields(parts[1]) { + for prereq := range strings.FieldsSeq(parts[1]) { prereqs = append(prereqs, abs(prereq)) } diff --git a/vendor/github.com/cilium/ebpf/collection.go b/vendor/github.com/cilium/ebpf/collection.go index 3e5b05a4..42621e74 100644 --- a/vendor/github.com/cilium/ebpf/collection.go +++ b/vendor/github.com/cilium/ebpf/collection.go @@ -36,6 +36,12 @@ type CollectionOptions struct { // The given Maps are Clone()d before being used in the Collection, so the // caller can Close() them freely when they are no longer needed. MapReplacements map[string]*Map + + // Cache amortises the cost of decoding kernel BTF across multiple + // Collection loads. Callers loading more than one Collection should + // allocate a Cache via [btf.NewCache] and share it across loads. + // If nil, a fresh cache is allocated per load and discarded. + Cache *btf.Cache } // CollectionSpec describes a collection. @@ -115,22 +121,22 @@ func copyMapOfSpecs[T interface{ Copy() T }](m map[string]T) map[string]T { // // Returns an error if any of the eBPF objects can't be found, or // if the same Spec is assigned multiple times. -func (cs *CollectionSpec) Assign(to interface{}) error { - getValue := func(typ reflect.Type, name string) (interface{}, error) { +func (cs *CollectionSpec) Assign(to any) error { + getValue := func(typ reflect.Type, name string) (any, error) { switch typ { - case reflect.TypeOf((*ProgramSpec)(nil)): + case reflect.TypeFor[*ProgramSpec](): if p := cs.Programs[name]; p != nil { return p, nil } return nil, fmt.Errorf("missing program %q", name) - case reflect.TypeOf((*MapSpec)(nil)): + case reflect.TypeFor[*MapSpec](): if m := cs.Maps[name]; m != nil { return m, nil } return nil, fmt.Errorf("missing map %q", name) - case reflect.TypeOf((*VariableSpec)(nil)): + case reflect.TypeFor[*VariableSpec](): if v := cs.Variables[name]; v != nil { return v, nil } @@ -171,7 +177,7 @@ func (cs *CollectionSpec) Assign(to interface{}) error { // // Returns an error if any of the fields can't be found, or // if the same Map or Program is assigned multiple times. -func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error { +func (cs *CollectionSpec) LoadAndAssign(to any, opts *CollectionOptions) error { loader, err := newCollectionLoader(cs, opts) if err != nil { return err @@ -183,18 +189,18 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) assignedProgs := make(map[string]bool) assignedVars := make(map[string]bool) - getValue := func(typ reflect.Type, name string) (interface{}, error) { + getValue := func(typ reflect.Type, name string) (any, error) { switch typ { - case reflect.TypeOf((*Program)(nil)): + case reflect.TypeFor[*Program](): assignedProgs[name] = true return loader.loadProgram(name) - case reflect.TypeOf((*Map)(nil)): + case reflect.TypeFor[*Map](): assignedMaps[name] = true return loader.loadMap(name) - case reflect.TypeOf((*Variable)(nil)): + case reflect.TypeFor[*Variable](): assignedVars[name] = true return loader.loadVariable(name) @@ -205,12 +211,12 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) // Load the Maps and Programs requested by the annotated struct. if err := assignValues(to, getValue); err != nil { - return err + return fmt.Errorf("assign values: %w", err) } // Populate the requested maps. Has a chance of lazy-loading other dependent maps. if err := loader.populateDeferredMaps(); err != nil { - return err + return fmt.Errorf("populate deferred maps: %w", err) } // Evaluate the loader's objects after all (lazy)loading has taken place. @@ -341,13 +347,18 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec return nil, fmt.Errorf("populating kallsyms caches: %w", err) } + cache := opts.Cache + if cache == nil { + cache = btf.NewCache() + } + return &collectionLoader{ coll, opts, make(map[string]*Map), make(map[string]*Program), make(map[string]*Variable), - btf.NewCache(), + cache, }, nil } @@ -838,31 +849,31 @@ func LoadCollection(file string) (*Collection, error) { // // Ownership and Close()ing responsibility is transferred to `to` // for any successful assigns. On error `to` is left in an undefined state. -func (coll *Collection) Assign(to interface{}) error { +func (coll *Collection) Assign(to any) 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. - getValue := func(typ reflect.Type, name string) (interface{}, error) { + getValue := func(typ reflect.Type, name string) (any, error) { switch typ { - case reflect.TypeOf((*Program)(nil)): + case reflect.TypeFor[*Program](): if p := coll.Programs[name]; p != nil { assignedProgs[name] = true return p, nil } return nil, fmt.Errorf("missing program %q", name) - case reflect.TypeOf((*Map)(nil)): + case reflect.TypeFor[*Map](): if m := coll.Maps[name]; m != nil { assignedMaps[name] = true return m, nil } return nil, fmt.Errorf("missing map %q", name) - case reflect.TypeOf((*Variable)(nil)): + case reflect.TypeFor[*Variable](): if v := coll.Variables[name]; v != nil { assignedVars[name] = true return v, nil @@ -875,7 +886,7 @@ func (coll *Collection) Assign(to interface{}) error { } if err := assignValues(to, getValue); err != nil { - return err + return fmt.Errorf("assign values: %w", err) } // Finalize ownership transfer @@ -963,7 +974,7 @@ func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]struc // to a struct, attempt to gather its fields as well. var v reflect.Value switch field.Type.Kind() { - case reflect.Ptr: + case reflect.Pointer: if field.Type.Elem().Kind() != reflect.Struct { continue } @@ -998,18 +1009,16 @@ func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]struc // // getValue is called for every tagged field of 'to' and must return the value // to be assigned to the field with the given typ and name. -func assignValues(to interface{}, - getValue func(typ reflect.Type, name string) (interface{}, error)) error { +func assignValues(to any, getValue func(typ reflect.Type, name string) (any, error)) error { + if err := internal.IsNilPointer(to); err != nil { + return err + } toValue := reflect.ValueOf(to) - if toValue.Type().Kind() != reflect.Ptr { + if toValue.Type().Kind() != reflect.Pointer { return fmt.Errorf("%T is not a pointer to struct", to) } - if toValue.IsNil() { - return fmt.Errorf("nil pointer to %T", to) - } - fields, err := ebpfFields(toValue.Elem(), nil) if err != nil { return err diff --git a/vendor/github.com/cilium/ebpf/elf_reader.go b/vendor/github.com/cilium/ebpf/elf_reader.go index 3f609d2e..e2f26491 100644 --- a/vendor/github.com/cilium/ebpf/elf_reader.go +++ b/vendor/github.com/cilium/ebpf/elf_reader.go @@ -382,6 +382,10 @@ func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) { return nil, fmt.Errorf("section %v: missing symbols", sec.Name) } + if sec.ReaderAt == nil { + return nil, fmt.Errorf("compressed program section is not supported") + } + funcs, err := ec.loadFunctions(sec) if err != nil { return nil, fmt.Errorf("section %v: %w", sec.Name, err) @@ -452,6 +456,13 @@ func (ec *elfCode) loadFunctions(sec *elfSection) (map[string]asm.Instructions, return nil, fmt.Errorf("duplicate symbol %s in section %s", sym.Name, sec.Name) } + if sym.Value > math.MaxUint32 || sym.Size > math.MaxUint32 { + return nil, fmt.Errorf("symbol %s: offset or size exceeds 32 bits in section %s", sym.Name, sec.Name) + } + if sym.Value+sym.Size > sec.Size { + return nil, fmt.Errorf("symbol %s: size goes out of bounds of section %s", sym.Name, sec.Name) + } + // Decode the symbol's instruction stream, limited to its size. sr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size)) insns := make(asm.Instructions, 0, sym.Size/asm.InstructionSize) @@ -803,6 +814,10 @@ func (ec *elfCode) loadMaps() error { return fmt.Errorf("section %v: no symbols", sec.Name) } + if sec.ReaderAt == nil { + return fmt.Errorf("compressed map section is not supported") + } + vars, err := ec.sectionVars(ec.btf, sec.Name) if err != nil { return fmt.Errorf("section %v: loading map variable BTF: %w", sec.Name, err) @@ -814,6 +829,13 @@ func (ec *elfCode) loadMaps() error { return fmt.Errorf("duplicate symbol %s in section %s", name, sec.Name) } + if sym.Value > math.MaxUint32 || sym.Size > math.MaxUint32 { + return fmt.Errorf("symbol %s: offset or size exceeds 32 bits in section %s", sym.Name, sec.Name) + } + if sym.Value+sym.Size > sec.Size { + return fmt.Errorf("symbol %s: size goes out of bounds of section %s", name, sec.Name) + } + sr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size)) spec := MapSpec{ @@ -889,6 +911,10 @@ func (ec *elfCode) loadBTFMaps() error { return fmt.Errorf("missing BTF") } + if sec.ReaderAt == nil { + return fmt.Errorf("compressed BTF map section is not supported") + } + vars, err := ec.sectionVars(ec.btf, sec.Name) if err != nil { return fmt.Errorf("section %v: loading map variable BTF: %w", sec.Name, err) @@ -912,6 +938,13 @@ func (ec *elfCode) loadBTFMaps() error { return fmt.Errorf("section %v: missing symbol for map %s", sec.Name, name) } + if sym.Value > math.MaxUint32 || sym.Size > math.MaxUint32 { + return fmt.Errorf("symbol %s: offset or size exceeds 32 bits in section %s", sym.Name, sec.Name) + } + if sym.Value+sym.Size > sec.Size { + return fmt.Errorf("section %v: symbol %s: size goes out of bounds of section", sec.Name, name) + } + sr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size)) // The BTF metadata for each Var contains the full length of the map @@ -1245,6 +1278,10 @@ func resolveBTFValuesContents(es *elfSection, sym elf.Symbol, member btf.Member) return nil, fmt.Errorf("member offset %d exceeds symbol size %d", member.Offset.Bytes(), sym.Size) } + if es.Addralign == 0 { + return nil, fmt.Errorf("section has no address alignment, can't resolve .values contents") + } + for i, sym := range valuesRelocations(es, sym, member) { // Emit a value stub based on the type of relocation to be replaced by a // real fd later in the pipeline before populating the Map. @@ -1354,6 +1391,10 @@ func (ec *elfCode) loadDataSections() error { return fmt.Errorf("data section %s: variable %s offset %d exceeds maximum", sec.Name, sym.Name, off) } + if sym.Size > math.MaxUint32 { + return fmt.Errorf("data section %s: variable %s size %d exceeds maximum", sec.Name, sym.Name, sym.Size) + } + ec.vars[sym.Name] = &VariableSpec{ SectionName: sec.Name, Name: sym.Name, @@ -1487,6 +1528,10 @@ func (ec *elfCode) associateStructOpsRelocs(progs map[string]*ProgramSpec) error return fmt.Errorf("failed to read section data: %w", err) } + if ec.btf == nil { + return fmt.Errorf("struct_ops section %s: missing BTF", sec.Name) + } + // Resolve the BTF datasec describing variables in this section. var ds *btf.Datasec if err := ec.btf.TypeByName(sec.Name, &ds); err != nil { @@ -1530,7 +1575,7 @@ func (ec *elfCode) createStructOpsMap(vsi btf.VarSecinfo, userData []byte, flags userSize := userSt.Size baseOff := vsi.Offset - if baseOff+userSize > uint32(len(userData)) { + if uint64(baseOff)+uint64(userSize) > uint64(len(userData)) { return nil, 0, fmt.Errorf("%s exceeds section", mapName) } diff --git a/vendor/github.com/cilium/ebpf/elf_sections.go b/vendor/github.com/cilium/ebpf/elf_sections.go index e48b84a7..1ded4c4d 100644 --- a/vendor/github.com/cilium/ebpf/elf_sections.go +++ b/vendor/github.com/cilium/ebpf/elf_sections.go @@ -51,6 +51,8 @@ var elfSectionDefs = []libbpfElfSectionDef{ {"fentry.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FENTRY, _SEC_ATTACH_BTF | _SEC_SLEEPABLE}, {"fmod_ret.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_MODIFY_RETURN, _SEC_ATTACH_BTF | _SEC_SLEEPABLE}, {"fexit.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FEXIT, _SEC_ATTACH_BTF | _SEC_SLEEPABLE}, + {"fsession+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FSESSION, _SEC_ATTACH_BTF}, + {"fsession.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FSESSION, _SEC_ATTACH_BTF | _SEC_SLEEPABLE}, {"freplace+", sys.BPF_PROG_TYPE_EXT, 0, _SEC_ATTACH_BTF}, {"lsm+", sys.BPF_PROG_TYPE_LSM, sys.BPF_LSM_MAC, _SEC_ATTACH_BTF}, {"lsm.s+", sys.BPF_PROG_TYPE_LSM, sys.BPF_LSM_MAC, _SEC_ATTACH_BTF | _SEC_SLEEPABLE}, diff --git a/vendor/github.com/cilium/ebpf/info.go b/vendor/github.com/cilium/ebpf/info.go index b52f5f45..b2d08de4 100644 --- a/vendor/github.com/cilium/ebpf/info.go +++ b/vendor/github.com/cilium/ebpf/info.go @@ -140,7 +140,7 @@ func newMapInfoFromFd(fd *sys.FD) (*MapInfo, error) { // /proc/self/fdinfo. It only writes data into fields that have a zero value. func readMapInfoFromProc(fd *sys.FD, mi *MapInfo) error { var mapType uint32 - err := scanFdInfo(fd, map[string]interface{}{ + err := scanFdInfo(fd, map[string]any{ "map_type": &mapType, "map_id": &mi.id, "key_size": &mi.KeySize, @@ -539,7 +539,7 @@ func readNameFromFunc(pi *ProgramInfo) (string, error) { func readProgramInfoFromProc(fd *sys.FD, pi *ProgramInfo) error { var progType uint32 - err := scanFdInfo(fd, map[string]interface{}{ + err := scanFdInfo(fd, map[string]any{ "prog_type": &progType, "prog_tag": &pi.Tag, "memlock": &pi.memlock, @@ -882,7 +882,7 @@ func (pi *ProgramInfo) Memlock() (uint64, bool) { return pi.memlock, pi.memlock > 0 } -func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error { +func scanFdInfo(fd *sys.FD, fields map[string]any) error { if platform.IsWindows { return fmt.Errorf("read fdinfo: %w", internal.ErrNotSupportedOnOS) } @@ -899,7 +899,7 @@ func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error { return nil } -func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { +func scanFdInfoReader(r io.Reader, fields map[string]any) error { var ( scanner = bufio.NewScanner(r) scanned int diff --git a/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go b/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go index 29c62b62..de18dc82 100644 --- a/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go +++ b/vendor/github.com/cilium/ebpf/internal/kconfig/kconfig.go @@ -183,8 +183,8 @@ func putValueString(data []byte, typ btf.Type, value string) error { // Any Int, which is not bool, of one byte could be used to store char: // https://github.com/torvalds/linux/blob/1a5304fecee5/tools/lib/bpf/libbpf.c#L3637-L3638 - if contentType.Size != 1 && contentType.Encoding != btf.Bool { - return fmt.Errorf("cannot add string value, expected array of btf.Int of size 1, got array of btf.Int of size: %v", contentType.Size) + if contentType.Size != 1 || contentType.Encoding == btf.Bool { + return fmt.Errorf("cannot add string value, expected array of non-bool btf.Int of size 1, got array of btf.Int with encoding %v and size %v", contentType.Encoding, contentType.Size) } if !strings.HasPrefix(value, `"`) || !strings.HasSuffix(value, `"`) { diff --git a/vendor/github.com/cilium/ebpf/internal/mountinfo/mountinfo.go b/vendor/github.com/cilium/ebpf/internal/mountinfo/mountinfo.go new file mode 100644 index 00000000..a3cb1d19 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/mountinfo/mountinfo.go @@ -0,0 +1,150 @@ +// Package mountinfo parses /proc/self/mountinfo to discover filesystem +// mounts visible in the calling process's mount namespace. +package mountinfo + +import ( + "bufio" + "errors" + "fmt" + "io" + "os" + "slices" + "strings" + "sync" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/platform" +) + +// Entry represents one parsed line from /proc/self/mountinfo. +type Entry struct { + // MountPoint is the absolute path where the filesystem is mounted, + // with octal escapes (\040, \011, \012, \134) decoded. + MountPoint string + + // Root is the path within the source filesystem that's exposed at + // MountPoint, octal-decoded. It is "/" for a full filesystem mount and + // some other path (e.g. "/events") when a subdirectory of the source + // filesystem has been bind-mounted onto MountPoint. Callers that want + // to use MountPoint as the root of a filesystem should reject entries + // where Root != "/". + Root string + + // FSType is the filesystem type, e.g. "bpf", "tracefs", "debugfs". + FSType string +} + +// Read parses /proc/self/mountinfo and returns one Entry per mount line. +// The result is cached for the lifetime of the process; mounts that appear +// or disappear after the first call are not reflected. +// +// On non-Linux, returns an error wrapping [internal.ErrNotSupportedOnOS]. +func Read() ([]Entry, error) { + return readOnce() +} + +// FindByFSType returns the mount points for filesystems of the given type, +// in order of appearance, with duplicates removed. +func FindByFSType(fstype string) ([]string, error) { + entries, err := Read() + if err != nil { + return nil, err + } + return filterByFSType(entries, fstype), nil +} + +func filterByFSType(entries []Entry, fstype string) []string { + var mounts []string + for _, e := range entries { + if e.FSType != fstype { + continue + } + // Number of matching mounts is expected to be very low; linear + // search is faster and more cache-friendly than a map at this + // scale, and avoids the allocations a map would incur. + if !slices.Contains(mounts, e.MountPoint) { + mounts = append(mounts, e.MountPoint) + } + } + return mounts +} + +// parseEntries parses mountinfo data read from r. +func parseEntries(r io.Reader) ([]Entry, error) { + var entries []Entry + // Format of /proc/self/mountinfo: + // + // {id} {parent id} {major:minor} {root} {mount point} {mount options} [optional fields...] - {filesystem type} {source} {superblock options} + br := bufio.NewReader(r) + for { + line, err := br.ReadString('\n') + if line != "" { + line = strings.TrimRight(line, "\n") + if line != "" { + entry, perr := parseLine(line) + if perr != nil { + return nil, perr + } + entries = append(entries, entry) + } + } + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return nil, fmt.Errorf("read mountinfo: %w", err) + } + } + + return entries, nil +} + +func parseLine(line string) (Entry, error) { + firstHalfStr, secondHalfStr, ok := strings.Cut(line, " - ") + if !ok { + return Entry{}, fmt.Errorf("invalid mountinfo line, missing dash: %q", line) + } + + secondHalf := strings.Fields(strings.TrimSpace(secondHalfStr)) + if len(secondHalf) == 0 { + return Entry{}, fmt.Errorf("invalid mountinfo line, too few fields after dash: %q", line) + } + + firstHalf := strings.Fields(strings.TrimSpace(firstHalfStr)) + if len(firstHalf) < 6 { + return Entry{}, fmt.Errorf("invalid mountinfo line, too few fields: %q", line) + } + + return Entry{ + Root: unescape(firstHalf[3]), + MountPoint: unescape(firstHalf[4]), + FSType: secondHalf[0], + }, nil +} + +// show_mountinfo in the kernel has the escape set of ' \t\n\\'. Instead of +// a full octal unescaper, only replace these specific characters. +var unescaper = strings.NewReplacer( + `\040`, " ", + `\011`, "\t", + `\012`, "\n", + `\134`, `\`, +) + +func unescape(s string) string { + return unescaper.Replace(s) +} + +var readOnce = sync.OnceValues(func() ([]Entry, error) { + if !platform.IsLinux { + return nil, fmt.Errorf("mountinfo: %w", internal.ErrNotSupportedOnOS) + } + + f, err := os.Open("/proc/self/mountinfo") + if err != nil { + return nil, fmt.Errorf("open mountinfo: %w", err) + } + defer f.Close() + + return parseEntries(f) +}) diff --git a/vendor/github.com/cilium/ebpf/internal/nil.go b/vendor/github.com/cilium/ebpf/internal/nil.go new file mode 100644 index 00000000..83ba6c6f --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/nil.go @@ -0,0 +1,24 @@ +package internal + +import ( + "fmt" + "reflect" +) + +// IsNilPointer returns an error if i is a nil interface or a nil pointer. +// Otherwise, it returns nil. +func IsNilPointer(i any) error { + if i == nil { + return fmt.Errorf("nil interface") + } + v := reflect.ValueOf(i) + if v.Kind() != reflect.Pointer { + return nil + } + + if v.IsNil() { + return fmt.Errorf("nil %T", i) + } + + return nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/sys/syscall.go b/vendor/github.com/cilium/ebpf/internal/sys/syscall.go index 2efab76b..3a2ae9c5 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/syscall.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/syscall.go @@ -18,6 +18,7 @@ const ENOTSUPP = unix.Errno(524) // ProgInfo // LinkInfo // BtfInfo +// TokenInfo type Info interface { info() (unsafe.Pointer, uint32) } @@ -106,9 +107,15 @@ func (i *PerfEventLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } +var _ Info = (*TokenInfo)(nil) + +func (i *TokenInfo) info() (unsafe.Pointer, uint32) { + return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) +} + // ObjInfo retrieves information about a BPF Fd. // -// info may be one of MapInfo, ProgInfo, LinkInfo and BtfInfo. +// info may be one of MapInfo, ProgInfo, LinkInfo, BtfInfo and TokenInfo. func ObjInfo(fd *FD, info Info) error { ptr, len := info.info() err := ObjGetInfoByFd(&ObjGetInfoByFdAttr{ diff --git a/vendor/github.com/cilium/ebpf/internal/sys/syscall_other.go b/vendor/github.com/cilium/ebpf/internal/sys/syscall_other.go index b99e6e46..969eaa41 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/syscall_other.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/syscall_other.go @@ -3,6 +3,7 @@ package sys import ( + "errors" "fmt" "os" "path/filepath" @@ -24,6 +25,11 @@ func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) { defer unmaskProfilerSignal() } + tok, err := tokenAttr(cmd, attr) + if err != nil { + return 0, fmt.Errorf("apply token attributes: %w", err) + } + for { r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size) runtime.KeepAlive(attr) @@ -39,6 +45,24 @@ func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) { err = wrappedErrno{errNo} } + // Tokens are in use, attempt to enrich EPERM with capability information. + if tok != nil && errors.Is(err, unix.EPERM) { + // Kernels before 6.17 don't have token info, so we can't return accurate + // errors. Make the token error a hint by adding a question mark. + // + // Returning ErrTokenCapabilities here isn't ideal since it's not + // conclusive, but since this behaviour is limited to one LTS release + // (6.12), and only when tokens are enabled, it's better than having to + // account for it in all existing feature probes and tests. + if tok.info == nil { + return r1, fmt.Errorf("%w (%w?)", err, ErrTokenCapabilities) + } + + if terr := tok.Capable(cmd, attr); terr != nil { + return r1, fmt.Errorf("%w: %w", terr, err) + } + } + return r1, err } } diff --git a/vendor/github.com/cilium/ebpf/internal/sys/token.go b/vendor/github.com/cilium/ebpf/internal/sys/token.go new file mode 100644 index 00000000..9a5df392 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/sys/token.go @@ -0,0 +1,226 @@ +package sys + +import ( + "errors" + "fmt" + "sync" + "unsafe" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/mountinfo" + "github.com/cilium/ebpf/internal/platform" + "github.com/cilium/ebpf/internal/testutils/testmain" + "github.com/cilium/ebpf/internal/unix" +) + +var ( + // ErrTokenCapabilities indicates that the operation failed due to + // insufficient capabilities being delegated to the token. + ErrTokenCapabilities = errors.New("token insufficiently privileged") +) + +// Token is a BPF token that can be used to create BPF objects that normally +// require CAP_SYS_ADMIN or CAP_NET_ADMIN. +// +// With a valid token, equivalent BPF objects can be created while only being +// granted CAP_BPF. +type Token struct { + fd *FD + + // Token info is available as of Linux 6.17, may be nil. + info *TokenInfo +} + +// FD returns the raw file descriptor of the token. +func (t *Token) FD() int { + return t.fd.Int() +} + +// Capable returns an error if the token does not allow using cmd or some of its +// provided attributes. +// +// Always returns nil on kernels before 6.17, as they don't provide token info. +func (t *Token) Capable(cmd Cmd, attr unsafe.Pointer) error { + if t.info == nil { + return nil + } + + if !t.cmd(cmd) { + return fmt.Errorf("command %s: %w", cmd, ErrTokenCapabilities) + } + + switch cmd { + case BPF_MAP_CREATE: + attr := (*MapCreateAttr)(attr) + if !t.mapType(attr.MapType) { + return fmt.Errorf("map type %s: %w", attr.MapType, ErrTokenCapabilities) + } + + case BPF_PROG_LOAD: + attr := (*ProgLoadAttr)(attr) + if !t.progType(attr.ProgType) { + return fmt.Errorf("program type %s: %w", attr.ProgType, ErrTokenCapabilities) + } + + if !t.attachType(attr.ExpectedAttachType) { + return fmt.Errorf("attach type %s: %w", attr.ExpectedAttachType, ErrTokenCapabilities) + } + } + + return nil +} + +// cmd returns true if cmd is allowed by the Token. +func (t *Token) cmd(cmd Cmd) bool { + if t.info == nil { + return true + } + return t.info.AllowedCmds&(uint64(1)<= len(_Cmd_index)-1 { + return "Cmd(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Cmd_name[_Cmd_index[idx]:_Cmd_index[idx+1]] +} +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_MAP_TYPE_UNSPEC-0] + _ = x[BPF_MAP_TYPE_HASH-1] + _ = x[BPF_MAP_TYPE_ARRAY-2] + _ = x[BPF_MAP_TYPE_PROG_ARRAY-3] + _ = x[BPF_MAP_TYPE_PERF_EVENT_ARRAY-4] + _ = x[BPF_MAP_TYPE_PERCPU_HASH-5] + _ = x[BPF_MAP_TYPE_PERCPU_ARRAY-6] + _ = x[BPF_MAP_TYPE_STACK_TRACE-7] + _ = x[BPF_MAP_TYPE_CGROUP_ARRAY-8] + _ = x[BPF_MAP_TYPE_LRU_HASH-9] + _ = x[BPF_MAP_TYPE_LRU_PERCPU_HASH-10] + _ = x[BPF_MAP_TYPE_LPM_TRIE-11] + _ = x[BPF_MAP_TYPE_ARRAY_OF_MAPS-12] + _ = x[BPF_MAP_TYPE_HASH_OF_MAPS-13] + _ = x[BPF_MAP_TYPE_DEVMAP-14] + _ = x[BPF_MAP_TYPE_SOCKMAP-15] + _ = x[BPF_MAP_TYPE_CPUMAP-16] + _ = x[BPF_MAP_TYPE_XSKMAP-17] + _ = x[BPF_MAP_TYPE_SOCKHASH-18] + _ = x[BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED-19] + _ = x[BPF_MAP_TYPE_CGROUP_STORAGE-19] + _ = x[BPF_MAP_TYPE_REUSEPORT_SOCKARRAY-20] + _ = x[BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED-21] + _ = x[BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE-21] + _ = x[BPF_MAP_TYPE_QUEUE-22] + _ = x[BPF_MAP_TYPE_STACK-23] + _ = x[BPF_MAP_TYPE_SK_STORAGE-24] + _ = x[BPF_MAP_TYPE_DEVMAP_HASH-25] + _ = x[BPF_MAP_TYPE_STRUCT_OPS-26] + _ = x[BPF_MAP_TYPE_RINGBUF-27] + _ = x[BPF_MAP_TYPE_INODE_STORAGE-28] + _ = x[BPF_MAP_TYPE_TASK_STORAGE-29] + _ = x[BPF_MAP_TYPE_BLOOM_FILTER-30] + _ = x[BPF_MAP_TYPE_USER_RINGBUF-31] + _ = x[BPF_MAP_TYPE_CGRP_STORAGE-32] + _ = x[BPF_MAP_TYPE_ARENA-33] + _ = x[BPF_MAP_TYPE_INSN_ARRAY-34] + _ = x[__MAX_BPF_MAP_TYPE-35] +} + +const _MapType_name = "BPF_MAP_TYPE_UNSPECBPF_MAP_TYPE_HASHBPF_MAP_TYPE_ARRAYBPF_MAP_TYPE_PROG_ARRAYBPF_MAP_TYPE_PERF_EVENT_ARRAYBPF_MAP_TYPE_PERCPU_HASHBPF_MAP_TYPE_PERCPU_ARRAYBPF_MAP_TYPE_STACK_TRACEBPF_MAP_TYPE_CGROUP_ARRAYBPF_MAP_TYPE_LRU_HASHBPF_MAP_TYPE_LRU_PERCPU_HASHBPF_MAP_TYPE_LPM_TRIEBPF_MAP_TYPE_ARRAY_OF_MAPSBPF_MAP_TYPE_HASH_OF_MAPSBPF_MAP_TYPE_DEVMAPBPF_MAP_TYPE_SOCKMAPBPF_MAP_TYPE_CPUMAPBPF_MAP_TYPE_XSKMAPBPF_MAP_TYPE_SOCKHASHBPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATEDBPF_MAP_TYPE_REUSEPORT_SOCKARRAYBPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATEDBPF_MAP_TYPE_QUEUEBPF_MAP_TYPE_STACKBPF_MAP_TYPE_SK_STORAGEBPF_MAP_TYPE_DEVMAP_HASHBPF_MAP_TYPE_STRUCT_OPSBPF_MAP_TYPE_RINGBUFBPF_MAP_TYPE_INODE_STORAGEBPF_MAP_TYPE_TASK_STORAGEBPF_MAP_TYPE_BLOOM_FILTERBPF_MAP_TYPE_USER_RINGBUFBPF_MAP_TYPE_CGRP_STORAGEBPF_MAP_TYPE_ARENABPF_MAP_TYPE_INSN_ARRAY__MAX_BPF_MAP_TYPE" + +var _MapType_index = [...]uint16{0, 19, 36, 54, 77, 106, 130, 155, 179, 204, 225, 253, 274, 300, 325, 344, 364, 383, 402, 423, 461, 493, 538, 556, 574, 597, 621, 644, 664, 690, 715, 740, 765, 790, 808, 831, 849} + +func (i MapType) String() string { + idx := int(i) - 0 + if i < 0 || idx >= len(_MapType_index)-1 { + return "MapType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _MapType_name[_MapType_index[idx]:_MapType_index[idx+1]] +} +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_PROG_TYPE_UNSPEC-0] + _ = x[BPF_PROG_TYPE_SOCKET_FILTER-1] + _ = x[BPF_PROG_TYPE_KPROBE-2] + _ = x[BPF_PROG_TYPE_SCHED_CLS-3] + _ = x[BPF_PROG_TYPE_SCHED_ACT-4] + _ = x[BPF_PROG_TYPE_TRACEPOINT-5] + _ = x[BPF_PROG_TYPE_XDP-6] + _ = x[BPF_PROG_TYPE_PERF_EVENT-7] + _ = x[BPF_PROG_TYPE_CGROUP_SKB-8] + _ = x[BPF_PROG_TYPE_CGROUP_SOCK-9] + _ = x[BPF_PROG_TYPE_LWT_IN-10] + _ = x[BPF_PROG_TYPE_LWT_OUT-11] + _ = x[BPF_PROG_TYPE_LWT_XMIT-12] + _ = x[BPF_PROG_TYPE_SOCK_OPS-13] + _ = x[BPF_PROG_TYPE_SK_SKB-14] + _ = x[BPF_PROG_TYPE_CGROUP_DEVICE-15] + _ = x[BPF_PROG_TYPE_SK_MSG-16] + _ = x[BPF_PROG_TYPE_RAW_TRACEPOINT-17] + _ = x[BPF_PROG_TYPE_CGROUP_SOCK_ADDR-18] + _ = x[BPF_PROG_TYPE_LWT_SEG6LOCAL-19] + _ = x[BPF_PROG_TYPE_LIRC_MODE2-20] + _ = x[BPF_PROG_TYPE_SK_REUSEPORT-21] + _ = x[BPF_PROG_TYPE_FLOW_DISSECTOR-22] + _ = x[BPF_PROG_TYPE_CGROUP_SYSCTL-23] + _ = x[BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE-24] + _ = x[BPF_PROG_TYPE_CGROUP_SOCKOPT-25] + _ = x[BPF_PROG_TYPE_TRACING-26] + _ = x[BPF_PROG_TYPE_STRUCT_OPS-27] + _ = x[BPF_PROG_TYPE_EXT-28] + _ = x[BPF_PROG_TYPE_LSM-29] + _ = x[BPF_PROG_TYPE_SK_LOOKUP-30] + _ = x[BPF_PROG_TYPE_SYSCALL-31] + _ = x[BPF_PROG_TYPE_NETFILTER-32] + _ = x[__MAX_BPF_PROG_TYPE-33] +} + +const _ProgType_name = "BPF_PROG_TYPE_UNSPECBPF_PROG_TYPE_SOCKET_FILTERBPF_PROG_TYPE_KPROBEBPF_PROG_TYPE_SCHED_CLSBPF_PROG_TYPE_SCHED_ACTBPF_PROG_TYPE_TRACEPOINTBPF_PROG_TYPE_XDPBPF_PROG_TYPE_PERF_EVENTBPF_PROG_TYPE_CGROUP_SKBBPF_PROG_TYPE_CGROUP_SOCKBPF_PROG_TYPE_LWT_INBPF_PROG_TYPE_LWT_OUTBPF_PROG_TYPE_LWT_XMITBPF_PROG_TYPE_SOCK_OPSBPF_PROG_TYPE_SK_SKBBPF_PROG_TYPE_CGROUP_DEVICEBPF_PROG_TYPE_SK_MSGBPF_PROG_TYPE_RAW_TRACEPOINTBPF_PROG_TYPE_CGROUP_SOCK_ADDRBPF_PROG_TYPE_LWT_SEG6LOCALBPF_PROG_TYPE_LIRC_MODE2BPF_PROG_TYPE_SK_REUSEPORTBPF_PROG_TYPE_FLOW_DISSECTORBPF_PROG_TYPE_CGROUP_SYSCTLBPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLEBPF_PROG_TYPE_CGROUP_SOCKOPTBPF_PROG_TYPE_TRACINGBPF_PROG_TYPE_STRUCT_OPSBPF_PROG_TYPE_EXTBPF_PROG_TYPE_LSMBPF_PROG_TYPE_SK_LOOKUPBPF_PROG_TYPE_SYSCALLBPF_PROG_TYPE_NETFILTER__MAX_BPF_PROG_TYPE" + +var _ProgType_index = [...]uint16{0, 20, 47, 67, 90, 113, 137, 154, 178, 202, 227, 247, 268, 290, 312, 332, 359, 379, 407, 437, 464, 488, 514, 542, 569, 606, 634, 655, 679, 696, 713, 736, 757, 780, 799} + +func (i ProgType) String() string { + idx := int(i) - 0 + if i < 0 || idx >= len(_ProgType_index)-1 { + return "ProgType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ProgType_name[_ProgType_index[idx]:_ProgType_index[idx+1]] +} +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_CGROUP_INET_INGRESS-0] + _ = x[BPF_CGROUP_INET_EGRESS-1] + _ = x[BPF_CGROUP_INET_SOCK_CREATE-2] + _ = x[BPF_CGROUP_SOCK_OPS-3] + _ = x[BPF_SK_SKB_STREAM_PARSER-4] + _ = x[BPF_SK_SKB_STREAM_VERDICT-5] + _ = x[BPF_CGROUP_DEVICE-6] + _ = x[BPF_SK_MSG_VERDICT-7] + _ = x[BPF_CGROUP_INET4_BIND-8] + _ = x[BPF_CGROUP_INET6_BIND-9] + _ = x[BPF_CGROUP_INET4_CONNECT-10] + _ = x[BPF_CGROUP_INET6_CONNECT-11] + _ = x[BPF_CGROUP_INET4_POST_BIND-12] + _ = x[BPF_CGROUP_INET6_POST_BIND-13] + _ = x[BPF_CGROUP_UDP4_SENDMSG-14] + _ = x[BPF_CGROUP_UDP6_SENDMSG-15] + _ = x[BPF_LIRC_MODE2-16] + _ = x[BPF_FLOW_DISSECTOR-17] + _ = x[BPF_CGROUP_SYSCTL-18] + _ = x[BPF_CGROUP_UDP4_RECVMSG-19] + _ = x[BPF_CGROUP_UDP6_RECVMSG-20] + _ = x[BPF_CGROUP_GETSOCKOPT-21] + _ = x[BPF_CGROUP_SETSOCKOPT-22] + _ = x[BPF_TRACE_RAW_TP-23] + _ = x[BPF_TRACE_FENTRY-24] + _ = x[BPF_TRACE_FEXIT-25] + _ = x[BPF_MODIFY_RETURN-26] + _ = x[BPF_LSM_MAC-27] + _ = x[BPF_TRACE_ITER-28] + _ = x[BPF_CGROUP_INET4_GETPEERNAME-29] + _ = x[BPF_CGROUP_INET6_GETPEERNAME-30] + _ = x[BPF_CGROUP_INET4_GETSOCKNAME-31] + _ = x[BPF_CGROUP_INET6_GETSOCKNAME-32] + _ = x[BPF_XDP_DEVMAP-33] + _ = x[BPF_CGROUP_INET_SOCK_RELEASE-34] + _ = x[BPF_XDP_CPUMAP-35] + _ = x[BPF_SK_LOOKUP-36] + _ = x[BPF_XDP-37] + _ = x[BPF_SK_SKB_VERDICT-38] + _ = x[BPF_SK_REUSEPORT_SELECT-39] + _ = x[BPF_SK_REUSEPORT_SELECT_OR_MIGRATE-40] + _ = x[BPF_PERF_EVENT-41] + _ = x[BPF_TRACE_KPROBE_MULTI-42] + _ = x[BPF_LSM_CGROUP-43] + _ = x[BPF_STRUCT_OPS-44] + _ = x[BPF_NETFILTER-45] + _ = x[BPF_TCX_INGRESS-46] + _ = x[BPF_TCX_EGRESS-47] + _ = x[BPF_TRACE_UPROBE_MULTI-48] + _ = x[BPF_CGROUP_UNIX_CONNECT-49] + _ = x[BPF_CGROUP_UNIX_SENDMSG-50] + _ = x[BPF_CGROUP_UNIX_RECVMSG-51] + _ = x[BPF_CGROUP_UNIX_GETPEERNAME-52] + _ = x[BPF_CGROUP_UNIX_GETSOCKNAME-53] + _ = x[BPF_NETKIT_PRIMARY-54] + _ = x[BPF_NETKIT_PEER-55] + _ = x[BPF_TRACE_KPROBE_SESSION-56] + _ = x[BPF_TRACE_UPROBE_SESSION-57] + _ = x[BPF_TRACE_FSESSION-58] + _ = x[__MAX_BPF_ATTACH_TYPE-59] +} + +const _AttachType_name = "BPF_CGROUP_INET_INGRESSBPF_CGROUP_INET_EGRESSBPF_CGROUP_INET_SOCK_CREATEBPF_CGROUP_SOCK_OPSBPF_SK_SKB_STREAM_PARSERBPF_SK_SKB_STREAM_VERDICTBPF_CGROUP_DEVICEBPF_SK_MSG_VERDICTBPF_CGROUP_INET4_BINDBPF_CGROUP_INET6_BINDBPF_CGROUP_INET4_CONNECTBPF_CGROUP_INET6_CONNECTBPF_CGROUP_INET4_POST_BINDBPF_CGROUP_INET6_POST_BINDBPF_CGROUP_UDP4_SENDMSGBPF_CGROUP_UDP6_SENDMSGBPF_LIRC_MODE2BPF_FLOW_DISSECTORBPF_CGROUP_SYSCTLBPF_CGROUP_UDP4_RECVMSGBPF_CGROUP_UDP6_RECVMSGBPF_CGROUP_GETSOCKOPTBPF_CGROUP_SETSOCKOPTBPF_TRACE_RAW_TPBPF_TRACE_FENTRYBPF_TRACE_FEXITBPF_MODIFY_RETURNBPF_LSM_MACBPF_TRACE_ITERBPF_CGROUP_INET4_GETPEERNAMEBPF_CGROUP_INET6_GETPEERNAMEBPF_CGROUP_INET4_GETSOCKNAMEBPF_CGROUP_INET6_GETSOCKNAMEBPF_XDP_DEVMAPBPF_CGROUP_INET_SOCK_RELEASEBPF_XDP_CPUMAPBPF_SK_LOOKUPBPF_XDPBPF_SK_SKB_VERDICTBPF_SK_REUSEPORT_SELECTBPF_SK_REUSEPORT_SELECT_OR_MIGRATEBPF_PERF_EVENTBPF_TRACE_KPROBE_MULTIBPF_LSM_CGROUPBPF_STRUCT_OPSBPF_NETFILTERBPF_TCX_INGRESSBPF_TCX_EGRESSBPF_TRACE_UPROBE_MULTIBPF_CGROUP_UNIX_CONNECTBPF_CGROUP_UNIX_SENDMSGBPF_CGROUP_UNIX_RECVMSGBPF_CGROUP_UNIX_GETPEERNAMEBPF_CGROUP_UNIX_GETSOCKNAMEBPF_NETKIT_PRIMARYBPF_NETKIT_PEERBPF_TRACE_KPROBE_SESSIONBPF_TRACE_UPROBE_SESSIONBPF_TRACE_FSESSION__MAX_BPF_ATTACH_TYPE" + +var _AttachType_index = [...]uint16{0, 23, 45, 72, 91, 115, 140, 157, 175, 196, 217, 241, 265, 291, 317, 340, 363, 377, 395, 412, 435, 458, 479, 500, 516, 532, 547, 564, 575, 589, 617, 645, 673, 701, 715, 743, 757, 770, 777, 795, 818, 852, 866, 888, 902, 916, 929, 944, 958, 980, 1003, 1026, 1049, 1076, 1103, 1121, 1136, 1160, 1184, 1202, 1223} + +func (i AttachType) String() string { + idx := int(i) - 0 + if i < 0 || idx >= len(_AttachType_index)-1 { + return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _AttachType_name[_AttachType_index[idx]:_AttachType_index[idx+1]] +} diff --git a/vendor/github.com/cilium/ebpf/internal/sysenc/marshal.go b/vendor/github.com/cilium/ebpf/internal/sysenc/marshal.go index 3f7deb80..fdd81ac1 100644 --- a/vendor/github.com/cilium/ebpf/internal/sysenc/marshal.go +++ b/vendor/github.com/cilium/ebpf/internal/sysenc/marshal.go @@ -68,7 +68,7 @@ func Marshal(data any, size int) (Buffer, error) { // // Returns an error if buf can't be unmarshalled according to the behaviour // of [binary.Decode]. -func Unmarshal(data interface{}, buf []byte) error { +func Unmarshal(data any, buf []byte) error { switch value := data.(type) { case encoding.BinaryUnmarshaler: return value.UnmarshalBinary(buf) diff --git a/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go b/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go index d0b5be66..c42725b4 100644 --- a/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go +++ b/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go @@ -13,6 +13,7 @@ import ( "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/linux" + "github.com/cilium/ebpf/internal/mountinfo" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/unix" ) @@ -109,15 +110,31 @@ func sanitizeTracefsPath(path ...string) (string, error) { return p, nil } -// getTracefsPath will return a correct path to the tracefs mount point. -// Since kernel 4.1 tracefs should be mounted by default at /sys/kernel/tracing, -// but may be also be available at /sys/kernel/debug/tracing if debugfs is mounted. -// The available tracefs paths will depends on distribution choices. +// getTracefsPath returns a correct path to the tracefs mount point. +// +// The discovery order is: +// +// 1. Any tracefs mount listed in /proc/self/mountinfo (kernel 4.1+). +// This works regardless of where the mount sits in the filesystem, +// so containers that bind-mount tracefs at a non-canonical path are +// supported automatically. +// 2. A debugfs mount with a tracing/ subdirectory, for older systems +// where tracefs has not been lifted out of debugfs. +// 3. As a final fallback, probe the canonical kernel paths +// (/sys/kernel/tracing, /sys/kernel/debug/tracing) directly with +// statfs. This catches edge cases where /proc/self/mountinfo is +// unavailable or doesn't report the mount. var getTracefsPath = sync.OnceValues(func() (string, error) { if !platform.IsLinux { return "", fmt.Errorf("tracefs: %w", internal.ErrNotSupportedOnOS) } + if entries, err := mountinfo.Read(); err == nil { + if path := findTracefsInEntries(entries); path != "" { + return path, nil + } + } + for _, p := range []struct { path string fsType int64 @@ -135,6 +152,28 @@ var getTracefsPath = sync.OnceValues(func() (string, error) { return "", errors.New("neither debugfs nor tracefs are mounted") }) +// findTracefsInEntries returns the first occurrence of a tracefs in the +// given entries. +// +// Returns an empty string when no usable mount is found. +func findTracefsInEntries(entries []mountinfo.Entry) string { + for _, e := range entries { + if e.FSType == "tracefs" && e.Root == "/" { + return e.MountPoint + } + } + for _, e := range entries { + if e.FSType != "debugfs" || e.Root != "/" { + continue + } + tracing := filepath.Join(e.MountPoint, "tracing") + if info, err := os.Stat(tracing); err == nil && info.IsDir() { + return tracing + } + } + return "" +} + // sanitizeIdentifier replaces every invalid character for the tracefs api with an underscore. // // It is equivalent to calling regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString("_"). 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 03981610..7cb13372 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go @@ -33,6 +33,7 @@ const ( EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC O_RDONLY = linux.O_RDONLY + O_DIRECTORY = linux.O_DIRECTORY O_CLOEXEC = linux.O_CLOEXEC O_NONBLOCK = linux.O_NONBLOCK PROT_NONE = linux.PROT_NONE @@ -74,8 +75,18 @@ const ( BPF_RB_FORCE_WAKEUP = linux.BPF_RB_FORCE_WAKEUP AF_UNSPEC = linux.AF_UNSPEC IFF_UP = linux.IFF_UP - CLONE_NEWNET = linux.CLONE_NEWNET LINUX_CAPABILITY_VERSION_3 = linux.LINUX_CAPABILITY_VERSION_3 + CLONE_NEWNET = linux.CLONE_NEWNET + CLONE_NEWUSER = linux.CLONE_NEWUSER + CLONE_NEWNS = linux.CLONE_NEWNS + MOVE_MOUNT_F_EMPTY_PATH = linux.MOVE_MOUNT_F_EMPTY_PATH + AF_UNIX = linux.AF_UNIX + SOCK_STREAM = linux.SOCK_STREAM + SOCK_CLOEXEC = linux.SOCK_CLOEXEC + FSOPEN_CLOEXEC = linux.FSOPEN_CLOEXEC + FSMOUNT_CLOEXEC = linux.FSMOUNT_CLOEXEC + MSG_CMSG_CLOEXEC = linux.MSG_CMSG_CLOEXEC + SizeofInt = linux.SizeofInt ) type Statfs_t = linux.Statfs_t @@ -90,6 +101,7 @@ type Utsname = linux.Utsname type CPUSet = linux.CPUSet type CapUserData = linux.CapUserData type CapUserHeader = linux.CapUserHeader +type SysProcAttr = linux.SysProcAttr func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return linux.Syscall(trap, a1, a2, a3) @@ -231,3 +243,51 @@ func Capget(hdr *CapUserHeader, data *CapUserData) (err error) { func Capset(hdr *CapUserHeader, data *CapUserData) (err error) { return linux.Capset(hdr, data) } + +func Sendmsg(fd int, p []byte, oob []byte, to linux.Sockaddr, flags int) (err error) { + return linux.Sendmsg(fd, p, oob, to, flags) +} + +func Fsopen(fsname string, flags int) (fd int, err error) { + return linux.Fsopen(fsname, flags) +} + +func FsconfigSetString(fd int, key string, value string) error { + return linux.FsconfigSetString(fd, key, value) +} + +func FsconfigCreate(fd int) (err error) { + return linux.FsconfigCreate(fd) +} + +func Fsmount(fd int, flags int, mountAttrs int) (fsfd int, err error) { + return linux.Fsmount(fd, flags, mountAttrs) +} + +func MoveMount(fromDirfd int, fromPathName string, toDirfd int, toPathName string, flags int) (err error) { + return linux.MoveMount(fromDirfd, fromPathName, toDirfd, toPathName, flags) +} + +func UnixRights(fds ...int) []byte { + return linux.UnixRights(fds...) +} + +func Recvmsg(fd int, p []byte, oob []byte, flags int) (n int, oobn int, recvflags int, from linux.Sockaddr, err error) { + return linux.Recvmsg(fd, p, oob, flags) +} + +func Socketpair(domain int, typ int, proto int) (fd [2]int, err error) { + return linux.Socketpair(domain, typ, proto) +} + +func CmsgSpace(datalen int) int { + return linux.CmsgSpace(datalen) +} + +func ParseSocketControlMessage(b []byte) ([]linux.SocketControlMessage, error) { + return linux.ParseSocketControlMessage(b) +} + +func ParseUnixRights(m *linux.SocketControlMessage) ([]int, error) { + return linux.ParseUnixRights(m) +} 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 7ab8f929..5d75582e 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go @@ -33,6 +33,8 @@ const ( EPOLL_CLOEXEC O_CLOEXEC O_NONBLOCK + O_DIRECTORY + O_RDONLY PROT_NONE PROT_READ PROT_WRITE @@ -74,6 +76,11 @@ const ( AF_UNSPEC IFF_UP LINUX_CAPABILITY_VERSION_3 + SOCK_CLOEXEC + FSOPEN_CLOEXEC + FSMOUNT_CLOEXEC + MSG_CMSG_CLOEXEC + SizeofInt ) type Statfs_t struct { diff --git a/vendor/github.com/cilium/ebpf/link/link.go b/vendor/github.com/cilium/ebpf/link/link.go index 32be5898..57b328c2 100644 --- a/vendor/github.com/cilium/ebpf/link/link.go +++ b/vendor/github.com/cilium/ebpf/link/link.go @@ -114,7 +114,7 @@ type Info struct { Type Type ID ID Program ebpf.ProgramID - extra interface{} + extra any } // RawLink is the low-level API to bpf_link. diff --git a/vendor/github.com/cilium/ebpf/link/link_other.go b/vendor/github.com/cilium/ebpf/link/link_other.go index 2071b819..31904aaf 100644 --- a/vendor/github.com/cilium/ebpf/link/link_other.go +++ b/vendor/github.com/cilium/ebpf/link/link_other.go @@ -239,7 +239,7 @@ const ( type PerfEventInfo struct { Type sys.PerfEventType - extra interface{} + extra any } func (r *PerfEventInfo) Kprobe() *KprobeInfo { diff --git a/vendor/github.com/cilium/ebpf/link/perf_event.go b/vendor/github.com/cilium/ebpf/link/perf_event.go index a1455cab..7938b9d9 100644 --- a/vendor/github.com/cilium/ebpf/link/perf_event.go +++ b/vendor/github.com/cilium/ebpf/link/perf_event.go @@ -164,7 +164,7 @@ func (pl *perfEventLink) Info() (*Info, error) { return nil, fmt.Errorf("perf event link info: %s", err) } - var extra2 interface{} + var extra2 any switch info.PerfEventType { case PerfEventKprobe, PerfEventKretprobe: var kprobeInfo sys.KprobeLinkInfo diff --git a/vendor/github.com/cilium/ebpf/link/program.go b/vendor/github.com/cilium/ebpf/link/program.go index dbd7a972..4928800b 100644 --- a/vendor/github.com/cilium/ebpf/link/program.go +++ b/vendor/github.com/cilium/ebpf/link/program.go @@ -50,12 +50,11 @@ func RawAttachProgram(opts RawAttachProgramOptions) error { return fmt.Errorf("attach program: %w", err) } - if flags == sys.BPF_F_REPLACE { - // Ensure that replacing a program works on old kernels. + attr.AttachFlags |= flags + if flags&sys.BPF_F_REPLACE != 0 { attr.ReplaceBpfFd = fdOrID } else { attr.RelativeFdOrId = fdOrID - attr.AttachFlags |= flags } } diff --git a/vendor/github.com/cilium/ebpf/link/uprobe.go b/vendor/github.com/cilium/ebpf/link/uprobe.go index d53975d5..83333c4e 100644 --- a/vendor/github.com/cilium/ebpf/link/uprobe.go +++ b/vendor/github.com/cilium/ebpf/link/uprobe.go @@ -6,7 +6,6 @@ import ( "debug/elf" "errors" "fmt" - "io/fs" "os" "sync" @@ -35,8 +34,6 @@ var ( ErrNoSymbol = errors.New("not found") ) -const permExec fs.FileMode = 0111 - // Executable defines an executable program on the filesystem. type Executable struct { // Path of the executable on the filesystem. @@ -113,15 +110,10 @@ func OpenExecutable(path string) (*Executable, error) { return nil, fmt.Errorf("path cannot be empty") } - info, err := os.Stat(path) - if err != nil { + if _, err := os.Stat(path); err != nil { return nil, fmt.Errorf("stat executable: %w", err) } - if info.Mode()&permExec == 0 { - return nil, fmt.Errorf("file %s is not executable", path) - } - return &Executable{ path: path, cachedSymbols: make(map[string]symbol), @@ -264,12 +256,15 @@ func (ex *Executable) Symbol(address uint64) (SymbolOffset, error) { // ex, _ = OpenExecutable("/bin/bash") // ex.Uprobe("main", prog, nil) // -// When using symbols which belongs to shared libraries, -// an offset must be provided via options: +// When tracing a location for which no ELF symbol is available, or a shared +// library symbol whose static address is zero, set UprobeOptions.Address to +// the absolute file offset and pass an empty symbol: // -// up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123}) +// up, err := ex.Uprobe("", prog, &UprobeOptions{Address: 0x123}) // -// Note: Setting the Offset field in the options supersedes the symbol's offset. +// Address bypasses symbol lookup. Offset, if set, is added to the resolved +// address (whether from symbol resolution or from Address); on its own it +// does not replace symbol resolution. // // Losing the reference to the resulting Link (up) will close the Uprobe // and prevent further execution of prog. The Link must be Closed during @@ -300,12 +295,15 @@ func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti // ex, _ = OpenExecutable("/bin/bash") // ex.Uretprobe("main", prog, nil) // -// When using symbols which belongs to shared libraries, -// an offset must be provided via options: +// When tracing a location for which no ELF symbol is available, or a shared +// library symbol whose static address is zero, set UprobeOptions.Address to +// the absolute file offset and pass an empty symbol: // -// up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123}) +// up, err := ex.Uretprobe("", prog, &UprobeOptions{Address: 0x123}) // -// Note: Setting the Offset field in the options supersedes the symbol's offset. +// Address bypasses symbol lookup. Offset, if set, is added to the resolved +// address (whether from symbol resolution or from Address); on its own it +// does not replace symbol resolution. // // Losing the reference to the resulting Link (up) will close the Uprobe // and prevent further execution of prog. The Link must be Closed during diff --git a/vendor/github.com/cilium/ebpf/linker.go b/vendor/github.com/cilium/ebpf/linker.go index d23963ce..519c0aed 100644 --- a/vendor/github.com/cilium/ebpf/linker.go +++ b/vendor/github.com/cilium/ebpf/linker.go @@ -305,7 +305,8 @@ fixups: return nil, fmt.Errorf("kfuncMetaKey doesn't contain kfuncMeta") } - // findTargetInKernel returns btf.ErrNotFound if the input btf.Spec is nil. + // findTargetInKernel returns [btf.ErrNotFound] if the target can't be found + // or if BTF is not enabled. target := btf.Type((*btf.Func)(nil)) spec, module, err := findTargetInKernel(kfm.Func.Name, &target, cache) if errors.Is(err, btf.ErrNotFound) { diff --git a/vendor/github.com/cilium/ebpf/map.go b/vendor/github.com/cilium/ebpf/map.go index 78fc9957..884fbebf 100644 --- a/vendor/github.com/cilium/ebpf/map.go +++ b/vendor/github.com/cilium/ebpf/map.go @@ -263,8 +263,8 @@ func (ms *MapSpec) writeOnly() bool { // MapKV is used to initialize the contents of a Map. type MapKV struct { - Key interface{} - Value interface{} + Key any + Value any } // Compatible returns nil if an existing map may be used instead of creating @@ -312,13 +312,15 @@ func (ms *MapSpec) Compatible(m *Map) error { // Map represents a Map file descriptor. // -// It is not safe to close a map which is used by other goroutines. +// It is not safe to close a Map which is used by other goroutines. // -// Methods which take interface{} arguments by default encode -// them using binary.Read/Write in the machine's native endianness. +// Map operations taking `any` arguments are encoded using [sysenc.Marshal], +// which is zero-copy for fixed-size types without padding as well as slices of +// bytes, but may involve allocations for other types. See its documentation for +// details. // -// Implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler -// if you require custom encoding. +// Implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler if you +// require custom encoding or decoding. type Map struct { name string fd *sys.FD @@ -669,11 +671,16 @@ func handleMapCreateError(attr sys.MapCreateAttr, spec *MapSpec, err error) erro return err } + if errors.Is(err, sys.ErrTokenCapabilities) { + return fmt.Errorf("map create: %w", err) + } + if errors.Is(err, unix.EPERM) { return fmt.Errorf("map create: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err) } + if errors.Is(err, unix.EINVAL) { - if spec.MaxEntries == 0 { + if spec.MaxEntries == 0 && !spec.Type.mustHaveZeroMaxEntries() { return fmt.Errorf("map create: %w (MaxEntries may be incorrectly set to zero)", err) } if spec.Type == UnspecifiedMap { @@ -827,7 +834,7 @@ const LookupLock MapLookupFlags = sys.BPF_F_LOCK // and *valueOut is not nil. // // Returns an error if the key doesn't exist, see ErrKeyNotExist. -func (m *Map) Lookup(key, valueOut interface{}) error { +func (m *Map) Lookup(key, valueOut any) error { return m.LookupWithFlags(key, valueOut, 0) } @@ -841,7 +848,7 @@ func (m *Map) Lookup(key, valueOut interface{}) error { // and *valueOut is not nil. // // Returns an error if the key doesn't exist, see ErrKeyNotExist. -func (m *Map) LookupWithFlags(key, valueOut interface{}, flags MapLookupFlags) error { +func (m *Map) LookupWithFlags(key, valueOut any, flags MapLookupFlags) error { if m.typ.hasPerCPUValue() { return m.lookupPerCPU(key, valueOut, flags) } @@ -857,7 +864,7 @@ func (m *Map) LookupWithFlags(key, valueOut interface{}, flags MapLookupFlags) e // LookupAndDelete retrieves and deletes a value from a Map. // // Returns ErrKeyNotExist if the key doesn't exist. -func (m *Map) LookupAndDelete(key, valueOut interface{}) error { +func (m *Map) LookupAndDelete(key, valueOut any) error { return m.LookupAndDeleteWithFlags(key, valueOut, 0) } @@ -868,7 +875,7 @@ func (m *Map) LookupAndDelete(key, valueOut interface{}) error { // contain a spinlock. // // Returns ErrKeyNotExist if the key doesn't exist. -func (m *Map) LookupAndDeleteWithFlags(key, valueOut interface{}, flags MapLookupFlags) error { +func (m *Map) LookupAndDeleteWithFlags(key, valueOut any, flags MapLookupFlags) error { if m.typ.hasPerCPUValue() { return m.lookupAndDeletePerCPU(key, valueOut, flags) } @@ -883,7 +890,7 @@ func (m *Map) LookupAndDeleteWithFlags(key, valueOut interface{}, flags MapLooku // LookupBytes gets a value from Map. // // Returns a nil value if a key doesn't exist. -func (m *Map) LookupBytes(key interface{}) ([]byte, error) { +func (m *Map) LookupBytes(key any) ([]byte, error) { valueBytes := make([]byte, m.fullValueSize) valuePtr := sys.UnsafeSlicePointer(valueBytes) @@ -907,7 +914,7 @@ func (m *Map) lookupPerCPU(key, valueOut any, flags MapLookupFlags) error { return unmarshalPerCPUValue(slice, int(m.valueSize), valueBytes) } -func (m *Map) lookup(key interface{}, valueOut sys.Pointer, flags MapLookupFlags) error { +func (m *Map) lookup(key any, valueOut sys.Pointer, flags MapLookupFlags) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) @@ -943,6 +950,10 @@ func (m *Map) lookupAndDeletePerCPU(key, valueOut any, flags MapLookupFlags) err // ensurePerCPUSlice allocates a slice for a per-CPU value if necessary. func ensurePerCPUSlice(sliceOrPtr any) (any, error) { + if err := internal.IsNilPointer(sliceOrPtr); err != nil { + return nil, fmt.Errorf("per-cpu value: %w", err) + } + sliceOrPtrType := reflect.TypeOf(sliceOrPtr) if sliceOrPtrType.Kind() == reflect.Slice { // The target is a slice, the caller is responsible for ensuring that @@ -951,7 +962,7 @@ func ensurePerCPUSlice(sliceOrPtr any) (any, error) { } slicePtrType := sliceOrPtrType - if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice { + if slicePtrType.Kind() != reflect.Pointer || slicePtrType.Elem().Kind() != reflect.Slice { return nil, fmt.Errorf("per-cpu value requires a slice or a pointer to slice") } @@ -964,14 +975,14 @@ func ensurePerCPUSlice(sliceOrPtr any) (any, error) { slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs) sliceElemType := sliceType.Elem() - sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr + sliceElemIsPointer := sliceElemType.Kind() == reflect.Pointer reflect.ValueOf(sliceOrPtr).Elem().Set(slice) if !sliceElemIsPointer { return slice.Interface(), nil } sliceElemType = sliceElemType.Elem() - for i := 0; i < possibleCPUs; i++ { + for i := range possibleCPUs { newElem := reflect.New(sliceElemType) slice.Index(i).Set(newElem) } @@ -1018,7 +1029,7 @@ const ( // Put replaces or creates a value in map. // // It is equivalent to calling Update with UpdateAny. -func (m *Map) Put(key, value interface{}) error { +func (m *Map) Put(key, value any) error { return m.Update(key, value, UpdateAny) } @@ -1068,7 +1079,7 @@ func (m *Map) update(key any, valuePtr sys.Pointer, flags MapUpdateFlags) error // Delete removes a value. // // Returns ErrKeyNotExist if the key does not exist. -func (m *Map) Delete(key interface{}) error { +func (m *Map) Delete(key any) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) @@ -1090,7 +1101,7 @@ func (m *Map) Delete(key interface{}) error { // See NextKeyBytes for details. // // Returns ErrKeyNotExist if there is no next key. -func (m *Map) NextKey(key, nextKeyOut interface{}) error { +func (m *Map) NextKey(key, nextKeyOut any) error { nextKeyBytes := makeMapSyscallOutput(nextKeyOut, int(m.keySize)) if err := m.nextKey(key, nextKeyBytes.Pointer()); err != nil { @@ -1110,7 +1121,7 @@ func (m *Map) NextKey(key, nextKeyOut interface{}) error { // Use Iterate if you want to traverse all entries in the map. // // Returns nil if there are no more keys. -func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) { +func (m *Map) NextKeyBytes(key any) ([]byte, error) { nextKey := make([]byte, m.keySize) nextKeyPtr := sys.UnsafeSlicePointer(nextKey) @@ -1122,7 +1133,7 @@ func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) { return nextKey, err } -func (m *Map) nextKey(key interface{}, nextKeyOut sys.Pointer) error { +func (m *Map) nextKey(key any, nextKeyOut sys.Pointer) error { var ( keyPtr sys.Pointer err error @@ -1182,7 +1193,7 @@ func (m *Map) guessNonExistentKey() ([]byte, error) { randKey := make([]byte, int(m.keySize)) - for i := 0; i < 4; i++ { + for i := range 4 { switch i { // For hash maps, the 0 key is less likely to be occupied. They're often // used for storing data related to pointers, and their access pattern is @@ -1231,7 +1242,7 @@ func (m *Map) guessNonExistentKey() ([]byte, error) { // ErrKeyNotExist is returned when the batch lookup has reached // the end of all possible results, even when partial results // are returned. It should be used to evaluate when lookup is "done". -func (m *Map) BatchLookup(cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { +func (m *Map) BatchLookup(cursor *MapBatchCursor, keysOut, valuesOut any, opts *BatchOptions) (int, error) { n, err := m.batchLookup(sys.BPF_MAP_LOOKUP_BATCH, cursor, keysOut, valuesOut, opts) if err != nil { return n, fmt.Errorf("map batch lookup: %w", err) @@ -1255,7 +1266,7 @@ func (m *Map) BatchLookup(cursor *MapBatchCursor, keysOut, valuesOut interface{} // ErrKeyNotExist is returned when the batch lookup has reached // the end of all possible results, even when partial results // are returned. It should be used to evaluate when lookup is "done". -func (m *Map) BatchLookupAndDelete(cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { +func (m *Map) BatchLookupAndDelete(cursor *MapBatchCursor, keysOut, valuesOut any, opts *BatchOptions) (int, error) { n, err := m.batchLookup(sys.BPF_MAP_LOOKUP_AND_DELETE_BATCH, cursor, keysOut, valuesOut, opts) if err != nil { return n, fmt.Errorf("map batch lookup and delete: %w", err) @@ -1269,7 +1280,7 @@ type MapBatchCursor struct { opaque []byte } -func (m *Map) batchLookup(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { +func (m *Map) batchLookup(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut any, opts *BatchOptions) (int, error) { if m.typ.hasPerCPUValue() { return m.batchLookupPerCPU(cmd, cursor, keysOut, valuesOut, opts) } @@ -1298,7 +1309,7 @@ func (m *Map) batchLookup(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOu return n, sysErr } -func (m *Map) batchLookupPerCPU(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { +func (m *Map) batchLookupPerCPU(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut any, opts *BatchOptions) (int, error) { count, err := sliceLen(keysOut) if err != nil { return 0, fmt.Errorf("keys: %w", err) @@ -1344,10 +1355,6 @@ func (m *Map) batchLookupCmd(cmd sys.Cmd, cursor *MapBatchCursor, count int, key return 0, errors.New("a cursor may not be reused across maps") } - if err := haveBatchAPI(); err != nil { - return 0, err - } - keyBuf := sysenc.SyscallOutput(keysOut, count*int(m.keySize)) attr := sys.MapLookupBatchAttr{ @@ -1367,6 +1374,9 @@ func (m *Map) batchLookupCmd(cmd sys.Cmd, cursor *MapBatchCursor, count int, key _, sysErr := sys.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) sysErr = wrapMapError(sysErr) if sysErr != nil && !errors.Is(sysErr, unix.ENOENT) { + if haveFeatErr := haveBatchAPI(); haveFeatErr != nil { + return 0, haveFeatErr + } return 0, sysErr } @@ -1381,7 +1391,7 @@ func (m *Map) batchLookupCmd(cmd sys.Cmd, cursor *MapBatchCursor, count int, key // simultaneously. // "keys" and "values" must be of type slice, a pointer // to a slice or buffer will not work. -func (m *Map) BatchUpdate(keys, values interface{}, opts *BatchOptions) (int, error) { +func (m *Map) BatchUpdate(keys, values any, opts *BatchOptions) (int, error) { if m.typ.hasPerCPUValue() { return m.batchUpdatePerCPU(keys, values, opts) } @@ -1443,7 +1453,7 @@ func (m *Map) batchUpdatePerCPU(keys, values any, opts *BatchOptions) (int, erro // BatchDelete batch deletes entries in the map by keys. // "keys" must be of type slice, a pointer to a slice or buffer will not work. -func (m *Map) BatchDelete(keys interface{}, opts *BatchOptions) (int, error) { +func (m *Map) BatchDelete(keys any, opts *BatchOptions) (int, error) { count, err := sliceLen(keys) if err != nil { return 0, fmt.Errorf("keys: %w", err) @@ -1623,7 +1633,7 @@ func (m *Map) finalize(spec *MapSpec) error { return nil } -func (m *Map) marshalKey(data interface{}) (sys.Pointer, error) { +func (m *Map) marshalKey(data any) (sys.Pointer, error) { if data == nil { if m.keySize == 0 { // Queues have a key length of zero, so passing nil here is valid. @@ -1635,7 +1645,7 @@ func (m *Map) marshalKey(data interface{}) (sys.Pointer, error) { return marshalMapSyscallInput(data, int(m.keySize)) } -func (m *Map) marshalValue(data interface{}) (sys.Pointer, error) { +func (m *Map) marshalValue(data any) (sys.Pointer, error) { var ( buf []byte err error @@ -1794,7 +1804,7 @@ func newMapIterator(target *Map) *MapIterator { // the result of Err afterwards. // // See Map.Get for further caveats around valueOut. -func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool { +func (mi *MapIterator) Next(keyOut, valueOut any) bool { if mi.err != nil || mi.done { return false } diff --git a/vendor/github.com/cilium/ebpf/marshalers.go b/vendor/github.com/cilium/ebpf/marshalers.go index d4e719c6..2542f2d0 100644 --- a/vendor/github.com/cilium/ebpf/marshalers.go +++ b/vendor/github.com/cilium/ebpf/marshalers.go @@ -62,7 +62,7 @@ func appendPerCPUSlice(buf []byte, slice any, possibleCPUs, elemLength, alignedE // Grow increases the slice's capacity, _if_necessary_ buf = slices.Grow(buf, alignedElemLength*possibleCPUs) - for i := 0; i < sliceLen; i++ { + for i := range sliceLen { elem := sliceValue.Index(i).Interface() elemBytes, err := sysenc.Marshal(elem, elemLength) if err != nil { @@ -118,7 +118,7 @@ func marshalBatchPerCPUValue(slice any, batchLen, elemLength int) ([]byte, error } alignedElemLength := internal.Align(elemLength, 8) buf := make([]byte, 0, batchLen*alignedElemLength*possibleCPUs) - for i := 0; i < batchLen; i++ { + for i := range batchLen { batch := sliceValue.Slice(i*possibleCPUs, (i+1)*possibleCPUs).Interface() buf, err = appendPerCPUSlice(buf, batch, possibleCPUs, elemLength, alignedElemLength) if err != nil { @@ -150,9 +150,9 @@ func unmarshalPerCPUValue(slice any, elemLength int, buf []byte) error { } sliceElemType := sliceType.Elem() - sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr + sliceElemIsPointer := sliceElemType.Kind() == reflect.Pointer stride := internal.Align(elemLength, 8) - for i := 0; i < possibleCPUs; i++ { + for i := range possibleCPUs { var elem any v := sliceValue.Index(i) if sliceElemIsPointer { @@ -199,7 +199,7 @@ func unmarshalBatchPerCPUValue(slice any, batchLen, elemLength int, buf []byte) len(buf), batchLen*fullValueSize) } - for i := 0; i < batchLen; i++ { + for i := range batchLen { elem := sliceValue.Slice(i*possibleCPUs, (i+1)*possibleCPUs).Interface() if err := unmarshalPerCPUValue(elem, elemLength, buf[:fullValueSize]); err != nil { return fmt.Errorf("batch %d: %w", i, err) diff --git a/vendor/github.com/cilium/ebpf/memory_unsafe.go b/vendor/github.com/cilium/ebpf/memory_unsafe.go index 07463e82..7e00b9e6 100644 --- a/vendor/github.com/cilium/ebpf/memory_unsafe.go +++ b/vendor/github.com/cilium/ebpf/memory_unsafe.go @@ -118,13 +118,10 @@ func allocate(size int) (unsafe.Pointer, error) { // Allocate a new slice and store a pointer to its backing array. alloc := unsafe.Pointer(unsafe.SliceData(make([]byte, size))) - // nolint:govet - // // Align the pointer to a page boundary within the allocation. This may alias - // the initial pointer if it was already page-aligned. Ignore govet warnings - // since we're calling [runtime.KeepAlive] on the original Go memory. - aligned := unsafe.Pointer(internal.Align(uintptr(alloc), uintptr(os.Getpagesize()))) - runtime.KeepAlive(alloc) + // the initial pointer if it was already page-aligned. + aligned := internal.Align(uintptr(alloc), uintptr(os.Getpagesize())) + alloc = unsafe.Add(alloc, aligned-uintptr(alloc)) // Return an aligned pointer into the backing array, losing the original // reference. The runtime.SetFinalizer docs specify that its argument 'must be @@ -142,7 +139,7 @@ func allocate(size int) (unsafe.Pointer, error) { // pointer separately, which severely complicates finalizer setup and makes it // prone to human error. For now, just bump the pointer and treat it as the // new and only reference to the backing array. - return aligned, nil + return alloc, nil } // mapmap memory-maps the given file descriptor at the given address and sets a @@ -246,7 +243,7 @@ func checkUnsafeMemory[T comparable](mm *Memory, off uint32) error { } vs, bs := uint32(size), uint32(len(mm.b)) - if off+vs > bs { + if !mm.bounds(off, vs) { return fmt.Errorf("%d-byte value at offset %d exceeds mmap size of %d bytes", vs, off, bs) } diff --git a/vendor/github.com/cilium/ebpf/prog.go b/vendor/github.com/cilium/ebpf/prog.go index 0394b02f..9d120ab6 100644 --- a/vendor/github.com/cilium/ebpf/prog.go +++ b/vendor/github.com/cilium/ebpf/prog.go @@ -242,6 +242,8 @@ type Program struct { name string pinnedPath string typ ProgramType + + btf *btf.Handle } // NewProgram creates a new Program. @@ -274,15 +276,15 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er } var ( - coreBadLoad = []byte(fmt.Sprintf("(18) r10 = 0x%x\n", btf.COREBadRelocationSentinel)) + coreBadLoad = fmt.Appendf(nil, "(18) r10 = 0x%x\n", btf.COREBadRelocationSentinel) // This log message was introduced by ebb676daa1a3 ("bpf: Print function name in // addition to function id") which first appeared in v4.10 and has remained // unchanged since. - coreBadCall = []byte(fmt.Sprintf("invalid func unknown#%d\n", btf.COREBadRelocationSentinel)) - kfuncBadCall = []byte(fmt.Sprintf("invalid func unknown#%d\n", kfuncCallPoisonBase)) + coreBadCall = fmt.Appendf(nil, "invalid func unknown#%d\n", btf.COREBadRelocationSentinel) + kfuncBadCall = fmt.Appendf(nil, "invalid func unknown#%d\n", kfuncCallPoisonBase) ) -func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache) (*Program, error) { +func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache) (result *Program, _ error) { if len(spec.Instructions) == 0 { return nil, errors.New("instructions cannot be empty") } @@ -356,12 +358,18 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache) attr.LineInfo = sys.SlicePointer(lib) } + var handle *btf.Handle if !b.Empty() { - handle, err := btf.NewHandle(&b) + var err error + handle, err = btf.NewHandle(&b) if err != nil { return nil, fmt.Errorf("load BTF: %w", err) } - defer handle.Close() + defer func() { + if result == nil || result.btf != handle { + handle.Close() + } + }() attr.ProgBtfFd = uint32(handle.FD()) } @@ -471,7 +479,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache) // Loading with logging disabled should never retry. fd, err = sys.ProgLoad(attr) if err == nil { - return &Program{"", fd, spec.Name, "", spec.Type}, nil + return &Program{"", fd, spec.Name, "", spec.Type, handle}, nil } } else { // Only specify log size if log level is also specified. Setting size @@ -491,7 +499,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache) fd, err = sys.ProgLoad(attr) if err == nil { - return &Program{unix.ByteSliceToString(logBuf), fd, spec.Name, "", spec.Type}, nil + return &Program{unix.ByteSliceToString(logBuf), fd, spec.Name, "", spec.Type, handle}, nil } if !retryLogAttrs(attr, opts.LogSizeStart, err) { @@ -505,6 +513,10 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache) } } + if errors.Is(err, sys.ErrTokenCapabilities) { + return nil, fmt.Errorf("load program: %w", err) + } + end := bytes.IndexByte(logBuf, 0) if end < 0 { end = len(logBuf) @@ -632,7 +644,7 @@ func newProgramFromFD(fd *sys.FD) (*Program, error) { return nil, fmt.Errorf("discover program type: %w", err) } - return &Program{"", fd, info.Name, "", info.Type}, nil + return &Program{"", fd, info.Name, "", info.Type, nil}, nil } func (p *Program) String() string { @@ -667,6 +679,10 @@ func (p *Program) Stats() (*ProgramStats, error) { // Returns ErrNotSupported if the kernel has no BTF support, or if there is no // BTF associated with the program. func (p *Program) Handle() (*btf.Handle, error) { + if p.btf != nil { + return p.btf.Clone() + } + info, err := p.Info() if err != nil { return nil, err @@ -680,6 +696,48 @@ func (p *Program) Handle() (*btf.Handle, error) { return btf.NewHandleFromID(id) } +// SetHandle caches program's type information so that subsequent +// Handle() call doesn't need to query it from the kernel (requires +// CAP_SYS_ADMIN). +// +// The caller remains responsible for closing the btf.Handle; SetHandle +// makes a private copy. +// +// Type info is auto-cached for Programs loaded by the library itself. +// Use SetHandle() with program references obtained via NewProgramFromFD +// and LoadPinnedProgram. +func (p *Program) SetHandle(h *btf.Handle) error { + if h == nil { + return fmt.Errorf("nil BTF handle") + } + + info, err := p.Info() + if err != nil { + return err + } + + handleInfo, err := h.Info() + if err != nil { + return err + } + + if id, ok := info.BTFID(); !ok || id != handleInfo.ID { + return fmt.Errorf("program/BTF mismatch") + } + + if p.btf != nil { + return nil + } + + dup, err := h.Clone() + if err != nil { + return err + } + + p.btf = dup + return nil +} + // FD gets the file descriptor of the Program. // // It is invalid to call this function after Close has been called. @@ -697,12 +755,18 @@ func (p *Program) Clone() (*Program, error) { return nil, nil } + btfClone, err := p.btf.Clone() + if err != nil { + return nil, fmt.Errorf("can't clone program: %w", err) + } + dup, err := p.fd.Dup() if err != nil { + btfClone.Close() return nil, fmt.Errorf("can't clone program: %w", err) } - return &Program{p.VerifierLog, dup, p.name, "", p.typ}, nil + return &Program{p.VerifierLog, dup, p.name, "", p.typ, btfClone}, nil } // Pin persists the Program on the BPF virtual file system past the lifetime of @@ -747,6 +811,10 @@ func (p *Program) Close() error { return nil } + if err := p.btf.Close(); err != nil { + return err + } + return p.fd.Close() } @@ -760,9 +828,9 @@ type RunOptions struct { // Program's data after Program has run. Caller must allocate. Optional field. DataOut []byte // Program's context input. Optional field. - Context interface{} + Context any // Program's context after Program has run. Must be a pointer or slice. Optional field. - ContextOut interface{} + ContextOut any // Minimum number of times to run Program. Optional field. Defaults to 1. // // The program may be executed more often than this due to interruptions, e.g. @@ -1174,12 +1242,12 @@ func findProgramTargetInKernel(name string, progType ProgramType, attachType Att // target will point at the found type after a successful call. Searches both // vmlinux and any loaded modules. // -// Returns a non-nil handle if the type was found in a module, or btf.ErrNotFound -// if the type wasn't found at all. +// Returns a non-nil handle if the type was found in a module, [btf.ErrNotFound] +// if the type wasn't found or if BTF is not enabled. func findTargetInKernel(typeName string, target *btf.Type, cache *btf.Cache) (*btf.Spec, *btf.Handle, error) { kernelSpec, err := cache.Kernel() if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("load kernel spec: %w (%w)", btf.ErrNotFound, err) } err = kernelSpec.TypeByName(typeName, target) diff --git a/vendor/github.com/cilium/ebpf/struct_ops.go b/vendor/github.com/cilium/ebpf/struct_ops.go index 3b70d56d..78e4d35b 100644 --- a/vendor/github.com/cilium/ebpf/struct_ops.go +++ b/vendor/github.com/cilium/ebpf/struct_ops.go @@ -15,6 +15,58 @@ const structOpsLinkSec = ".struct_ops.link" const structOpsSec = ".struct_ops" const structOpsKeySize = 4 +// structOpsMemberLayout describes the location and type of a struct_ops member. +type structOpsMemberLayout struct { + member btf.Member + off int + size int + typ btf.Type +} + +// newStructOpsMemberLayout returns a layout information from a struct_ops member. +func newStructOpsMemberLayout(m btf.Member) (*structOpsMemberLayout, error) { + if m.BitfieldSize > 0 { + return nil, fmt.Errorf("bitfield %s not supported", m.Name) + } + + size, err := btf.Sizeof(m.Type) + if err != nil { + return nil, fmt.Errorf("sizeof(%s): %w", m.Name, err) + } + + off := int(m.Offset.Bytes()) + if off < 0 { + return nil, fmt.Errorf("member %q: invalid offset", m.Name) + } + + return &structOpsMemberLayout{ + member: m, + off: off, + size: size, + typ: btf.UnderlyingType(m.Type), + }, nil +} + +// bytes returns the portion of `buf` corresponding to the member. +func (ml *structOpsMemberLayout) bytes(buf []byte) ([]byte, error) { + if ml.off < 0 || ml.off+ml.size > len(buf) { + return nil, fmt.Errorf("member %q: value buffer too small", ml.member.Name) + } + return buf[ml.off : ml.off+ml.size], nil +} + +// structOpsFuncPtrMember returns an error unless m is a func pointer member. +func structOpsFuncPtrMember(m btf.Member) error { + kmPtr, ok := btf.As[*btf.Pointer](m.Type) + if !ok { + return fmt.Errorf("member %s is not a func pointer", m.Name) + } + if _, isFuncProto := btf.As[*btf.FuncProto](kmPtr.Target); !isFuncProto { + return fmt.Errorf("member %s is not a func pointer", m.Name) + } + return nil +} + // structOpsFindInnerType returns the "inner" struct inside a value struct_ops type. // // Given a value like: @@ -67,74 +119,97 @@ func structOpsFindTarget(userType *btf.Struct, cache *btf.Cache) (vType *btf.Str // struct_ops value buffer `kernVData` at byte offset `dstOff` corresponding to // the member `km`. func structOpsPopulateValue(km btf.Member, kernVData []byte, p *Program) error { - kmPtr, ok := btf.As[*btf.Pointer](km.Type) - if !ok { - return fmt.Errorf("member %s is not a func pointer", km.Name) + if err := structOpsFuncPtrMember(km); err != nil { + return err } - if _, isFuncProto := btf.As[*btf.FuncProto](kmPtr.Target); !isFuncProto { - return fmt.Errorf("member %s is not a func pointer", km.Name) + layout, err := newStructOpsMemberLayout(km) + if err != nil { + return err } - dstOff := int(km.Offset.Bytes()) - if dstOff < 0 || dstOff+8 > len(kernVData) { + dst, err := layout.bytes(kernVData) + if err != nil || len(dst) != 8 { return fmt.Errorf("member %q: value buffer too small for func ptr", km.Name) } - internal.NativeEndian.PutUint64(kernVData[dstOff:dstOff+8], uint64(p.FD())) + internal.NativeEndian.PutUint64(dst, uint64(p.FD())) return nil } -// structOpsCopyMember copies a single member from the user struct (m) -// into the kernel value struct (km) for struct_ops. -func structOpsCopyMember(m, km btf.Member, data []byte, kernVData []byte) error { - mSize, err := btf.Sizeof(m.Type) +// structOpsValidateMemberPair checks whether `m` can be copied into `km`. +func structOpsValidateMemberPair(m, km btf.Member) (int, error) { + mLayout, err := newStructOpsMemberLayout(m) if err != nil { - return fmt.Errorf("sizeof(user.%s): %w", m.Name, err) + return 0, fmt.Errorf("user member %s: %w", m.Name, err) } - kSize, err := btf.Sizeof(km.Type) + + kLayout, err := newStructOpsMemberLayout(km) if err != nil { - return fmt.Errorf("sizeof(kernel.%s): %w", km.Name, err) + return 0, fmt.Errorf("kernel member %s: %w", km.Name, err) } - if mSize != kSize { - return fmt.Errorf("size mismatch for %s: user=%d kernel=%d", m.Name, mSize, kSize) + + if mLayout.size != kLayout.size { + return 0, fmt.Errorf("size mismatch for %s: user=%d kernel=%d", m.Name, mLayout.size, kLayout.size) } - if km.BitfieldSize > 0 || m.BitfieldSize > 0 { - return fmt.Errorf("bitfield %s not supported", m.Name) + + if reflect.TypeOf(mLayout.typ) != reflect.TypeOf(kLayout.typ) { + return 0, fmt.Errorf("unmatched member type %s != %s (kernel)", m.Name, km.Name) } - srcOff := int(m.Offset.Bytes()) - dstOff := int(km.Offset.Bytes()) + return mLayout.size, nil +} - if srcOff < 0 || srcOff+mSize > len(data) { - return fmt.Errorf("member %q: userdata is too small", m.Name) +// structOpsCopyMemberBytes copies the bytes of `m` into `km`. +func structOpsCopyMemberBytes(m, km btf.Member, data, kernVData []byte, size int) error { + mLayout, err := newStructOpsMemberLayout(m) + if err != nil { + return fmt.Errorf("user member %s: %w", m.Name, err) + } + + kLayout, err := newStructOpsMemberLayout(km) + if err != nil { + return fmt.Errorf("kernel member %s: %w", km.Name, err) } - if dstOff < 0 || dstOff+mSize > len(kernVData) { - return fmt.Errorf("member %q: value type is too small", m.Name) + if mLayout.size != size { + return fmt.Errorf("member %q: unexpected validated size %d, got %d", m.Name, size, mLayout.size) + } + if kLayout.size != size { + return fmt.Errorf("member %q: unexpected validated size %d, got %d", km.Name, size, kLayout.size) + } + + src, err := mLayout.bytes(data) + if err != nil { + return fmt.Errorf("member %q: userdata is too small", m.Name) } - // skip mods(const, restrict, volatile and typetag) - // and typedef to check type compatibility - mType := btf.UnderlyingType(m.Type) - kernMType := btf.UnderlyingType(km.Type) - if reflect.TypeOf(mType) != reflect.TypeOf(kernMType) { - return fmt.Errorf("unmatched member type %s != %s (kernel)", m.Name, km.Name) + dst, err := kLayout.bytes(kernVData) + if err != nil { + return fmt.Errorf("member %q: value type is too small", km.Name) } - switch mType.(type) { + switch mLayout.typ.(type) { case *btf.Struct, *btf.Union: - if !structOpsIsMemZeroed(data[srcOff : srcOff+mSize]) { + if !structOpsIsMemZeroed(src) { return fmt.Errorf("non-zero nested struct %s: %w", m.Name, ErrNotSupported) } - // the bytes has zeroed value, we simply skip the copy. return nil } - copy(kernVData[dstOff:dstOff+mSize], data[srcOff:srcOff+mSize]) + copy(dst, src) return nil } +// structOpsCopyMember copies `m` into `km`. +func structOpsCopyMember(m, km btf.Member, data []byte, kernVData []byte) error { + size, err := structOpsValidateMemberPair(m, km) + if err != nil { + return err + } + return structOpsCopyMemberBytes(m, km, data, kernVData, size) +} + // structOpsIsMemZeroed() checks whether all bytes in data are zero. func structOpsIsMemZeroed(data []byte) bool { for _, b := range data { diff --git a/vendor/github.com/cilium/ebpf/types.go b/vendor/github.com/cilium/ebpf/types.go index 52ff75b5..f87715aa 100644 --- a/vendor/github.com/cilium/ebpf/types.go +++ b/vendor/github.com/cilium/ebpf/types.go @@ -188,6 +188,16 @@ func (mt MapType) mustHaveNoPrealloc() bool { return false } +// mustHaveZeroMaxEntries returns true if the map type requires MaxEntries to be zero. +func (mt MapType) mustHaveZeroMaxEntries() bool { + switch mt { + case CgroupStorage, CGroupStorage, PerCPUCGroupStorage, InodeStorage, TaskStorage, SkStorage: + return true + } + + return false +} + // ProgramType of the eBPF program type ProgramType uint32 diff --git a/vendor/github.com/cilium/ebpf/variable.go b/vendor/github.com/cilium/ebpf/variable.go index 003ef89c..51691bb4 100644 --- a/vendor/github.com/cilium/ebpf/variable.go +++ b/vendor/github.com/cilium/ebpf/variable.go @@ -125,7 +125,7 @@ type Variable struct { func newVariable(name string, offset, size uint32, t *btf.Var, mm *Memory) (*Variable, error) { if mm != nil { - if offset+size > mm.Size() { + if !mm.bounds(offset, size) { return nil, fmt.Errorf("offset %d(+%d) is out of bounds", offset, size) } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 28b6eb01..5c350853 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -195,8 +195,8 @@ github.com/cespare/xxhash/v2 # github.com/chigopher/pathlib v0.19.1 ## explicit; go 1.21 github.com/chigopher/pathlib -# github.com/cilium/ebpf v0.21.0 -## explicit; go 1.24.0 +# github.com/cilium/ebpf v0.22.0 +## explicit; go 1.25.0 github.com/cilium/ebpf github.com/cilium/ebpf/asm github.com/cilium/ebpf/btf @@ -209,6 +209,7 @@ github.com/cilium/ebpf/internal/efw github.com/cilium/ebpf/internal/kallsyms github.com/cilium/ebpf/internal/kconfig github.com/cilium/ebpf/internal/linux +github.com/cilium/ebpf/internal/mountinfo github.com/cilium/ebpf/internal/platform github.com/cilium/ebpf/internal/sys github.com/cilium/ebpf/internal/sysenc