diff --git a/Makefile b/Makefile index 8173c71f2..614041335 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ OCI_BIN_PATH := $(shell which docker 2>/dev/null || which podman) OCI_BIN ?= $(shell basename ${OCI_BIN_PATH}) LOCAL_GENERATOR_IMAGE ?= ebpf-generator:latest -CILIUM_EBPF_VERSION := v0.14.0 +CILIUM_EBPF_VERSION := v0.15.0 GOLANGCI_LINT_VERSION = v1.54.2 GO_VERSION = "1.21.7" PROTOC_VERSION = "3.19.4" diff --git a/go.mod b/go.mod index 9a242fab8..d8fe002ae 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.1 require ( github.com/caarlos0/env/v6 v6.10.1 - github.com/cilium/ebpf v0.14.0 + github.com/cilium/ebpf v0.15.0 github.com/fsnotify/fsnotify v1.7.0 github.com/gavv/monotime v0.0.0-20190418164738-30dba4353424 github.com/google/gopacket v1.1.19 diff --git a/go.sum b/go.sum index 2fec04d2d..b1c728995 100644 --- a/go.sum +++ b/go.sum @@ -132,8 +132,8 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.14.0 h1:0PsxAjO6EjI1rcT+rkp6WcCnE0ZvfkXBYiMedJtrSUs= -github.com/cilium/ebpf v0.14.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= +github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= +github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= diff --git a/vendor/github.com/cilium/ebpf/Makefile b/vendor/github.com/cilium/ebpf/Makefile index 3727a6e11..c55a93d9c 100644 --- a/vendor/github.com/cilium/ebpf/Makefile +++ b/vendor/github.com/cilium/ebpf/Makefile @@ -44,6 +44,7 @@ TARGETS := \ testdata/invalid-kfunc \ testdata/kfunc-kmod \ testdata/constants \ + testdata/errors \ btf/testdata/relocs \ btf/testdata/relocs_read \ btf/testdata/relocs_read_tgt \ diff --git a/vendor/github.com/cilium/ebpf/README.md b/vendor/github.com/cilium/ebpf/README.md index b36f8c0be..85871db1a 100644 --- a/vendor/github.com/cilium/ebpf/README.md +++ b/vendor/github.com/cilium/ebpf/README.md @@ -58,8 +58,8 @@ This library includes the following packages: * A version of Go that is [supported by upstream](https://golang.org/doc/devel/release.html#policy) -* Linux >= 4.9. CI is run against kernel.org LTS releases. 4.4 should work but is - not tested against. +* CI is run against kernel.org LTS releases. >= 4.4 should work but EOL'ed versions + are not supported. ## License diff --git a/vendor/github.com/cilium/ebpf/btf/btf.go b/vendor/github.com/cilium/ebpf/btf/btf.go index 3d924672c..204757dbf 100644 --- a/vendor/github.com/cilium/ebpf/btf/btf.go +++ b/vendor/github.com/cilium/ebpf/btf/btf.go @@ -90,23 +90,7 @@ func (mt *mutableTypes) add(typ Type, typeIDs map[Type]TypeID) Type { mt.mu.Lock() defer mt.mu.Unlock() - return modifyGraphPreorder(typ, func(t Type) (Type, bool) { - cpy, ok := mt.copies[t] - if ok { - // This has been copied previously, no need to continue. - return cpy, false - } - - cpy = t.copy() - mt.copies[t] = cpy - - if id, ok := typeIDs[t]; ok { - mt.copiedTypeIDs[cpy] = id - } - - // This is a new copy, keep copying children. - return cpy, true - }) + return copyType(typ, typeIDs, mt.copies, mt.copiedTypeIDs) } // copy a set of mutable types. @@ -122,17 +106,14 @@ func (mt *mutableTypes) copy() mutableTypes { mt.mu.RLock() defer mt.mu.RUnlock() - copies := make(map[Type]Type, len(mt.copies)) + copiesOfCopies := make(map[Type]Type, len(mt.copies)) for orig, copy := range mt.copies { // NB: We make a copy of copy, not orig, so that changes to mutable types // are preserved. - copyOfCopy := mtCopy.add(copy, mt.copiedTypeIDs) - copies[orig] = copyOfCopy + copyOfCopy := copyType(copy, mt.copiedTypeIDs, copiesOfCopies, mtCopy.copiedTypeIDs) + mtCopy.copies[orig] = copyOfCopy } - // mtCopy.copies is currently map[copy]copyOfCopy, replace it with - // map[orig]copyOfCopy. - mtCopy.copies = copies return mtCopy } @@ -582,7 +563,7 @@ func (s *Spec) TypeByID(id TypeID) (Type, error) { // TypeID returns the ID for a given Type. // -// Returns an error wrapping ErrNoFound if the type isn't part of the Spec. +// Returns an error wrapping [ErrNotFound] if the type isn't part of the Spec. func (s *Spec) TypeID(typ Type) (TypeID, error) { return s.mutableTypes.typeID(typ) } diff --git a/vendor/github.com/cilium/ebpf/btf/core.go b/vendor/github.com/cilium/ebpf/btf/core.go index a3d311a06..ee89f9833 100644 --- a/vendor/github.com/cilium/ebpf/btf/core.go +++ b/vendor/github.com/cilium/ebpf/btf/core.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "reflect" + "slices" "strconv" "strings" @@ -15,6 +16,11 @@ import ( // Code in this file is derived from libbpf, which is available under a BSD // 2-Clause license. +// A constant used when CO-RE relocation has to remove instructions. +// +// Taken from libbpf. +const COREBadRelocationSentinel = 0xbad2310 + // COREFixup is the result of computing a CO-RE relocation for a target. type COREFixup struct { kind coreKind @@ -41,17 +47,22 @@ func (f *COREFixup) String() string { func (f *COREFixup) Apply(ins *asm.Instruction) error { if f.poison { - const badRelo = 0xbad2310 - // Relocation is poisoned, replace the instruction with an invalid one. - if ins.OpCode.IsDWordLoad() { // Replace a dword load with a invalid dword load to preserve instruction size. - *ins = asm.LoadImm(asm.R10, badRelo, asm.DWord) + *ins = asm.LoadImm(asm.R10, COREBadRelocationSentinel, asm.DWord) } else { // Replace all single size instruction with a invalid call instruction. - *ins = asm.BuiltinFunc(badRelo).Call() + *ins = asm.BuiltinFunc(COREBadRelocationSentinel).Call() + } + + // Add context to the kernel verifier output. + if source := ins.Source(); source != nil { + *ins = ins.WithSource(asm.Comment(fmt.Sprintf("instruction poisoned by CO-RE: %s", source))) + } else { + *ins = ins.WithSource(asm.Comment("instruction poisoned by CO-RE")) } + return nil } @@ -127,10 +138,11 @@ const ( reloTypeSize /* type size in bytes */ reloEnumvalExists /* enum value existence in target kernel */ reloEnumvalValue /* enum value integer value */ + reloTypeMatches /* type matches kernel type */ ) func (k coreKind) checksForExistence() bool { - return k == reloEnumvalExists || k == reloTypeExists || k == reloFieldExists + return k == reloEnumvalExists || k == reloTypeExists || k == reloFieldExists || k == reloTypeMatches } func (k coreKind) String() string { @@ -159,41 +171,19 @@ func (k coreKind) String() string { return "enumval_exists" case reloEnumvalValue: return "enumval_value" + case reloTypeMatches: + return "type_matches" default: - return "unknown" - } -} - -type mergedSpec []*Spec - -func (s mergedSpec) TypeByID(id TypeID) (Type, error) { - for _, sp := range s { - t, err := sp.TypeByID(id) - if err != nil { - if errors.Is(err, ErrNotFound) { - continue - } - return nil, err - } - return t, nil - } - return nil, fmt.Errorf("look up type with ID %d (first ID is %d): %w", id, s[0].imm.firstTypeID, ErrNotFound) -} - -func (s mergedSpec) NamedTypes(name essentialName) []TypeID { - var typeIDs []TypeID - for _, sp := range s { - namedTypes := sp.imm.namedTypes[name] - if len(namedTypes) > 0 { - typeIDs = append(typeIDs, namedTypes...) - } + return fmt.Sprintf("unknown (%d)", k) } - return typeIDs } // CORERelocate calculates changes needed to adjust eBPF instructions for differences // in types. // +// targets forms the set of types to relocate against. The first element has to be +// BTF for vmlinux, the following must be types for kernel modules. +// // resolveLocalTypeID is called for each local type which requires a stable TypeID. // Calling the function with the same type multiple times must produce the same // result. It is the callers responsibility to ensure that the relocated instructions @@ -210,6 +200,10 @@ func CORERelocate(relos []*CORERelocation, targets []*Spec, bo binary.ByteOrder, return nil, fmt.Errorf("targets must be provided") } + // We can't encode type IDs that aren't for vmlinux into instructions at the + // moment. + resolveTargetTypeID := targets[0].TypeID + for _, target := range targets { if bo != target.imm.byteOrder { return nil, fmt.Errorf("can't relocate %s against %s", bo, target.imm.byteOrder) @@ -255,15 +249,29 @@ func CORERelocate(relos []*CORERelocation, targets []*Spec, bo binary.ByteOrder, group.indices = append(group.indices, i) } - mergeTarget := mergedSpec(targets) for localType, group := range relosByType { localTypeName := localType.TypeName() if localTypeName == "" { return nil, fmt.Errorf("relocate unnamed or anonymous type %s: %w", localType, ErrNotSupported) } - targets := mergeTarget.NamedTypes(newEssentialName(localTypeName)) - fixups, err := coreCalculateFixups(group.relos, &mergeTarget, targets, bo) + essentialName := newEssentialName(localTypeName) + + var targetTypes []Type + for _, target := range targets { + namedTypeIDs := target.imm.namedTypes[essentialName] + targetTypes = slices.Grow(targetTypes, len(namedTypeIDs)) + for _, id := range namedTypeIDs { + typ, err := target.TypeByID(id) + if err != nil { + return nil, err + } + + targetTypes = append(targetTypes, typ) + } + } + + fixups, err := coreCalculateFixups(group.relos, targetTypes, bo, resolveTargetTypeID) if err != nil { return nil, fmt.Errorf("relocate %s: %w", localType, err) } @@ -286,19 +294,14 @@ var errIncompatibleTypes = errors.New("incompatible types") // // The best target is determined by scoring: the less poisoning we have to do // the better the target is. -func coreCalculateFixups(relos []*CORERelocation, targetSpec *mergedSpec, targets []TypeID, bo binary.ByteOrder) ([]COREFixup, error) { +func coreCalculateFixups(relos []*CORERelocation, targets []Type, bo binary.ByteOrder, resolveTargetTypeID func(Type) (TypeID, error)) ([]COREFixup, error) { bestScore := len(relos) var bestFixups []COREFixup - for _, targetID := range targets { - target, err := targetSpec.TypeByID(targetID) - if err != nil { - return nil, fmt.Errorf("look up target: %w", err) - } - + for _, target := range targets { score := 0 // lower is better fixups := make([]COREFixup, 0, len(relos)) for _, relo := range relos { - fixup, err := coreCalculateFixup(relo, target, targetID, bo) + fixup, err := coreCalculateFixup(relo, target, bo, resolveTargetTypeID) if err != nil { return nil, fmt.Errorf("target %s: %s: %w", target, relo.kind, err) } @@ -349,9 +352,8 @@ func coreCalculateFixups(relos []*CORERelocation, targetSpec *mergedSpec, target var errNoSignedness = errors.New("no signedness") -// coreCalculateFixup calculates the fixup for a single local type, target type -// and relocation. -func coreCalculateFixup(relo *CORERelocation, target Type, targetID TypeID, bo binary.ByteOrder) (COREFixup, error) { +// coreCalculateFixup calculates the fixup given a relocation and a target type. +func coreCalculateFixup(relo *CORERelocation, target Type, bo binary.ByteOrder, resolveTargetTypeID func(Type) (TypeID, error)) (COREFixup, error) { fixup := func(local, target uint64) (COREFixup, error) { return COREFixup{kind: relo.kind, local: local, target: target}, nil } @@ -369,6 +371,21 @@ func coreCalculateFixup(relo *CORERelocation, target Type, targetID TypeID, bo b local := relo.typ switch relo.kind { + case reloTypeMatches: + if len(relo.accessor) > 1 || relo.accessor[0] != 0 { + return zero, fmt.Errorf("unexpected accessor %v", relo.accessor) + } + + err := coreTypesMatch(local, target, nil) + if errors.Is(err, errIncompatibleTypes) { + return poison() + } + if err != nil { + return zero, err + } + + return fixup(1, 1) + case reloTypeIDTarget, reloTypeSize, reloTypeExists: if len(relo.accessor) > 1 || relo.accessor[0] != 0 { return zero, fmt.Errorf("unexpected accessor %v", relo.accessor) @@ -387,6 +404,15 @@ func coreCalculateFixup(relo *CORERelocation, target Type, targetID TypeID, bo b return fixup(1, 1) case reloTypeIDTarget: + targetID, err := resolveTargetTypeID(target) + if errors.Is(err, ErrNotFound) { + // Probably a relocation trying to get the ID + // of a type from a kmod. + return poison() + } + if err != nil { + return zero, err + } return fixup(uint64(relo.id), uint64(targetID)) case reloTypeSize: @@ -421,7 +447,7 @@ func coreCalculateFixup(relo *CORERelocation, target Type, targetID TypeID, bo b } case reloFieldByteOffset, reloFieldByteSize, reloFieldExists, reloFieldLShiftU64, reloFieldRShiftU64, reloFieldSigned: - if _, ok := as[*Fwd](target); ok { + if _, ok := As[*Fwd](target); ok { // We can't relocate fields using a forward declaration, so // skip it. If a non-forward declaration is present in the BTF // we'll find it in one of the other iterations. @@ -490,14 +516,14 @@ func coreCalculateFixup(relo *CORERelocation, target Type, targetID TypeID, bo b case reloFieldSigned: switch local := UnderlyingType(localField.Type).(type) { case *Enum: - target, ok := as[*Enum](targetField.Type) + target, ok := As[*Enum](targetField.Type) if !ok { return zero, fmt.Errorf("target isn't *Enum but %T", targetField.Type) } return fixup(boolToUint64(local.Signed), boolToUint64(target.Signed)) case *Int: - target, ok := as[*Int](targetField.Type) + target, ok := As[*Int](targetField.Type) if !ok { return zero, fmt.Errorf("target isn't *Int but %T", targetField.Type) } @@ -581,7 +607,7 @@ func (ca coreAccessor) String() string { } func (ca coreAccessor) enumValue(t Type) (*EnumValue, error) { - e, ok := as[*Enum](t) + e, ok := As[*Enum](t) if !ok { return nil, fmt.Errorf("not an enum: %s", t) } @@ -707,7 +733,7 @@ func coreFindField(localT Type, localAcc coreAccessor, targetT Type) (coreField, localMember := localMembers[acc] if localMember.Name == "" { - localMemberType, ok := as[composite](localMember.Type) + localMemberType, ok := As[composite](localMember.Type) if !ok { return coreField{}, coreField{}, fmt.Errorf("unnamed field with type %s: %s", localMember.Type, ErrNotSupported) } @@ -721,7 +747,7 @@ func coreFindField(localT Type, localAcc coreAccessor, targetT Type) (coreField, continue } - targetType, ok := as[composite](target.Type) + targetType, ok := As[composite](target.Type) if !ok { return coreField{}, coreField{}, fmt.Errorf("target not composite: %w", errImpossibleRelocation) } @@ -767,7 +793,7 @@ func coreFindField(localT Type, localAcc coreAccessor, targetT Type) (coreField, case *Array: // For arrays, acc is the index in the target. - targetType, ok := as[*Array](target.Type) + targetType, ok := As[*Array](target.Type) if !ok { return coreField{}, coreField{}, fmt.Errorf("target not array: %w", errImpossibleRelocation) } @@ -861,7 +887,7 @@ func coreFindMember(typ composite, name string) (Member, bool, error) { continue } - comp, ok := as[composite](member.Type) + comp, ok := As[composite](member.Type) if !ok { return Member{}, false, fmt.Errorf("anonymous non-composite type %T not allowed", member.Type) } @@ -880,7 +906,7 @@ func coreFindEnumValue(local Type, localAcc coreAccessor, target Type) (localVal return nil, nil, err } - targetEnum, ok := as[*Enum](target) + targetEnum, ok := As[*Enum](target) if !ok { return nil, nil, errImpossibleRelocation } @@ -1016,19 +1042,6 @@ func coreAreMembersCompatible(localType Type, targetType Type) error { localType = UnderlyingType(localType) targetType = UnderlyingType(targetType) - doNamesMatch := func(a, b string) error { - if a == "" || b == "" { - // allow anonymous and named type to match - return nil - } - - if newEssentialName(a) == newEssentialName(b) { - return nil - } - - return fmt.Errorf("names don't match: %w", errImpossibleRelocation) - } - _, lok := localType.(composite) _, tok := targetType.(composite) if lok && tok { @@ -1045,13 +1058,204 @@ func coreAreMembersCompatible(localType Type, targetType Type) error { case *Enum: tv := targetType.(*Enum) - return doNamesMatch(lv.Name, tv.Name) + if !coreEssentialNamesMatch(lv.Name, tv.Name) { + return fmt.Errorf("names %q and %q don't match: %w", lv.Name, tv.Name, errImpossibleRelocation) + } + + return nil case *Fwd: tv := targetType.(*Fwd) - return doNamesMatch(lv.Name, tv.Name) + if !coreEssentialNamesMatch(lv.Name, tv.Name) { + return fmt.Errorf("names %q and %q don't match: %w", lv.Name, tv.Name, errImpossibleRelocation) + } + + return nil default: return fmt.Errorf("type %s: %w", localType, ErrNotSupported) } } + +// coreEssentialNamesMatch compares two names while ignoring their flavour suffix. +// +// This should only be used on names which are in the global scope, like struct +// names, typedefs or enum values. +func coreEssentialNamesMatch(a, b string) bool { + if a == "" || b == "" { + // allow anonymous and named type to match + return true + } + + return newEssentialName(a) == newEssentialName(b) +} + +/* The comment below is from __bpf_core_types_match in relo_core.c: + * + * Check that two types "match". This function assumes that root types were + * already checked for name match. + * + * The matching relation is defined as follows: + * - modifiers and typedefs are stripped (and, hence, effectively ignored) + * - generally speaking types need to be of same kind (struct vs. struct, union + * vs. union, etc.) + * - exceptions are struct/union behind a pointer which could also match a + * forward declaration of a struct or union, respectively, and enum vs. + * enum64 (see below) + * Then, depending on type: + * - integers: + * - match if size and signedness match + * - arrays & pointers: + * - target types are recursively matched + * - structs & unions: + * - local members need to exist in target with the same name + * - for each member we recursively check match unless it is already behind a + * pointer, in which case we only check matching names and compatible kind + * - enums: + * - local variants have to have a match in target by symbolic name (but not + * numeric value) + * - size has to match (but enum may match enum64 and vice versa) + * - function pointers: + * - number and position of arguments in local type has to match target + * - for each argument and the return value we recursively check match + */ +func coreTypesMatch(localType Type, targetType Type, visited map[pair]struct{}) error { + localType = UnderlyingType(localType) + targetType = UnderlyingType(targetType) + + if !coreEssentialNamesMatch(localType.TypeName(), targetType.TypeName()) { + return fmt.Errorf("type name %q don't match %q: %w", localType.TypeName(), targetType.TypeName(), errIncompatibleTypes) + } + + if reflect.TypeOf(localType) != reflect.TypeOf(targetType) { + return fmt.Errorf("type mismatch between %v and %v: %w", localType, targetType, errIncompatibleTypes) + } + + if _, ok := visited[pair{localType, targetType}]; ok { + return nil + } + if visited == nil { + visited = make(map[pair]struct{}) + } + visited[pair{localType, targetType}] = struct{}{} + + switch lv := (localType).(type) { + case *Void: + + case *Fwd: + if targetType.(*Fwd).Kind != lv.Kind { + return fmt.Errorf("fwd kind mismatch between %v and %v: %w", localType, targetType, errIncompatibleTypes) + } + + case *Enum: + return coreEnumsMatch(lv, targetType.(*Enum)) + + case composite: + tv := targetType.(composite) + + if len(lv.members()) > len(tv.members()) { + return errIncompatibleTypes + } + + localMembers := lv.members() + targetMembers := map[string]Member{} + for _, member := range tv.members() { + targetMembers[member.Name] = member + } + + for _, localMember := range localMembers { + targetMember, found := targetMembers[localMember.Name] + if !found { + return fmt.Errorf("no field %q in %v: %w", localMember.Name, targetType, errIncompatibleTypes) + } + + err := coreTypesMatch(localMember.Type, targetMember.Type, visited) + if err != nil { + return err + } + } + + case *Int: + if !coreEncodingMatches(lv, targetType.(*Int)) { + return fmt.Errorf("int mismatch between %v and %v: %w", localType, targetType, errIncompatibleTypes) + } + + case *Pointer: + tv := targetType.(*Pointer) + + // Allow a pointer to a forward declaration to match a struct + // or union. + if fwd, ok := As[*Fwd](lv.Target); ok && fwd.matches(tv.Target) { + return nil + } + + if fwd, ok := As[*Fwd](tv.Target); ok && fwd.matches(lv.Target) { + return nil + } + + return coreTypesMatch(lv.Target, tv.Target, visited) + + case *Array: + tv := targetType.(*Array) + + if lv.Nelems != tv.Nelems { + return fmt.Errorf("array mismatch between %v and %v: %w", localType, targetType, errIncompatibleTypes) + } + + return coreTypesMatch(lv.Type, tv.Type, visited) + + case *FuncProto: + tv := targetType.(*FuncProto) + + if len(lv.Params) != len(tv.Params) { + return fmt.Errorf("function param mismatch: %w", errIncompatibleTypes) + } + + for i, lparam := range lv.Params { + if err := coreTypesMatch(lparam.Type, tv.Params[i].Type, visited); err != nil { + return err + } + } + + return coreTypesMatch(lv.Return, tv.Return, visited) + + default: + return fmt.Errorf("unsupported type %T", localType) + } + + return nil +} + +// coreEncodingMatches returns true if both ints have the same size and signedness. +// All encodings other than `Signed` are considered unsigned. +func coreEncodingMatches(local, target *Int) bool { + return local.Size == target.Size && (local.Encoding == Signed) == (target.Encoding == Signed) +} + +// coreEnumsMatch checks two enums match, which is considered to be the case if the following is true: +// - size has to match (but enum may match enum64 and vice versa) +// - local variants have to have a match in target by symbolic name (but not numeric value) +func coreEnumsMatch(local *Enum, target *Enum) error { + if local.Size != target.Size { + return fmt.Errorf("size mismatch between %v and %v: %w", local, target, errIncompatibleTypes) + } + + // If there are more values in the local than the target, there must be at least one value in the local + // that isn't in the target, and therefor the types are incompatible. + if len(local.Values) > len(target.Values) { + return fmt.Errorf("local has more values than target: %w", errIncompatibleTypes) + } + +outer: + for _, lv := range local.Values { + for _, rv := range target.Values { + if coreEssentialNamesMatch(lv.Name, rv.Name) { + continue outer + } + } + + return fmt.Errorf("no match for %v in %v: %w", lv, target, errIncompatibleTypes) + } + + return nil +} diff --git a/vendor/github.com/cilium/ebpf/btf/ext_info.go b/vendor/github.com/cilium/ebpf/btf/ext_info.go index d5652bad5..eb9044bad 100644 --- a/vendor/github.com/cilium/ebpf/btf/ext_info.go +++ b/vendor/github.com/cilium/ebpf/btf/ext_info.go @@ -142,12 +142,13 @@ func AssignMetadataToInstructions( // MarshalExtInfos encodes function and line info embedded in insns into kernel // wire format. +// +// If an instruction has an [asm.Comment], it will be synthesized into a mostly +// empty line info. func MarshalExtInfos(insns asm.Instructions, b *Builder) (funcInfos, lineInfos []byte, _ error) { iter := insns.Iterate() for iter.Next() { - _, ok := iter.Ins.Source().(*Line) - fn := FuncMetadata(iter.Ins) - if ok || fn != nil { + if iter.Ins.Source() != nil || FuncMetadata(iter.Ins) != nil { goto marshal } } @@ -167,7 +168,16 @@ marshal: } } - if line, ok := iter.Ins.Source().(*Line); ok { + if source := iter.Ins.Source(); source != nil { + var line *Line + if l, ok := source.(*Line); ok { + line = l + } else { + line = &Line{ + line: source.String(), + } + } + li := &lineInfo{ line: line, offset: iter.Offset, diff --git a/vendor/github.com/cilium/ebpf/btf/kernel.go b/vendor/github.com/cilium/ebpf/btf/kernel.go index 7421a72be..8584ebcb9 100644 --- a/vendor/github.com/cilium/ebpf/btf/kernel.go +++ b/vendor/github.com/cilium/ebpf/btf/kernel.go @@ -63,6 +63,7 @@ func LoadKernelSpec() (*Spec, error) { // // 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) { kernelBTF.RLock() spec := kernelBTF.modules[module] diff --git a/vendor/github.com/cilium/ebpf/btf/marshal.go b/vendor/github.com/cilium/ebpf/btf/marshal.go index 744c84c4c..f14cfa6e9 100644 --- a/vendor/github.com/cilium/ebpf/btf/marshal.go +++ b/vendor/github.com/cilium/ebpf/btf/marshal.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "errors" "fmt" + "maps" "math" "slices" "sync" @@ -19,14 +20,17 @@ type MarshalOptions struct { StripFuncLinkage bool // Replace Enum64 with a placeholder for compatibility with <6.0 kernels. ReplaceEnum64 bool + // Prevent the "No type found" error when loading BTF without any types. + PreventNoTypeFound bool } // KernelMarshalOptions will generate BTF suitable for the current kernel. func KernelMarshalOptions() *MarshalOptions { return &MarshalOptions{ - Order: internal.NativeEndian, - StripFuncLinkage: haveFuncLinkage() != nil, - ReplaceEnum64: haveEnum64() != nil, + Order: internal.NativeEndian, + StripFuncLinkage: haveFuncLinkage() != nil, + ReplaceEnum64: haveEnum64() != nil, + PreventNoTypeFound: true, // All current kernels require this. } } @@ -38,6 +42,7 @@ type encoder struct { buf *bytes.Buffer strings *stringTableBuilder ids map[Type]TypeID + visited map[Type]struct{} lastID TypeID } @@ -92,9 +97,9 @@ func NewBuilder(types []Type) (*Builder, error) { return b, nil } -// Empty returns true if [Add] has not been invoked on the builder. +// Empty returns true if neither types nor strings have been added. func (b *Builder) Empty() bool { - return len(b.types) == 0 + return len(b.types) == 0 && (b.strings == nil || b.strings.Length() == 0) } // Add a Type and allocate a stable ID for it. @@ -163,15 +168,29 @@ func (b *Builder) Marshal(buf []byte, opts *MarshalOptions) ([]byte, error) { buf: w, strings: stb, lastID: TypeID(len(b.types)), - ids: make(map[Type]TypeID, len(b.types)), + visited: make(map[Type]struct{}, len(b.types)), + ids: maps.Clone(b.stableIDs), + } + + if e.ids == nil { + e.ids = make(map[Type]TypeID) + } + + types := b.types + if len(types) == 0 && stb.Length() > 0 && opts.PreventNoTypeFound { + // We have strings that need to be written out, + // but no types (besides the implicit Void). + // Kernels as recent as v6.7 refuse to load such BTF + // with a "No type found" error in the log. + // Fix this by adding a dummy type. + types = []Type{&Int{Size: 0}} } // Ensure that types are marshaled in the exact order they were Add()ed. // Otherwise the ID returned from Add() won't match. - e.pending.Grow(len(b.types)) - for _, typ := range b.types { + e.pending.Grow(len(types)) + for _, typ := range types { e.pending.Push(typ) - e.ids[typ] = b.stableIDs[typ] } if err := e.deflatePending(); err != nil { @@ -218,16 +237,28 @@ func (b *Builder) addString(str string) (uint32, error) { return b.strings.Add(str) } -func (e *encoder) allocateID(typ Type) error { - id := e.lastID + 1 - if id < e.lastID { - return errors.New("type ID overflow") - } +func (e *encoder) allocateIDs(root Type) (err error) { + visitInPostorder(root, e.visited, func(typ Type) bool { + if _, ok := typ.(*Void); ok { + return true + } - e.pending.Push(typ) - e.ids[typ] = id - e.lastID = id - return nil + if _, ok := e.ids[typ]; ok { + return true + } + + id := e.lastID + 1 + if id < e.lastID { + err = errors.New("type ID overflow") + return false + } + + e.pending.Push(typ) + e.ids[typ] = id + e.lastID = id + return true + }) + return } // id returns the ID for the given type or panics with an error. @@ -247,33 +278,13 @@ func (e *encoder) id(typ Type) TypeID { func (e *encoder) deflatePending() error { // Declare root outside of the loop to avoid repeated heap allocations. var root Type - skip := func(t Type) (skip bool) { - if t == root { - // Force descending into the current root type even if it already - // has an ID. Otherwise we miss children of types that have their - // ID pre-allocated via Add. - return false - } - - _, isVoid := t.(*Void) - _, alreadyEncoded := e.ids[t] - return isVoid || alreadyEncoded - } for !e.pending.Empty() { root = e.pending.Shift() // Allocate IDs for all children of typ, including transitive dependencies. - iter := postorderTraversal(root, skip) - for iter.Next() { - if iter.Type == root { - // The iterator yields root at the end, do not allocate another ID. - break - } - - if err := e.allocateID(iter.Type); err != nil { - return err - } + if err := e.allocateIDs(root); err != nil { + return err } if err := e.deflateType(root); err != nil { @@ -498,7 +509,7 @@ func (e *encoder) deflateEnum64(raw *rawType, enum *Enum) (err error) { if enum.Signed { placeholder.Encoding = Signed } - if err := e.allocateID(placeholder); err != nil { + if err := e.allocateIDs(placeholder); err != nil { return fmt.Errorf("add enum64 placeholder: %w", err) } diff --git a/vendor/github.com/cilium/ebpf/btf/traversal.go b/vendor/github.com/cilium/ebpf/btf/traversal.go index 5a7387b06..c39dc66e4 100644 --- a/vendor/github.com/cilium/ebpf/btf/traversal.go +++ b/vendor/github.com/cilium/ebpf/btf/traversal.go @@ -2,130 +2,41 @@ package btf import ( "fmt" - - "github.com/cilium/ebpf/internal" ) // Functions to traverse a cyclic graph of types. The below was very useful: // https://eli.thegreenplace.net/2015/directed-graph-traversal-orderings-and-applications-to-data-flow-analysis/#post-order-and-reverse-post-order -type postorderIterator struct { - // Iteration skips types for which this function returns true. - skip func(Type) bool - // The root type. May be nil if skip(root) is true. - root Type - - // Contains types which need to be either walked or yielded. - types typeDeque - // Contains a boolean whether the type has been walked or not. - walked internal.Deque[bool] - // The set of types which has been pushed onto types. - pushed map[Type]struct{} - - // The current type. Only valid after a call to Next(). - Type Type -} - -// postorderTraversal iterates all types reachable from root by visiting the -// leaves of the graph first. +// Visit all types reachable from root in postorder. // -// Types for which skip returns true are ignored. skip may be nil. -func postorderTraversal(root Type, skip func(Type) (skip bool)) postorderIterator { - // Avoid allocations for the common case of a skipped root. - if skip != nil && skip(root) { - return postorderIterator{} - } - - po := postorderIterator{root: root, skip: skip} - walkType(root, po.push) - - return po -} - -func (po *postorderIterator) push(t *Type) { - if _, ok := po.pushed[*t]; ok || *t == po.root { - return - } - - if po.skip != nil && po.skip(*t) { - return +// Traversal stops if yield returns false. +// +// Returns false if traversal was aborted. +func visitInPostorder(root Type, visited map[Type]struct{}, yield func(typ Type) bool) bool { + if _, ok := visited[root]; ok { + return true } - - if po.pushed == nil { - // Lazily allocate pushed to avoid an allocation for Types without children. - po.pushed = make(map[Type]struct{}) + if visited == nil { + visited = make(map[Type]struct{}) } + visited[root] = struct{}{} - po.pushed[*t] = struct{}{} - po.types.Push(t) - po.walked.Push(false) -} - -// Next returns true if there is another Type to traverse. -func (po *postorderIterator) Next() bool { - for !po.types.Empty() { - t := po.types.Pop() - - if !po.walked.Pop() { - // Push the type again, so that we re-evaluate it in done state - // after all children have been handled. - po.types.Push(t) - po.walked.Push(true) - - // Add all direct children to todo. - walkType(*t, po.push) - } else { - // We've walked this type previously, so we now know that all - // children have been handled. - po.Type = *t - return true - } + cont := children(root, func(child *Type) bool { + return visitInPostorder(*child, visited, yield) + }) + if !cont { + return false } - // Only return root once. - po.Type, po.root = po.root, nil - return po.Type != nil + return yield(root) } -// modifyGraphPreorder allows modifying every Type in a graph. -// -// fn is invoked in preorder for every unique Type in a graph. See [Type] for the definition -// of equality. Every occurrence of node is substituted with its replacement. +// children calls yield on each child of typ. // -// If cont is true, fn is invoked for every child of replacement. Otherwise -// traversal stops. +// Traversal stops if yield returns false. // -// Returns the substitution of the root node. -func modifyGraphPreorder(root Type, fn func(node Type) (replacement Type, cont bool)) Type { - sub, cont := fn(root) - replacements := map[Type]Type{root: sub} - - // This is a preorder traversal. - var walk func(*Type) - walk = func(node *Type) { - sub, visited := replacements[*node] - if visited { - *node = sub - return - } - - sub, cont := fn(*node) - replacements[*node] = sub - *node = sub - - if cont { - walkType(*node, walk) - } - } - - if cont { - walkType(sub, walk) - } - return sub -} - -// walkType calls fn on each child of typ. -func walkType(typ Type, fn func(*Type)) { +// Returns false if traversal was aborted. +func children(typ Type, yield func(child *Type) bool) bool { // Explicitly type switch on the most common types to allow the inliner to // do its work. This avoids allocating intermediate slices from walk() on // the heap. @@ -133,46 +44,80 @@ func walkType(typ Type, fn func(*Type)) { case *Void, *Int, *Enum, *Fwd, *Float: // No children to traverse. case *Pointer: - fn(&v.Target) + if !yield(&v.Target) { + return false + } case *Array: - fn(&v.Index) - fn(&v.Type) + if !yield(&v.Index) { + return false + } + if !yield(&v.Type) { + return false + } case *Struct: for i := range v.Members { - fn(&v.Members[i].Type) + if !yield(&v.Members[i].Type) { + return false + } } case *Union: for i := range v.Members { - fn(&v.Members[i].Type) + if !yield(&v.Members[i].Type) { + return false + } } case *Typedef: - fn(&v.Type) + if !yield(&v.Type) { + return false + } case *Volatile: - fn(&v.Type) + if !yield(&v.Type) { + return false + } case *Const: - fn(&v.Type) + if !yield(&v.Type) { + return false + } case *Restrict: - fn(&v.Type) + if !yield(&v.Type) { + return false + } case *Func: - fn(&v.Type) + if !yield(&v.Type) { + return false + } case *FuncProto: - fn(&v.Return) + if !yield(&v.Return) { + return false + } for i := range v.Params { - fn(&v.Params[i].Type) + if !yield(&v.Params[i].Type) { + return false + } } case *Var: - fn(&v.Type) + if !yield(&v.Type) { + return false + } case *Datasec: for i := range v.Vars { - fn(&v.Vars[i].Type) + if !yield(&v.Vars[i].Type) { + return false + } } case *declTag: - fn(&v.Type) + if !yield(&v.Type) { + return false + } case *typeTag: - fn(&v.Type) + if !yield(&v.Type) { + return false + } case *cycle: // cycle has children, but we ignore them deliberately. default: panic(fmt.Sprintf("don't know how to walk Type %T", v)) } + + return true } diff --git a/vendor/github.com/cilium/ebpf/btf/types.go b/vendor/github.com/cilium/ebpf/btf/types.go index 1e57a9791..3cb9184f0 100644 --- a/vendor/github.com/cilium/ebpf/btf/types.go +++ b/vendor/github.com/cilium/ebpf/btf/types.go @@ -318,6 +318,18 @@ func (f *Fwd) copy() Type { return &cpy } +func (f *Fwd) matches(typ Type) bool { + if _, ok := As[*Struct](typ); ok && f.Kind == FwdStruct { + return true + } + + if _, ok := As[*Union](typ); ok && f.Kind == FwdUnion { + return true + } + + return false +} + // Typedef is an alias of a Type. type Typedef struct { Name string @@ -655,54 +667,40 @@ func alignof(typ Type) (int, error) { return 0, fmt.Errorf("can't calculate alignment of %T", t) } - if !pow(n) { + if !internal.IsPow(n) { return 0, fmt.Errorf("alignment value %d is not a power of two", n) } return n, nil } -// pow returns true if n is a power of two. -func pow(n int) bool { - return n != 0 && (n&(n-1)) == 0 -} - -// Transformer modifies a given Type and returns the result. -// -// For example, UnderlyingType removes any qualifiers or typedefs from a type. -// See the example on Copy for how to use a transform. -type Transformer func(Type) Type - // Copy a Type recursively. // -// typ may form a cycle. If transform is not nil, it is called with the -// to be copied type, and the returned value is copied instead. -func Copy(typ Type, transform Transformer) Type { - copies := make(copier) - return copies.copy(typ, transform) +// typ may form a cycle. +func Copy(typ Type) Type { + return copyType(typ, nil, make(map[Type]Type), nil) } -// A map of a type to its copy. -type copier map[Type]Type +func copyType(typ Type, ids map[Type]TypeID, copies map[Type]Type, copiedIDs map[Type]TypeID) Type { + cpy, ok := copies[typ] + if ok { + // This has been copied previously, no need to continue. + return cpy + } -func (c copier) copy(typ Type, transform Transformer) Type { - return modifyGraphPreorder(typ, func(t Type) (Type, bool) { - cpy, ok := c[t] - if ok { - // This has been copied previously, no need to continue. - return cpy, false - } + cpy = typ.copy() + copies[typ] = cpy - if transform != nil { - cpy = transform(t).copy() - } else { - cpy = t.copy() - } - c[t] = cpy + if id, ok := ids[typ]; ok { + copiedIDs[cpy] = id + } - // This is a new copy, keep copying children. - return cpy, true + children(cpy, func(child *Type) bool { + *child = copyType(*child, ids, copies, copiedIDs) + return true }) + + return cpy } type typeDeque = internal.Deque[*Type] @@ -1205,12 +1203,15 @@ func UnderlyingType(typ Type) Type { return &cycle{typ} } -// as returns typ if is of type T. Otherwise it peels qualifiers and Typedefs +// As returns typ if is of type T. Otherwise it peels qualifiers and Typedefs // until it finds a T. // // Returns the zero value and false if there is no T or if the type is nested // too deeply. -func as[T Type](typ Type) (T, bool) { +func As[T Type](typ Type) (T, bool) { + // NB: We can't make this function return (*T) since then + // we can't assert that a type matches an interface which + // embeds Type: as[composite](T). for depth := 0; depth <= maxResolveDepth; depth++ { switch v := (typ).(type) { case T: diff --git a/vendor/github.com/cilium/ebpf/internal/align.go b/vendor/github.com/cilium/ebpf/internal/math.go similarity index 63% rename from vendor/github.com/cilium/ebpf/internal/align.go rename to vendor/github.com/cilium/ebpf/internal/math.go index edc898fa9..e95c8efde 100644 --- a/vendor/github.com/cilium/ebpf/internal/align.go +++ b/vendor/github.com/cilium/ebpf/internal/math.go @@ -6,3 +6,8 @@ import "golang.org/x/exp/constraints" func Align[I constraints.Integer](n, alignment I) I { return (n + alignment - 1) / alignment * alignment } + +// IsPow returns true if n is a power of two. +func IsPow[I constraints.Integer](n I) bool { + return n != 0 && (n&(n-1)) == 0 +} diff --git a/vendor/github.com/cilium/ebpf/internal/sys/syscall.go b/vendor/github.com/cilium/ebpf/internal/sys/syscall.go index 2ef8a13e9..f6b6e9345 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/syscall.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/syscall.go @@ -99,12 +99,24 @@ func (i *NetkitLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } +func (i *KprobeMultiLinkInfo) info() (unsafe.Pointer, uint32) { + return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) +} + +func (i *KprobeLinkInfo) info() (unsafe.Pointer, uint32) { + return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) +} + var _ Info = (*BtfInfo)(nil) func (i *BtfInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } +func (i *PerfEventLinkInfo) 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. diff --git a/vendor/github.com/cilium/ebpf/internal/sys/types.go b/vendor/github.com/cilium/ebpf/internal/sys/types.go index cb63219d4..d2ae94266 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/types.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/types.go @@ -402,6 +402,18 @@ const ( BPF_MAP_TYPE_CGRP_STORAGE MapType = 32 ) +type PerfEventType uint32 + +const ( + BPF_PERF_EVENT_UNSPEC PerfEventType = 0 + BPF_PERF_EVENT_UPROBE PerfEventType = 1 + BPF_PERF_EVENT_URETPROBE PerfEventType = 2 + BPF_PERF_EVENT_KPROBE PerfEventType = 3 + BPF_PERF_EVENT_KRETPROBE PerfEventType = 4 + BPF_PERF_EVENT_TRACEPOINT PerfEventType = 5 + BPF_PERF_EVENT_EVENT PerfEventType = 6 +) + type ProgType uint32 const ( @@ -1263,6 +1275,32 @@ type IterLinkInfo struct { TargetNameLen uint32 } +type KprobeLinkInfo struct { + Type LinkType + Id LinkID + ProgId uint32 + _ [4]byte + PerfEventType PerfEventType + _ [4]byte + FuncName Pointer + NameLen uint32 + Offset uint32 + Addr uint64 + Missed uint64 +} + +type KprobeMultiLinkInfo struct { + Type LinkType + Id LinkID + ProgId uint32 + _ [4]byte + Addrs Pointer + Count uint32 + Flags uint32 + Missed uint64 + _ [16]byte +} + type NetNsLinkInfo struct { Type LinkType Id LinkID @@ -1295,6 +1333,14 @@ type NetkitLinkInfo struct { _ [32]byte } +type PerfEventLinkInfo struct { + Type LinkType + Id LinkID + ProgId uint32 + _ [4]byte + PerfEventType PerfEventType +} + type RawTracepointLinkInfo struct { Type LinkType Id LinkID 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 2f22b1278..d725cfaa3 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go @@ -41,6 +41,7 @@ const ( BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP BPF_F_KPROBE_MULTI_RETURN = linux.BPF_F_KPROBE_MULTI_RETURN BPF_F_UPROBE_MULTI_RETURN = linux.BPF_F_UPROBE_MULTI_RETURN + BPF_F_LOCK = linux.BPF_F_LOCK BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN BPF_TAG_SIZE = linux.BPF_TAG_SIZE BPF_RINGBUF_BUSY_BIT = linux.BPF_RINGBUF_BUSY_BIT @@ -100,6 +101,7 @@ type PerfEventMmapPage = linux.PerfEventMmapPage type EpollEvent = linux.EpollEvent type PerfEventAttr = linux.PerfEventAttr type Utsname = linux.Utsname +type CPUSet = linux.CPUSet func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return linux.Syscall(trap, a1, a2, a3) @@ -204,3 +206,11 @@ func Fstat(fd int, stat *Stat_t) error { func SetsockoptInt(fd, level, opt, value int) error { return linux.SetsockoptInt(fd, level, opt, value) } + +func SchedSetaffinity(pid int, set *CPUSet) error { + return linux.SchedSetaffinity(pid, set) +} + +func SchedGetaffinity(pid int, set *CPUSet) error { + return linux.SchedGetaffinity(pid, set) +} 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 e5bad0469..3ff896271 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go @@ -93,6 +93,7 @@ const ( DEBUGFS_MAGIC BPF_RB_NO_WAKEUP BPF_RB_FORCE_WAKEUP + BPF_F_LOCK ) type Statfs_t struct { @@ -296,3 +297,15 @@ func Fstat(fd int, stat *Stat_t) error { func SetsockoptInt(fd, level, opt, value int) error { return errNonLinux } + +type CPUSet struct{} + +func (*CPUSet) Set(int) {} + +func SchedSetaffinity(pid int, set *CPUSet) error { + return errNonLinux +} + +func SchedGetaffinity(pid int, set *CPUSet) error { + return errNonLinux +} diff --git a/vendor/github.com/cilium/ebpf/link/link.go b/vendor/github.com/cilium/ebpf/link/link.go index 9ca5e089e..81428568f 100644 --- a/vendor/github.com/cilium/ebpf/link/link.go +++ b/vendor/github.com/cilium/ebpf/link/link.go @@ -194,6 +194,48 @@ type NetkitInfo struct { AttachType sys.AttachType } +type KprobeMultiInfo struct { + count uint32 + flags uint32 + missed uint64 +} + +// AddressCount is the number of addresses hooked by the kprobe. +func (kpm *KprobeMultiInfo) AddressCount() (uint32, bool) { + return kpm.count, kpm.count > 0 +} + +func (kpm *KprobeMultiInfo) Flags() (uint32, bool) { + return kpm.flags, kpm.count > 0 +} + +func (kpm *KprobeMultiInfo) Missed() (uint64, bool) { + return kpm.missed, kpm.count > 0 +} + +type PerfEventInfo struct { + Type sys.PerfEventType + extra interface{} +} + +func (r *PerfEventInfo) Kprobe() *KprobeInfo { + e, _ := r.extra.(*KprobeInfo) + return e +} + +type KprobeInfo struct { + address uint64 + missed uint64 +} + +func (kp *KprobeInfo) Address() (uint64, bool) { + return kp.address, kp.address > 0 +} + +func (kp *KprobeInfo) Missed() (uint64, bool) { + return kp.missed, kp.address > 0 +} + // Tracing returns tracing type-specific link info. // // Returns nil if the type-specific link info isn't available. @@ -250,6 +292,22 @@ func (r Info) Netkit() *NetkitInfo { return e } +// KprobeMulti returns kprobe-multi type-specific link info. +// +// Returns nil if the type-specific link info isn't available. +func (r Info) KprobeMulti() *KprobeMultiInfo { + e, _ := r.extra.(*KprobeMultiInfo) + return e +} + +// PerfEvent returns perf-event type-specific link info. +// +// Returns nil if the type-specific link info isn't available. +func (r Info) PerfEvent() *PerfEventInfo { + e, _ := r.extra.(*PerfEventInfo) + return e +} + // RawLink is the low-level API to bpf_link. // // You should consider using the higher level interfaces in this @@ -425,8 +483,7 @@ func (l *RawLink) Info() (*Info, error) { extra = &XDPInfo{ Ifindex: xdpInfo.Ifindex, } - case RawTracepointType, IterType, - PerfEventType, KprobeMultiType, UprobeMultiType: + case RawTracepointType, IterType, UprobeMultiType: // Extra metadata not supported. case TCXType: var tcxInfo sys.TcxLinkInfo @@ -457,6 +514,39 @@ func (l *RawLink) Info() (*Info, error) { Ifindex: netkitInfo.Ifindex, AttachType: netkitInfo.AttachType, } + case KprobeMultiType: + var kprobeMultiInfo sys.KprobeMultiLinkInfo + if err := sys.ObjInfo(l.fd, &kprobeMultiInfo); err != nil { + return nil, fmt.Errorf("kprobe multi link info: %s", err) + } + extra = &KprobeMultiInfo{ + count: kprobeMultiInfo.Count, + flags: kprobeMultiInfo.Flags, + missed: kprobeMultiInfo.Missed, + } + case PerfEventType: + var perfEventInfo sys.PerfEventLinkInfo + if err := sys.ObjInfo(l.fd, &perfEventInfo); err != nil { + return nil, fmt.Errorf("perf event link info: %s", err) + } + + var extra2 interface{} + switch perfEventInfo.PerfEventType { + case sys.BPF_PERF_EVENT_KPROBE, sys.BPF_PERF_EVENT_KRETPROBE: + var kprobeInfo sys.KprobeLinkInfo + if err := sys.ObjInfo(l.fd, &kprobeInfo); err != nil { + return nil, fmt.Errorf("kprobe multi link info: %s", err) + } + extra2 = &KprobeInfo{ + address: kprobeInfo.Addr, + missed: kprobeInfo.Missed, + } + } + + extra = &PerfEventInfo{ + Type: perfEventInfo.PerfEventType, + extra: extra2, + } default: return nil, fmt.Errorf("unknown link info type: %d", info.Type) } diff --git a/vendor/github.com/cilium/ebpf/linker.go b/vendor/github.com/cilium/ebpf/linker.go index 538fd4e9e..788f21b7b 100644 --- a/vendor/github.com/cilium/ebpf/linker.go +++ b/vendor/github.com/cilium/ebpf/linker.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "io/fs" "math" "slices" @@ -148,10 +149,13 @@ func applyRelocations(insns asm.Instructions, targets []*btf.Spec, kmodName stri if kmodName != "" { kmodTarget, err := btf.LoadKernelModuleSpec(kmodName) - if err != nil { + // Ignore ErrNotExists to cater to kernels which have CONFIG_DEBUG_INFO_BTF_MODULES disabled. + if err != nil && !errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("load kernel module spec: %w", err) } - targets = append(targets, kmodTarget) + if err == nil { + targets = append(targets, kmodTarget) + } } } diff --git a/vendor/github.com/cilium/ebpf/map.go b/vendor/github.com/cilium/ebpf/map.go index 1eeee1776..e46fa3f12 100644 --- a/vendor/github.com/cilium/ebpf/map.go +++ b/vendor/github.com/cilium/ebpf/map.go @@ -490,6 +490,15 @@ func handleMapCreateError(attr sys.MapCreateAttr, spec *MapSpec, err error) erro return fmt.Errorf("map create: %w", haveFeatErr) } } + // BPF_MAP_TYPE_RINGBUF's max_entries must be a power-of-2 multiple of kernel's page size. + if errors.Is(err, unix.EINVAL) && + (attr.MapType == sys.BPF_MAP_TYPE_RINGBUF || attr.MapType == sys.BPF_MAP_TYPE_USER_RINGBUF) { + pageSize := uint32(os.Getpagesize()) + maxEntries := attr.MaxEntries + if maxEntries%pageSize != 0 || !internal.IsPow(maxEntries) { + return fmt.Errorf("map create: %w (ring map size %d not a multiple of page size %d)", err, maxEntries, pageSize) + } + } if attr.BtfFd == 0 { return fmt.Errorf("map create: %w (without BTF k/v)", err) } @@ -566,7 +575,7 @@ func (m *Map) Info() (*MapInfo, error) { type MapLookupFlags uint64 // LookupLock look up the value of a spin-locked map. -const LookupLock MapLookupFlags = 4 +const LookupLock MapLookupFlags = unix.BPF_F_LOCK // Lookup retrieves a value from a Map. // diff --git a/vendor/github.com/cilium/ebpf/perf/reader.go b/vendor/github.com/cilium/ebpf/perf/reader.go index 3c820708c..22548c0d8 100644 --- a/vendor/github.com/cilium/ebpf/perf/reader.go +++ b/vendor/github.com/cilium/ebpf/perf/reader.go @@ -169,6 +169,10 @@ type Reader struct { // ReaderOptions control the behaviour of the user // space reader. type ReaderOptions struct { + // The number of events required in any per CPU buffer before + // Read will process data. This is mutually exclusive with Watermark. + // The default is zero, which means Watermark will take precedence. + WakeupEvents int // The number of written bytes required in any per CPU buffer before // Read will process data. Must be smaller than PerCPUBuffer. // The default is to start processing as soon as data is available. @@ -192,6 +196,9 @@ func NewReaderWithOptions(array *ebpf.Map, perCPUBuffer int, opts ReaderOptions) if perCPUBuffer < 1 { return nil, errors.New("perCPUBuffer must be larger than 0") } + if opts.WakeupEvents > 0 && opts.Watermark > 0 { + return nil, errors.New("WakeupEvents and Watermark cannot both be non-zero") + } var ( fds []int @@ -224,7 +231,7 @@ func NewReaderWithOptions(array *ebpf.Map, perCPUBuffer int, opts ReaderOptions) // Hence we have to create a ring for each CPU. bufferSize := 0 for i := 0; i < nCPU; i++ { - ring, err := newPerfEventRing(i, perCPUBuffer, opts.Watermark, opts.Overwritable) + ring, err := newPerfEventRing(i, perCPUBuffer, opts) if errors.Is(err, unix.ENODEV) { // The requested CPU is currently offline, skip it. rings = append(rings, nil) diff --git a/vendor/github.com/cilium/ebpf/perf/ring.go b/vendor/github.com/cilium/ebpf/perf/ring.go index ddf3519f2..9bd959263 100644 --- a/vendor/github.com/cilium/ebpf/perf/ring.go +++ b/vendor/github.com/cilium/ebpf/perf/ring.go @@ -22,12 +22,12 @@ type perfEventRing struct { ringReader } -func newPerfEventRing(cpu, perCPUBuffer, watermark int, overwritable bool) (*perfEventRing, error) { - if watermark >= perCPUBuffer { +func newPerfEventRing(cpu, perCPUBuffer int, opts ReaderOptions) (*perfEventRing, error) { + if opts.Watermark >= perCPUBuffer { return nil, errors.New("watermark must be smaller than perCPUBuffer") } - fd, err := createPerfEvent(cpu, watermark, overwritable) + fd, err := createPerfEvent(cpu, opts) if err != nil { return nil, err } @@ -38,7 +38,7 @@ func newPerfEventRing(cpu, perCPUBuffer, watermark int, overwritable bool) (*per } protections := unix.PROT_READ - if !overwritable { + if !opts.Overwritable { protections |= unix.PROT_WRITE } @@ -55,7 +55,7 @@ func newPerfEventRing(cpu, perCPUBuffer, watermark int, overwritable bool) (*per meta := (*unix.PerfEventMmapPage)(unsafe.Pointer(&mmap[0])) var reader ringReader - if overwritable { + if opts.Overwritable { reader = newReverseReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size]) } else { reader = newForwardReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size]) @@ -98,13 +98,20 @@ func (ring *perfEventRing) Close() { ring.mmap = nil } -func createPerfEvent(cpu, watermark int, overwritable bool) (int, error) { - if watermark == 0 { - watermark = 1 +func createPerfEvent(cpu int, opts ReaderOptions) (int, error) { + wakeup := 0 + bits := 0 + if opts.WakeupEvents > 0 { + wakeup = opts.WakeupEvents + } else { + wakeup = opts.Watermark + if wakeup == 0 { + wakeup = 1 + } + bits |= unix.PerfBitWatermark } - bits := unix.PerfBitWatermark - if overwritable { + if opts.Overwritable { bits |= unix.PerfBitWriteBackward } @@ -113,7 +120,7 @@ func createPerfEvent(cpu, watermark int, overwritable bool) (int, error) { Config: unix.PERF_COUNT_SW_BPF_OUTPUT, Bits: uint64(bits), Sample_type: unix.PERF_SAMPLE_RAW, - Wakeup: uint32(watermark), + Wakeup: uint32(wakeup), } attr.Size = uint32(unsafe.Sizeof(attr)) diff --git a/vendor/github.com/cilium/ebpf/prog.go b/vendor/github.com/cilium/ebpf/prog.go index d479a3881..f4f3af7c3 100644 --- a/vendor/github.com/cilium/ebpf/prog.go +++ b/vendor/github.com/cilium/ebpf/prog.go @@ -24,6 +24,18 @@ import ( // ErrNotSupported is returned whenever the kernel doesn't support a feature. var ErrNotSupported = internal.ErrNotSupported +// errBadRelocation is returned when the verifier rejects a program due to a +// bad CO-RE relocation. +// +// This error is detected based on heuristics and therefore may not be reliable. +var errBadRelocation = errors.New("bad CO-RE relocation") + +// errUnknownKfunc is returned when the verifier rejects a program due to an +// unknown kfunc. +// +// This error is detected based on heuristics and therefore may not be reliable. +var errUnknownKfunc = errors.New("unknown kfunc") + // ProgramID represents the unique ID of an eBPF program. type ProgramID uint32 @@ -228,6 +240,15 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er return prog, err } +var ( + coreBadLoad = []byte(fmt.Sprintf("(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)) +) + func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) { if len(spec.Instructions) == 0 { return nil, errors.New("instructions cannot be empty") @@ -416,6 +437,12 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er _, err2 = sys.ProgLoad(attr) } + end := bytes.IndexByte(logBuf, 0) + if end < 0 { + end = len(logBuf) + } + + tail := logBuf[max(end-256, 0):end] switch { case errors.Is(err, unix.EPERM): if len(logBuf) > 0 && logBuf[0] == 0 { @@ -424,18 +451,32 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er return nil, fmt.Errorf("load program: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err) } - fallthrough - case errors.Is(err, unix.EINVAL): - if hasFunctionReferences(spec.Instructions) { - if err := haveBPFToBPFCalls(); err != nil { - return nil, fmt.Errorf("load program: %w", err) - } - } - if opts.LogSize > maxVerifierLogSize { return nil, fmt.Errorf("load program: %w (ProgramOptions.LogSize exceeds maximum value of %d)", err, maxVerifierLogSize) } + + if bytes.Contains(tail, coreBadCall) { + err = errBadRelocation + break + } else if bytes.Contains(tail, kfuncBadCall) { + err = errUnknownKfunc + break + } + + case errors.Is(err, unix.EACCES): + if bytes.Contains(tail, coreBadLoad) { + err = errBadRelocation + break + } + } + + // hasFunctionReferences may be expensive, so check it last. + if (errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM)) && + hasFunctionReferences(spec.Instructions) { + if err := haveBPFToBPFCalls(); err != nil { + return nil, fmt.Errorf("load program: %w", err) + } } truncated := errors.Is(err, unix.ENOSPC) || errors.Is(err2, unix.ENOSPC) @@ -618,7 +659,7 @@ type RunOptions struct { } // Test runs the Program in the kernel with the given input and returns the -// value returned by the eBPF program. outLen may be zero. +// value returned by the eBPF program. // // Note: the kernel expects at least 14 bytes input for an ethernet header for // XDP and SKB programs. diff --git a/vendor/modules.txt b/vendor/modules.txt index 2b3ad0685..18a8cfbd0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -32,7 +32,7 @@ github.com/cenkalti/backoff/v4 # github.com/cespare/xxhash/v2 v2.2.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 -# github.com/cilium/ebpf v0.14.0 +# github.com/cilium/ebpf v0.15.0 ## explicit; go 1.21.0 github.com/cilium/ebpf github.com/cilium/ebpf/asm