mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-10 21:53:57 +08:00
79a4ac0553
It has a fix for runc issue 4594. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
220 lines
6.0 KiB
Go
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")
|