Files
runc/vendor/github.com/cilium/ebpf/link/uprobe_multi.go
T
Kir Kolyshkin 79a4ac0553 deps: bump cilium/ebpf to v0.17.3
It has a fix for runc issue 4594.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2025-02-13 00:07:01 -08:00

220 lines
6.0 KiB
Go

package link
import (
"errors"
"fmt"
"os"
"unsafe"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/sys"
"github.com/cilium/ebpf/internal/unix"
)
// UprobeMultiOptions defines additional parameters that will be used
// when opening a UprobeMulti Link.
type UprobeMultiOptions struct {
// Symbol addresses. If set, overrides the addresses eventually parsed from
// the executable. Mutually exclusive with UprobeMulti's symbols argument.
Addresses []uint64
// Offsets into functions provided by UprobeMulti's symbols argument.
// For example: to set uprobes to main+5 and _start+10, call UprobeMulti
// with:
// symbols: "main", "_start"
// opt.Offsets: 5, 10
Offsets []uint64
// Optional list of associated ref counter offsets.
RefCtrOffsets []uint64
// Optional list of associated BPF cookies.
Cookies []uint64
// Only set the uprobe_multi link on the given process ID, zero PID means
// system-wide.
PID uint32
}
func (ex *Executable) UprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions) (Link, error) {
return ex.uprobeMulti(symbols, prog, opts, 0)
}
func (ex *Executable) UretprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions) (Link, error) {
// The return probe is not limited for symbols entry, so there's no special
// setup for return uprobes (other than the extra flag). The symbols, opts.Offsets
// and opts.Addresses arrays follow the same logic as for entry uprobes.
return ex.uprobeMulti(symbols, prog, opts, sys.BPF_F_UPROBE_MULTI_RETURN)
}
func (ex *Executable) uprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions, flags uint32) (Link, error) {
if prog == nil {
return nil, errors.New("cannot attach a nil program")
}
if opts == nil {
opts = &UprobeMultiOptions{}
}
addresses, err := ex.addresses(symbols, opts.Addresses, opts.Offsets)
if err != nil {
return nil, err
}
addrs := len(addresses)
cookies := len(opts.Cookies)
refCtrOffsets := len(opts.RefCtrOffsets)
if addrs == 0 {
return nil, fmt.Errorf("Addresses are required: %w", errInvalidInput)
}
if refCtrOffsets > 0 && refCtrOffsets != addrs {
return nil, fmt.Errorf("RefCtrOffsets must be exactly Addresses in length: %w", errInvalidInput)
}
if cookies > 0 && cookies != addrs {
return nil, fmt.Errorf("Cookies must be exactly Addresses in length: %w", errInvalidInput)
}
attr := &sys.LinkCreateUprobeMultiAttr{
Path: sys.NewStringPointer(ex.path),
ProgFd: uint32(prog.FD()),
AttachType: sys.BPF_TRACE_UPROBE_MULTI,
UprobeMultiFlags: flags,
Count: uint32(addrs),
Offsets: sys.NewPointer(unsafe.Pointer(&addresses[0])),
Pid: opts.PID,
}
if refCtrOffsets != 0 {
attr.RefCtrOffsets = sys.NewPointer(unsafe.Pointer(&opts.RefCtrOffsets[0]))
}
if cookies != 0 {
attr.Cookies = sys.NewPointer(unsafe.Pointer(&opts.Cookies[0]))
}
fd, err := sys.LinkCreateUprobeMulti(attr)
if errors.Is(err, unix.ESRCH) {
return nil, fmt.Errorf("%w (specified pid not found?)", os.ErrNotExist)
}
// Since Linux commit 46ba0e49b642 ("bpf: fix multi-uprobe PID filtering
// logic"), if the provided pid overflows MaxInt32 (turning it negative), the
// kernel will return EINVAL instead of ESRCH.
if errors.Is(err, unix.EINVAL) {
return nil, fmt.Errorf("%w (invalid pid, missing symbol or prog's AttachType not AttachTraceUprobeMulti?)", err)
}
if err != nil {
if haveFeatErr := haveBPFLinkUprobeMulti(); haveFeatErr != nil {
return nil, haveFeatErr
}
return nil, err
}
return &uprobeMultiLink{RawLink{fd, ""}}, nil
}
func (ex *Executable) addresses(symbols []string, addresses, offsets []uint64) ([]uint64, error) {
n := len(symbols)
if n == 0 {
n = len(addresses)
}
if n == 0 {
return nil, fmt.Errorf("%w: neither symbols nor addresses given", errInvalidInput)
}
if symbols != nil && len(symbols) != n {
return nil, fmt.Errorf("%w: have %d symbols but want %d", errInvalidInput, len(symbols), n)
}
if addresses != nil && len(addresses) != n {
return nil, fmt.Errorf("%w: have %d addresses but want %d", errInvalidInput, len(addresses), n)
}
if offsets != nil && len(offsets) != n {
return nil, fmt.Errorf("%w: have %d offsets but want %d", errInvalidInput, len(offsets), n)
}
results := make([]uint64, 0, n)
for i := 0; i < n; i++ {
var sym string
if symbols != nil {
sym = symbols[i]
}
var addr, off uint64
if addresses != nil {
addr = addresses[i]
}
if offsets != nil {
off = offsets[i]
}
result, err := ex.address(sym, addr, off)
if err != nil {
return nil, err
}
results = append(results, result)
}
return results, nil
}
type uprobeMultiLink struct {
RawLink
}
var _ Link = (*uprobeMultiLink)(nil)
func (kml *uprobeMultiLink) Update(_ *ebpf.Program) error {
return fmt.Errorf("update uprobe_multi: %w", ErrNotSupported)
}
var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", func() error {
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
Name: "probe_upm_link",
Type: ebpf.Kprobe,
Instructions: asm.Instructions{
asm.Mov.Imm(asm.R0, 0),
asm.Return(),
},
AttachType: ebpf.AttachTraceUprobeMulti,
License: "MIT",
})
if errors.Is(err, unix.E2BIG) {
// Kernel doesn't support AttachType field.
return internal.ErrNotSupported
}
if err != nil {
return err
}
defer prog.Close()
// We try to create uprobe multi link on '/' path which results in
// error with -EBADF in case uprobe multi link is supported.
fd, err := sys.LinkCreateUprobeMulti(&sys.LinkCreateUprobeMultiAttr{
ProgFd: uint32(prog.FD()),
AttachType: sys.BPF_TRACE_UPROBE_MULTI,
Path: sys.NewStringPointer("/"),
Offsets: sys.NewPointer(unsafe.Pointer(&[]uint64{0})),
Count: 1,
})
switch {
case errors.Is(err, unix.EBADF):
return nil
case errors.Is(err, unix.EINVAL):
return internal.ErrNotSupported
case err != nil:
return err
}
// should not happen
fd.Close()
return errors.New("successfully attached uprobe_multi to /, kernel bug?")
}, "6.6")