Skip to content

Commit

Permalink
link/kprobe.multi: support addresses array as an alternative to symbo…
Browse files Browse the repository at this point in the history
…ls array
  • Loading branch information
mmat11 committed Jun 17, 2022
1 parent 6091ecb commit 0fedd4d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 20 deletions.
60 changes: 40 additions & 20 deletions link/kprobe_multi.go
Expand Up @@ -15,21 +15,23 @@ import (

// KprobeMultiOptions defines additional parameters that will be used
// when opening a KprobeMulti Link.
//
// Symbols and Addresses are mutually exclusive.
type KprobeMultiOptions struct {
// Symbols is an array of kernel symbols to attach the ebpf program to.
Symbols []string
// Addresses is an array of kernel symbols' addresses.
Addresses []uint64
// Cookies is an array of arbitrary values that can be fetched from an eBPF program
// via `bpf_get_attach_cookie()`.
//
// If set, its length should be equal to the length of Symbols.
//
// Cookies will be assigned to Symbols based on their ordering.
Cookies []uint64

// Internal field. Only used for retprobes.
flags uint32

// TODO(matt): libbpf allows attaching via a pattern and an array of addresses;
// add these options for the first iteration?
}

// KprobeMulti attaches the given eBPF program to the entry point of a set of
Expand All @@ -56,30 +58,46 @@ func kprobeMulti(prog *ebpf.Program, opts *KprobeMultiOptions) (Link, error) {
return nil, errors.New("kprobe.multi: missing options")
}

cnt := uint32(len(opts.Symbols))
cookiesCnt := uint32(len(opts.Cookies))
if cnt == 0 {
return nil, errors.New("kprobe.multi: missing symbols array")
}
if cookiesCnt > 0 && cookiesCnt != cnt {
return nil, errors.New("kprobe.multi: invalid cookies array length")
}
symbolsCnt := uint32(len(opts.Symbols))
addressesCnt := uint32(len(opts.Addresses))

syms := make([]uint64, 0)
for _, s := range opts.Symbols {
sptr, err := unsafeStringPtr(s)
if err != nil {
return nil, fmt.Errorf("kprobe.multi: %w", err)
}
syms = append(syms, uint64(uintptr(sptr)))
if symbolsCnt > 0 && addressesCnt > 0 {
return nil, errors.New("kprobe.multi: symbols and addresses are mutually exclusive")
}
if symbolsCnt == 0 && addressesCnt == 0 {
return nil, errors.New("kprobe.multi: missing symbols/addresses array")
}

cookiesCnt := uint32(len(opts.Cookies))
attr := &sys.LinkCreateKprobeMultiAttr{
ProgFd: uint32(prog.FD()),
AttachType: sys.BPF_TRACE_KPROBE_MULTI,
KprobeMultiFlags: opts.flags,
Cnt: cnt,
Syms: sys.NewPointer(unsafe.Pointer(&syms[0])),
}

syms := make([]uint64, 0)
if symbolsCnt > addressesCnt {
if cookiesCnt > 0 && cookiesCnt != symbolsCnt {
return nil, errors.New("kprobe.multi: invalid cookies array length")
}

for _, s := range opts.Symbols {
sptr, err := unsafeStringPtr(s)
if err != nil {
return nil, fmt.Errorf("kprobe.multi: %w", err)
}
syms = append(syms, uint64(uintptr(sptr)))
}

attr.Cnt = symbolsCnt
attr.Syms = sys.NewPointer(unsafe.Pointer(&syms[0]))
} else {
if cookiesCnt > 0 && cookiesCnt != addressesCnt {
return nil, errors.New("kprobe.multi: invalid cookies array length")
}

attr.Cnt = addressesCnt
attr.Addrs = sys.NewPointer(unsafe.Pointer(&opts.Addresses[0]))
}

if cookiesCnt > 0 {
Expand Down Expand Up @@ -118,6 +136,8 @@ func (kml *kprobeMultiLink) Unpin() error {
// Probe BPF kprobe multi link.
//
// patchwork.kernel.org/project/netdevbpf/list/?series=623878&state=*
//
// TODO(matt): do we need this feature check?
var haveBPFLinkKprobeMulti = internal.FeatureTest("bpf_link_kprobe_multi", "5.18", func() error {
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
Name: "probe_bpf_kprobe_multi_link",
Expand Down
27 changes: 27 additions & 0 deletions link/kprobe_multi_test.go
@@ -1,6 +1,8 @@
package link

import (
"math"
"strings"
"testing"

"github.com/cilium/ebpf"
Expand Down Expand Up @@ -31,6 +33,31 @@ func TestKprobeMulti(t *testing.T) {
}
}

func TestKprobeMultiAddresses(t *testing.T) {
testutils.SkipOnOldKernel(t, "5.18", "kprobe_multi link")

prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, "")
// Only have a negative test for addresses as it would be hard to maintain
// a proper one.
// The test correctly fails if the address is set to the start range of perf kallsyms
// for any symbol:
//
// sudo perf kallsyms __x64_sys_getpid
// __x64_sys_getpid: [kernel] [kernel.kallsyms] 0xffffffffa10f9570-0xffffffffa10f9590 (0xffffffffa10f9570-0xffffffffa10f9590)
opts := KprobeMultiOptions{Addresses: []uint64{math.MaxUint64}}
_, err := KprobeMulti(prog, &opts)
if err == nil {
t.Fatal("expected err to not be nil")
}

opts.Symbols = []string{"bogus"}
_, err = KprobeMulti(prog, &opts)
// Assert this is not a link_create error.
if !strings.Contains(err.Error(), "mutually exclusive") {
t.Fatalf("expected fail due to Symbols and Addresses being mutual exclusive, got: %v", err)
}
}

func TestKprobeMultiCookieMismatch(t *testing.T) {
testutils.SkipOnOldKernel(t, "5.18", "kprobe_multi link")

Expand Down

0 comments on commit 0fedd4d

Please sign in to comment.