cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

usdt.c (12963B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
      3#include <test_progs.h>
      4
      5#define _SDT_HAS_SEMAPHORES 1
      6#include "../sdt.h"
      7
      8#include "test_usdt.skel.h"
      9#include "test_urandom_usdt.skel.h"
     10
     11int lets_test_this(int);
     12
     13static volatile int idx = 2;
     14static volatile __u64 bla = 0xFEDCBA9876543210ULL;
     15static volatile short nums[] = {-1, -2, -3, };
     16
     17static volatile struct {
     18	int x;
     19	signed char y;
     20} t1 = { 1, -127 };
     21
     22#define SEC(name) __attribute__((section(name), used))
     23
     24unsigned short test_usdt0_semaphore SEC(".probes");
     25unsigned short test_usdt3_semaphore SEC(".probes");
     26unsigned short test_usdt12_semaphore SEC(".probes");
     27
     28static void __always_inline trigger_func(int x) {
     29	long y = 42;
     30
     31	if (test_usdt0_semaphore)
     32		STAP_PROBE(test, usdt0);
     33	if (test_usdt3_semaphore)
     34		STAP_PROBE3(test, usdt3, x, y, &bla);
     35	if (test_usdt12_semaphore) {
     36		STAP_PROBE12(test, usdt12,
     37			     x, x + 1, y, x + y, 5,
     38			     y / 7, bla, &bla, -9, nums[x],
     39			     nums[idx], t1.y);
     40	}
     41}
     42
     43static void subtest_basic_usdt(void)
     44{
     45	LIBBPF_OPTS(bpf_usdt_opts, opts);
     46	struct test_usdt *skel;
     47	struct test_usdt__bss *bss;
     48	int err;
     49
     50	skel = test_usdt__open_and_load();
     51	if (!ASSERT_OK_PTR(skel, "skel_open"))
     52		return;
     53
     54	bss = skel->bss;
     55	bss->my_pid = getpid();
     56
     57	err = test_usdt__attach(skel);
     58	if (!ASSERT_OK(err, "skel_attach"))
     59		goto cleanup;
     60
     61	/* usdt0 won't be auto-attached */
     62	opts.usdt_cookie = 0xcafedeadbeeffeed;
     63	skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0,
     64						     0 /*self*/, "/proc/self/exe",
     65						     "test", "usdt0", &opts);
     66	if (!ASSERT_OK_PTR(skel->links.usdt0, "usdt0_link"))
     67		goto cleanup;
     68
     69	trigger_func(1);
     70
     71	ASSERT_EQ(bss->usdt0_called, 1, "usdt0_called");
     72	ASSERT_EQ(bss->usdt3_called, 1, "usdt3_called");
     73	ASSERT_EQ(bss->usdt12_called, 1, "usdt12_called");
     74
     75	ASSERT_EQ(bss->usdt0_cookie, 0xcafedeadbeeffeed, "usdt0_cookie");
     76	ASSERT_EQ(bss->usdt0_arg_cnt, 0, "usdt0_arg_cnt");
     77	ASSERT_EQ(bss->usdt0_arg_ret, -ENOENT, "usdt0_arg_ret");
     78
     79	/* auto-attached usdt3 gets default zero cookie value */
     80	ASSERT_EQ(bss->usdt3_cookie, 0, "usdt3_cookie");
     81	ASSERT_EQ(bss->usdt3_arg_cnt, 3, "usdt3_arg_cnt");
     82
     83	ASSERT_EQ(bss->usdt3_arg_rets[0], 0, "usdt3_arg1_ret");
     84	ASSERT_EQ(bss->usdt3_arg_rets[1], 0, "usdt3_arg2_ret");
     85	ASSERT_EQ(bss->usdt3_arg_rets[2], 0, "usdt3_arg3_ret");
     86	ASSERT_EQ(bss->usdt3_args[0], 1, "usdt3_arg1");
     87	ASSERT_EQ(bss->usdt3_args[1], 42, "usdt3_arg2");
     88	ASSERT_EQ(bss->usdt3_args[2], (uintptr_t)&bla, "usdt3_arg3");
     89
     90	/* auto-attached usdt12 gets default zero cookie value */
     91	ASSERT_EQ(bss->usdt12_cookie, 0, "usdt12_cookie");
     92	ASSERT_EQ(bss->usdt12_arg_cnt, 12, "usdt12_arg_cnt");
     93
     94	ASSERT_EQ(bss->usdt12_args[0], 1, "usdt12_arg1");
     95	ASSERT_EQ(bss->usdt12_args[1], 1 + 1, "usdt12_arg2");
     96	ASSERT_EQ(bss->usdt12_args[2], 42, "usdt12_arg3");
     97	ASSERT_EQ(bss->usdt12_args[3], 42 + 1, "usdt12_arg4");
     98	ASSERT_EQ(bss->usdt12_args[4], 5, "usdt12_arg5");
     99	ASSERT_EQ(bss->usdt12_args[5], 42 / 7, "usdt12_arg6");
    100	ASSERT_EQ(bss->usdt12_args[6], bla, "usdt12_arg7");
    101	ASSERT_EQ(bss->usdt12_args[7], (uintptr_t)&bla, "usdt12_arg8");
    102	ASSERT_EQ(bss->usdt12_args[8], -9, "usdt12_arg9");
    103	ASSERT_EQ(bss->usdt12_args[9], nums[1], "usdt12_arg10");
    104	ASSERT_EQ(bss->usdt12_args[10], nums[idx], "usdt12_arg11");
    105	ASSERT_EQ(bss->usdt12_args[11], t1.y, "usdt12_arg12");
    106
    107	/* trigger_func() is marked __always_inline, so USDT invocations will be
    108	 * inlined in two different places, meaning that each USDT will have
    109	 * at least 2 different places to be attached to. This verifies that
    110	 * bpf_program__attach_usdt() handles this properly and attaches to
    111	 * all possible places of USDT invocation.
    112	 */
    113	trigger_func(2);
    114
    115	ASSERT_EQ(bss->usdt0_called, 2, "usdt0_called");
    116	ASSERT_EQ(bss->usdt3_called, 2, "usdt3_called");
    117	ASSERT_EQ(bss->usdt12_called, 2, "usdt12_called");
    118
    119	/* only check values that depend on trigger_func()'s input value */
    120	ASSERT_EQ(bss->usdt3_args[0], 2, "usdt3_arg1");
    121
    122	ASSERT_EQ(bss->usdt12_args[0], 2, "usdt12_arg1");
    123	ASSERT_EQ(bss->usdt12_args[1], 2 + 1, "usdt12_arg2");
    124	ASSERT_EQ(bss->usdt12_args[3], 42 + 2, "usdt12_arg4");
    125	ASSERT_EQ(bss->usdt12_args[9], nums[2], "usdt12_arg10");
    126
    127	/* detach and re-attach usdt3 */
    128	bpf_link__destroy(skel->links.usdt3);
    129
    130	opts.usdt_cookie = 0xBADC00C51E;
    131	skel->links.usdt3 = bpf_program__attach_usdt(skel->progs.usdt3, -1 /* any pid */,
    132						     "/proc/self/exe", "test", "usdt3", &opts);
    133	if (!ASSERT_OK_PTR(skel->links.usdt3, "usdt3_reattach"))
    134		goto cleanup;
    135
    136	trigger_func(3);
    137
    138	ASSERT_EQ(bss->usdt3_called, 3, "usdt3_called");
    139	/* this time usdt3 has custom cookie */
    140	ASSERT_EQ(bss->usdt3_cookie, 0xBADC00C51E, "usdt3_cookie");
    141	ASSERT_EQ(bss->usdt3_arg_cnt, 3, "usdt3_arg_cnt");
    142
    143	ASSERT_EQ(bss->usdt3_arg_rets[0], 0, "usdt3_arg1_ret");
    144	ASSERT_EQ(bss->usdt3_arg_rets[1], 0, "usdt3_arg2_ret");
    145	ASSERT_EQ(bss->usdt3_arg_rets[2], 0, "usdt3_arg3_ret");
    146	ASSERT_EQ(bss->usdt3_args[0], 3, "usdt3_arg1");
    147	ASSERT_EQ(bss->usdt3_args[1], 42, "usdt3_arg2");
    148	ASSERT_EQ(bss->usdt3_args[2], (uintptr_t)&bla, "usdt3_arg3");
    149
    150cleanup:
    151	test_usdt__destroy(skel);
    152}
    153
    154unsigned short test_usdt_100_semaphore SEC(".probes");
    155unsigned short test_usdt_300_semaphore SEC(".probes");
    156unsigned short test_usdt_400_semaphore SEC(".probes");
    157
    158#define R10(F, X)  F(X+0); F(X+1);F(X+2); F(X+3); F(X+4); \
    159		   F(X+5); F(X+6); F(X+7); F(X+8); F(X+9);
    160#define R100(F, X) R10(F,X+ 0);R10(F,X+10);R10(F,X+20);R10(F,X+30);R10(F,X+40); \
    161		   R10(F,X+50);R10(F,X+60);R10(F,X+70);R10(F,X+80);R10(F,X+90);
    162
    163/* carefully control that we get exactly 100 inlines by preventing inlining */
    164static void __always_inline f100(int x)
    165{
    166	STAP_PROBE1(test, usdt_100, x);
    167}
    168
    169__weak void trigger_100_usdts(void)
    170{
    171	R100(f100, 0);
    172}
    173
    174/* we shouldn't be able to attach to test:usdt2_300 USDT as we don't have as
    175 * many slots for specs. It's important that each STAP_PROBE2() invocation
    176 * (after untolling) gets different arg spec due to compiler inlining i as
    177 * a constant
    178 */
    179static void __always_inline f300(int x)
    180{
    181	STAP_PROBE1(test, usdt_300, x);
    182}
    183
    184__weak void trigger_300_usdts(void)
    185{
    186	R100(f300, 0);
    187	R100(f300, 100);
    188	R100(f300, 200);
    189}
    190
    191static void __always_inline f400(int x __attribute__((unused)))
    192{
    193	STAP_PROBE1(test, usdt_400, 400);
    194}
    195
    196/* this time we have 400 different USDT call sites, but they have uniform
    197 * argument location, so libbpf's spec string deduplication logic should keep
    198 * spec count use very small and so we should be able to attach to all 400
    199 * call sites
    200 */
    201__weak void trigger_400_usdts(void)
    202{
    203	R100(f400, 0);
    204	R100(f400, 100);
    205	R100(f400, 200);
    206	R100(f400, 300);
    207}
    208
    209static void subtest_multispec_usdt(void)
    210{
    211	LIBBPF_OPTS(bpf_usdt_opts, opts);
    212	struct test_usdt *skel;
    213	struct test_usdt__bss *bss;
    214	int err, i;
    215
    216	skel = test_usdt__open_and_load();
    217	if (!ASSERT_OK_PTR(skel, "skel_open"))
    218		return;
    219
    220	bss = skel->bss;
    221	bss->my_pid = getpid();
    222
    223	err = test_usdt__attach(skel);
    224	if (!ASSERT_OK(err, "skel_attach"))
    225		goto cleanup;
    226
    227	/* usdt_100 is auto-attached and there are 100 inlined call sites,
    228	 * let's validate that all of them are properly attached to and
    229	 * handled from BPF side
    230	 */
    231	trigger_100_usdts();
    232
    233	ASSERT_EQ(bss->usdt_100_called, 100, "usdt_100_called");
    234	ASSERT_EQ(bss->usdt_100_sum, 99 * 100 / 2, "usdt_100_sum");
    235
    236	/* Stress test free spec ID tracking. By default libbpf allows up to
    237	 * 256 specs to be used, so if we don't return free spec IDs back
    238	 * after few detachments and re-attachments we should run out of
    239	 * available spec IDs.
    240	 */
    241	for (i = 0; i < 2; i++) {
    242		bpf_link__destroy(skel->links.usdt_100);
    243
    244		skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1,
    245							        "/proc/self/exe",
    246								"test", "usdt_100", NULL);
    247		if (!ASSERT_OK_PTR(skel->links.usdt_100, "usdt_100_reattach"))
    248			goto cleanup;
    249
    250		bss->usdt_100_sum = 0;
    251		trigger_100_usdts();
    252
    253		ASSERT_EQ(bss->usdt_100_called, (i + 1) * 100 + 100, "usdt_100_called");
    254		ASSERT_EQ(bss->usdt_100_sum, 99 * 100 / 2, "usdt_100_sum");
    255	}
    256
    257	/* Now let's step it up and try to attach USDT that requires more than
    258	 * 256 attach points with different specs for each.
    259	 * Note that we need trigger_300_usdts() only to actually have 300
    260	 * USDT call sites, we are not going to actually trace them.
    261	 */
    262	trigger_300_usdts();
    263
    264	/* we'll reuse usdt_100 BPF program for usdt_300 test */
    265	bpf_link__destroy(skel->links.usdt_100);
    266	skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1, "/proc/self/exe",
    267							"test", "usdt_300", NULL);
    268	err = -errno;
    269	if (!ASSERT_ERR_PTR(skel->links.usdt_100, "usdt_300_bad_attach"))
    270		goto cleanup;
    271	ASSERT_EQ(err, -E2BIG, "usdt_300_attach_err");
    272
    273	/* let's check that there are no "dangling" BPF programs attached due
    274	 * to partial success of the above test:usdt_300 attachment
    275	 */
    276	bss->usdt_100_called = 0;
    277	bss->usdt_100_sum = 0;
    278
    279	f300(777); /* this is 301st instance of usdt_300 */
    280
    281	ASSERT_EQ(bss->usdt_100_called, 0, "usdt_301_called");
    282	ASSERT_EQ(bss->usdt_100_sum, 0, "usdt_301_sum");
    283
    284	/* This time we have USDT with 400 inlined invocations, but arg specs
    285	 * should be the same across all sites, so libbpf will only need to
    286	 * use one spec and thus we'll be able to attach 400 uprobes
    287	 * successfully.
    288	 *
    289	 * Again, we are reusing usdt_100 BPF program.
    290	 */
    291	skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1,
    292							"/proc/self/exe",
    293							"test", "usdt_400", NULL);
    294	if (!ASSERT_OK_PTR(skel->links.usdt_100, "usdt_400_attach"))
    295		goto cleanup;
    296
    297	trigger_400_usdts();
    298
    299	ASSERT_EQ(bss->usdt_100_called, 400, "usdt_400_called");
    300	ASSERT_EQ(bss->usdt_100_sum, 400 * 400, "usdt_400_sum");
    301
    302cleanup:
    303	test_usdt__destroy(skel);
    304}
    305
    306static FILE *urand_spawn(int *pid)
    307{
    308	FILE *f;
    309
    310	/* urandom_read's stdout is wired into f */
    311	f = popen("./urandom_read 1 report-pid", "r");
    312	if (!f)
    313		return NULL;
    314
    315	if (fscanf(f, "%d", pid) != 1) {
    316		pclose(f);
    317		return NULL;
    318	}
    319
    320	return f;
    321}
    322
    323static int urand_trigger(FILE **urand_pipe)
    324{
    325	int exit_code;
    326
    327	/* pclose() waits for child process to exit and returns their exit code */
    328	exit_code = pclose(*urand_pipe);
    329	*urand_pipe = NULL;
    330
    331	return exit_code;
    332}
    333
    334static void subtest_urandom_usdt(bool auto_attach)
    335{
    336	struct test_urandom_usdt *skel;
    337	struct test_urandom_usdt__bss *bss;
    338	struct bpf_link *l;
    339	FILE *urand_pipe = NULL;
    340	int err, urand_pid = 0;
    341
    342	skel = test_urandom_usdt__open_and_load();
    343	if (!ASSERT_OK_PTR(skel, "skel_open"))
    344		return;
    345
    346	urand_pipe = urand_spawn(&urand_pid);
    347	if (!ASSERT_OK_PTR(urand_pipe, "urand_spawn"))
    348		goto cleanup;
    349
    350	bss = skel->bss;
    351	bss->urand_pid = urand_pid;
    352
    353	if (auto_attach) {
    354		err = test_urandom_usdt__attach(skel);
    355		if (!ASSERT_OK(err, "skel_auto_attach"))
    356			goto cleanup;
    357	} else {
    358		l = bpf_program__attach_usdt(skel->progs.urand_read_without_sema,
    359					     urand_pid, "./urandom_read",
    360					     "urand", "read_without_sema", NULL);
    361		if (!ASSERT_OK_PTR(l, "urand_without_sema_attach"))
    362			goto cleanup;
    363		skel->links.urand_read_without_sema = l;
    364
    365		l = bpf_program__attach_usdt(skel->progs.urand_read_with_sema,
    366					     urand_pid, "./urandom_read",
    367					     "urand", "read_with_sema", NULL);
    368		if (!ASSERT_OK_PTR(l, "urand_with_sema_attach"))
    369			goto cleanup;
    370		skel->links.urand_read_with_sema = l;
    371
    372		l = bpf_program__attach_usdt(skel->progs.urandlib_read_without_sema,
    373					     urand_pid, "./liburandom_read.so",
    374					     "urandlib", "read_without_sema", NULL);
    375		if (!ASSERT_OK_PTR(l, "urandlib_without_sema_attach"))
    376			goto cleanup;
    377		skel->links.urandlib_read_without_sema = l;
    378
    379		l = bpf_program__attach_usdt(skel->progs.urandlib_read_with_sema,
    380					     urand_pid, "./liburandom_read.so",
    381					     "urandlib", "read_with_sema", NULL);
    382		if (!ASSERT_OK_PTR(l, "urandlib_with_sema_attach"))
    383			goto cleanup;
    384		skel->links.urandlib_read_with_sema = l;
    385
    386	}
    387
    388	/* trigger urandom_read USDTs */
    389	ASSERT_OK(urand_trigger(&urand_pipe), "urand_exit_code");
    390
    391	ASSERT_EQ(bss->urand_read_without_sema_call_cnt, 1, "urand_wo_sema_cnt");
    392	ASSERT_EQ(bss->urand_read_without_sema_buf_sz_sum, 256, "urand_wo_sema_sum");
    393
    394	ASSERT_EQ(bss->urand_read_with_sema_call_cnt, 1, "urand_w_sema_cnt");
    395	ASSERT_EQ(bss->urand_read_with_sema_buf_sz_sum, 256, "urand_w_sema_sum");
    396
    397	ASSERT_EQ(bss->urandlib_read_without_sema_call_cnt, 1, "urandlib_wo_sema_cnt");
    398	ASSERT_EQ(bss->urandlib_read_without_sema_buf_sz_sum, 256, "urandlib_wo_sema_sum");
    399
    400	ASSERT_EQ(bss->urandlib_read_with_sema_call_cnt, 1, "urandlib_w_sema_cnt");
    401	ASSERT_EQ(bss->urandlib_read_with_sema_buf_sz_sum, 256, "urandlib_w_sema_sum");
    402
    403cleanup:
    404	if (urand_pipe)
    405		pclose(urand_pipe);
    406	test_urandom_usdt__destroy(skel);
    407}
    408
    409void test_usdt(void)
    410{
    411	if (test__start_subtest("basic"))
    412		subtest_basic_usdt();
    413	if (test__start_subtest("multispec"))
    414		subtest_multispec_usdt();
    415	if (test__start_subtest("urand_auto_attach"))
    416		subtest_urandom_usdt(true /* auto_attach */);
    417	if (test__start_subtest("urand_pid_attach"))
    418		subtest_urandom_usdt(false /* auto_attach */);
    419}