github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/link/uprobe_multi_test.go (about)

     1  package link
     2  
     3  import (
     4  	"errors"
     5  	"math"
     6  	"os"
     7  	"os/exec"
     8  	"testing"
     9  
    10  	"github.com/cilium/ebpf"
    11  	"github.com/cilium/ebpf/internal/testutils"
    12  	"github.com/go-quicktest/qt"
    13  )
    14  
    15  func TestUprobeMulti(t *testing.T) {
    16  	testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti())
    17  
    18  	prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "")
    19  
    20  	// uprobe
    21  	um, err := bashEx.UprobeMulti(bashSyms, prog, nil)
    22  	if err != nil {
    23  		t.Fatal(err)
    24  	}
    25  
    26  	testLink(t, um, prog)
    27  	_ = um.Close()
    28  
    29  	// uretprobe
    30  	um, err = bashEx.UretprobeMulti(bashSyms, prog, nil)
    31  	if err != nil {
    32  		t.Fatal(err)
    33  	}
    34  
    35  	testLink(t, um, prog)
    36  	_ = um.Close()
    37  }
    38  
    39  func TestUprobeMultiInput(t *testing.T) {
    40  	testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti())
    41  
    42  	prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "")
    43  
    44  	// Always doing same test for both uprobe and uretprobe
    45  
    46  	// One of symbols or offsets must be given.
    47  	_, err := bashEx.UprobeMulti([]string{}, prog, nil)
    48  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
    49  
    50  	_, err = bashEx.UretprobeMulti([]string{}, prog, nil)
    51  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
    52  
    53  	// One address, two cookies.
    54  	_, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{
    55  		Addresses: []uint64{1},
    56  		Cookies:   []uint64{2, 3},
    57  	})
    58  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
    59  
    60  	_, err = bashEx.UretprobeMulti([]string{}, prog, &UprobeMultiOptions{
    61  		Addresses: []uint64{1},
    62  		Cookies:   []uint64{2, 3},
    63  	})
    64  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
    65  
    66  	// Two addresses, one refctr offset.
    67  	_, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{
    68  		Addresses:     []uint64{1, 2},
    69  		RefCtrOffsets: []uint64{4},
    70  	})
    71  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
    72  
    73  	_, err = bashEx.UretprobeMulti([]string{}, prog, &UprobeMultiOptions{
    74  		Addresses:     []uint64{1, 2},
    75  		RefCtrOffsets: []uint64{4},
    76  	})
    77  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
    78  
    79  	// It's either symbols or addresses.
    80  	_, err = bashEx.UprobeMulti(bashSyms, prog, &UprobeMultiOptions{
    81  		Addresses: []uint64{1},
    82  	})
    83  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
    84  
    85  	_, err = bashEx.UretprobeMulti(bashSyms, prog, &UprobeMultiOptions{
    86  		Addresses: []uint64{1},
    87  	})
    88  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
    89  
    90  	// No addresses and no symbols
    91  	_, err = bashEx.UprobeMulti([]string{}, prog, nil)
    92  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
    93  
    94  	_, err = bashEx.UretprobeMulti([]string{}, prog, nil)
    95  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
    96  
    97  	// PID not found
    98  	_, err = bashEx.UprobeMulti(bashSyms, prog, &UprobeMultiOptions{
    99  		PID: math.MaxUint32,
   100  	})
   101  	qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist))
   102  
   103  	_, err = bashEx.UretprobeMulti(bashSyms, prog, &UprobeMultiOptions{
   104  		PID: math.MaxUint32,
   105  	})
   106  	qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist))
   107  }
   108  
   109  func TestUprobeMultiResolveOk(t *testing.T) {
   110  	addrSym1, err := bashEx.address(bashSyms[0], 0, 0)
   111  	qt.Assert(t, qt.IsNil(err))
   112  	addrSym2, err := bashEx.address(bashSyms[1], 0, 0)
   113  	qt.Assert(t, qt.IsNil(err))
   114  	addrSym3, err := bashEx.address(bashSyms[2], 0, 0)
   115  	qt.Assert(t, qt.IsNil(err))
   116  
   117  	addrs, err := bashEx.addresses(bashSyms, nil, nil)
   118  	qt.Assert(t, qt.IsNil(err))
   119  	qt.Assert(t, qt.DeepEquals(addrs, []uint64{addrSym1, addrSym2, addrSym3}))
   120  
   121  	addrs, err = bashEx.addresses(bashSyms, nil, []uint64{5, 10, 11})
   122  	qt.Assert(t, qt.IsNil(err))
   123  	qt.Assert(t, qt.DeepEquals(addrs, []uint64{addrSym1 + 5, addrSym2 + 10, addrSym3 + 11}))
   124  
   125  	addrs, err = bashEx.addresses(bashSyms, []uint64{1, 2, 3}, nil)
   126  	qt.Assert(t, qt.IsNil(err))
   127  
   128  	qt.Assert(t, qt.DeepEquals(addrs, []uint64{1, 2, 3}))
   129  }
   130  
   131  func TestUprobeMultiResolveFail(t *testing.T) {
   132  	// No input
   133  	_, err := bashEx.addresses(nil, nil, nil)
   134  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
   135  
   136  	// Different dimensions for Addresses and Offsets
   137  	_, err = bashEx.addresses(nil, []uint64{100, 200}, []uint64{5, 10, 11})
   138  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
   139  
   140  	// Different dimensions for symbols and Offsets
   141  	_, err = bashEx.addresses(bashSyms, nil, []uint64{5, 10})
   142  	qt.Assert(t, qt.ErrorIs(err, errInvalidInput))
   143  }
   144  
   145  func TestUprobeMultiCookie(t *testing.T) {
   146  	testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti())
   147  
   148  	prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "")
   149  
   150  	// uprobe
   151  	um, err := bashEx.UprobeMulti(bashSyms, prog,
   152  		&UprobeMultiOptions{
   153  			Cookies: []uint64{1, 2, 3},
   154  		})
   155  	if err != nil {
   156  		t.Fatal(err)
   157  	}
   158  	_ = um.Close()
   159  
   160  	// uretprobe
   161  	um, err = bashEx.UretprobeMulti(bashSyms, prog,
   162  		&UprobeMultiOptions{
   163  			Cookies: []uint64{3, 2, 1},
   164  		})
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	_ = um.Close()
   169  }
   170  
   171  func TestUprobeMultiProgramCall(t *testing.T) {
   172  	testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti())
   173  
   174  	// We execute 'bash --help'
   175  	args := []string{"--help"}
   176  	elf := "/bin/bash"
   177  
   178  	test := func(retprobe bool, expected uint32) {
   179  		m, p := newUpdaterMapProg(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti)
   180  
   181  		var err error
   182  
   183  		// Load the executable.
   184  		ex, err := OpenExecutable(elf)
   185  		if err != nil {
   186  			t.Fatal(err)
   187  		}
   188  
   189  		var um Link
   190  
   191  		// Open UprobeMulti on the executable for the given symbol
   192  		// and attach it to the ebpf program created above.
   193  		if retprobe {
   194  			um, err = ex.UretprobeMulti(bashSyms, p, nil)
   195  		} else {
   196  			um, err = ex.UprobeMulti(bashSyms, p, nil)
   197  		}
   198  		if errors.Is(err, ErrNoSymbol) {
   199  			// Assume bash_Syms symbols always exist and skip the test
   200  			// if the symbol can't be found as certain OS (eg. Debian)
   201  			// strip binaries.
   202  			t.Skipf("executable %s appear to be stripped, skipping", elf)
   203  		}
   204  		if err != nil {
   205  			t.Fatal(err)
   206  		}
   207  
   208  		// Trigger ebpf program call.
   209  		trigger := func(t *testing.T) {
   210  			if err := exec.Command(elf, args...).Run(); err != nil {
   211  				t.Fatal(err)
   212  			}
   213  		}
   214  		trigger(t)
   215  
   216  		// Detach link.
   217  		if err := um.Close(); err != nil {
   218  			t.Fatal(err)
   219  		}
   220  
   221  		assertMapValueGE(t, m, 0, expected)
   222  
   223  		// Reset map value to 0 at index 0.
   224  		if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil {
   225  			t.Fatal(err)
   226  		}
   227  
   228  		// Retrigger the ebpf program call.
   229  		trigger(t)
   230  
   231  		// Assert that this time the value has not been updated.
   232  		assertMapValue(t, m, 0, 0)
   233  	}
   234  
   235  	// all 3 uprobes should trigger for entry uprobes
   236  	test(false, 3)
   237  
   238  	// We have return uprobe installed on main, _start and check_dev_tty
   239  	// functions, but only check_dev_tty is triggered, because 'bash --help'
   240  	// calls exit(0).
   241  	test(true, 1)
   242  }
   243  
   244  func TestHaveBPFLinkUprobeMulti(t *testing.T) {
   245  	testutils.CheckFeatureTest(t, haveBPFLinkUprobeMulti)
   246  }