Skip to content

Commit 00a0fa2

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
selftests/bpf: Add urandom_read shared lib and USDTs
Extend urandom_read helper binary to include USDTs of 4 combinations: semaphore/semaphoreless (refcounted and non-refcounted) and based in executable or shared library. We also extend urandom_read with ability to report it's own PID to parent process and wait for parent process to ready itself up for tracing urandom_read. We utilize popen() and underlying pipe properties for proper signaling. Once urandom_read is ready, we add few tests to validate that libbpf's USDT attachment handles all the above combinations of semaphore (or lack of it) and static or shared library USDTs. Also, we validate that libbpf handles shared libraries both with PID filter and without one (i.e., -1 for PID argument). Having the shared library case tested with and without PID is important because internal logic differs on kernels that don't support BPF cookies. On such older kernels, attaching to USDTs in shared libraries without specifying concrete PID doesn't work in principle, because it's impossible to determine shared library's load address to derive absolute IPs for uprobe attachments. Without absolute IPs, it's impossible to perform correct look up of USDT spec based on uprobe's absolute IP (the only kind available from BPF at runtime). This is not the problem on newer kernels with BPF cookie as we don't need IP-to-ID lookup because BPF cookie value *is* spec ID. So having those two situations as separate subtests is good because libbpf CI is able to test latest selftests against old kernels (e.g., 4.9 and 5.5), so we'll be able to disable PID-less shared lib attachment for old kernels, but will still leave PID-specific one enabled to validate this legacy logic is working correctly. Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Dave Marchevsky <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 630301b commit 00a0fa2

File tree

8 files changed

+275
-9
lines changed

8 files changed

+275
-9
lines changed

tools/testing/selftests/bpf/Makefile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,15 @@ $(OUTPUT)/%:%.c
168168
$(call msg,BINARY,,$@)
169169
$(Q)$(LINK.c) $^ $(LDLIBS) -o $@
170170

171-
$(OUTPUT)/urandom_read: urandom_read.c
171+
$(OUTPUT)/liburandom_read.so: urandom_read_lib1.c urandom_read_lib2.c
172+
$(call msg,LIB,,$@)
173+
$(Q)$(CC) $(CFLAGS) -fPIC $(LDFLAGS) $^ $(LDLIBS) --shared -o $@
174+
175+
$(OUTPUT)/urandom_read: urandom_read.c urandom_read_aux.c $(OUTPUT)/liburandom_read.so
172176
$(call msg,BINARY,,$@)
173-
$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -Wl,--build-id=sha1 -o $@
177+
$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.c,$^) \
178+
liburandom_read.so $(LDLIBS) \
179+
-Wl,-rpath=. -Wl,--build-id=sha1 -o $@
174180

175181
$(OUTPUT)/bpf_testmod.ko: $(VMLINUX_BTF) $(wildcard bpf_testmod/Makefile bpf_testmod/*.[ch])
176182
$(call msg,MOD,,$@)
@@ -493,6 +499,7 @@ TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c \
493499
btf_helpers.c flow_dissector_load.h \
494500
cap_helpers.c
495501
TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \
502+
$(OUTPUT)/liburandom_read.so \
496503
ima_setup.sh \
497504
$(wildcard progs/btf_dump_test_case_*.c)
498505
TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE

tools/testing/selftests/bpf/prog_tests/usdt.c

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "../sdt.h"
77

88
#include "test_usdt.skel.h"
9+
#include "test_urandom_usdt.skel.h"
910

1011
int lets_test_this(int);
1112

@@ -304,10 +305,117 @@ static void subtest_multispec_usdt(void)
304305
test_usdt__destroy(skel);
305306
}
306307

308+
static FILE *urand_spawn(int *pid)
309+
{
310+
FILE *f;
311+
312+
/* urandom_read's stdout is wired into f */
313+
f = popen("./urandom_read 1 report-pid", "r");
314+
if (!f)
315+
return NULL;
316+
317+
if (fscanf(f, "%d", pid) != 1) {
318+
pclose(f);
319+
return NULL;
320+
}
321+
322+
return f;
323+
}
324+
325+
static int urand_trigger(FILE **urand_pipe)
326+
{
327+
int exit_code;
328+
329+
/* pclose() waits for child process to exit and returns their exit code */
330+
exit_code = pclose(*urand_pipe);
331+
*urand_pipe = NULL;
332+
333+
return exit_code;
334+
}
335+
336+
static void subtest_urandom_usdt(bool auto_attach)
337+
{
338+
struct test_urandom_usdt *skel;
339+
struct test_urandom_usdt__bss *bss;
340+
struct bpf_link *l;
341+
FILE *urand_pipe = NULL;
342+
int err, urand_pid = 0;
343+
344+
skel = test_urandom_usdt__open_and_load();
345+
if (!ASSERT_OK_PTR(skel, "skel_open"))
346+
return;
347+
348+
urand_pipe = urand_spawn(&urand_pid);
349+
if (!ASSERT_OK_PTR(urand_pipe, "urand_spawn"))
350+
goto cleanup;
351+
352+
bss = skel->bss;
353+
bss->urand_pid = urand_pid;
354+
355+
if (auto_attach) {
356+
err = test_urandom_usdt__attach(skel);
357+
if (!ASSERT_OK(err, "skel_auto_attach"))
358+
goto cleanup;
359+
} else {
360+
l = bpf_program__attach_usdt(skel->progs.urand_read_without_sema,
361+
urand_pid, "./urandom_read",
362+
"urand", "read_without_sema", NULL);
363+
if (!ASSERT_OK_PTR(l, "urand_without_sema_attach"))
364+
goto cleanup;
365+
skel->links.urand_read_without_sema = l;
366+
367+
l = bpf_program__attach_usdt(skel->progs.urand_read_with_sema,
368+
urand_pid, "./urandom_read",
369+
"urand", "read_with_sema", NULL);
370+
if (!ASSERT_OK_PTR(l, "urand_with_sema_attach"))
371+
goto cleanup;
372+
skel->links.urand_read_with_sema = l;
373+
374+
l = bpf_program__attach_usdt(skel->progs.urandlib_read_without_sema,
375+
urand_pid, "./liburandom_read.so",
376+
"urandlib", "read_without_sema", NULL);
377+
if (!ASSERT_OK_PTR(l, "urandlib_without_sema_attach"))
378+
goto cleanup;
379+
skel->links.urandlib_read_without_sema = l;
380+
381+
l = bpf_program__attach_usdt(skel->progs.urandlib_read_with_sema,
382+
urand_pid, "./liburandom_read.so",
383+
"urandlib", "read_with_sema", NULL);
384+
if (!ASSERT_OK_PTR(l, "urandlib_with_sema_attach"))
385+
goto cleanup;
386+
skel->links.urandlib_read_with_sema = l;
387+
388+
}
389+
390+
/* trigger urandom_read USDTs */
391+
ASSERT_OK(urand_trigger(&urand_pipe), "urand_exit_code");
392+
393+
ASSERT_EQ(bss->urand_read_without_sema_call_cnt, 1, "urand_wo_sema_cnt");
394+
ASSERT_EQ(bss->urand_read_without_sema_buf_sz_sum, 256, "urand_wo_sema_sum");
395+
396+
ASSERT_EQ(bss->urand_read_with_sema_call_cnt, 1, "urand_w_sema_cnt");
397+
ASSERT_EQ(bss->urand_read_with_sema_buf_sz_sum, 256, "urand_w_sema_sum");
398+
399+
ASSERT_EQ(bss->urandlib_read_without_sema_call_cnt, 1, "urandlib_wo_sema_cnt");
400+
ASSERT_EQ(bss->urandlib_read_without_sema_buf_sz_sum, 256, "urandlib_wo_sema_sum");
401+
402+
ASSERT_EQ(bss->urandlib_read_with_sema_call_cnt, 1, "urandlib_w_sema_cnt");
403+
ASSERT_EQ(bss->urandlib_read_with_sema_buf_sz_sum, 256, "urandlib_w_sema_sum");
404+
405+
cleanup:
406+
if (urand_pipe)
407+
pclose(urand_pipe);
408+
test_urandom_usdt__destroy(skel);
409+
}
410+
307411
void test_usdt(void)
308412
{
309413
if (test__start_subtest("basic"))
310414
subtest_basic_usdt();
311415
if (test__start_subtest("multispec"))
312416
subtest_multispec_usdt();
417+
if (test__start_subtest("urand_auto_attach"))
418+
subtest_urandom_usdt(true /* auto_attach */);
419+
if (test__start_subtest("urand_pid_attach"))
420+
subtest_urandom_usdt(false /* auto_attach */);
313421
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
3+
4+
#include "vmlinux.h"
5+
#include <bpf/bpf_helpers.h>
6+
#include <bpf/usdt.bpf.h>
7+
8+
int urand_pid;
9+
10+
int urand_read_without_sema_call_cnt;
11+
int urand_read_without_sema_buf_sz_sum;
12+
13+
SEC("usdt/./urandom_read:urand:read_without_sema")
14+
int BPF_USDT(urand_read_without_sema, int iter_num, int iter_cnt, int buf_sz)
15+
{
16+
if (urand_pid != (bpf_get_current_pid_tgid() >> 32))
17+
return 0;
18+
19+
__sync_fetch_and_add(&urand_read_without_sema_call_cnt, 1);
20+
__sync_fetch_and_add(&urand_read_without_sema_buf_sz_sum, buf_sz);
21+
22+
return 0;
23+
}
24+
25+
int urand_read_with_sema_call_cnt;
26+
int urand_read_with_sema_buf_sz_sum;
27+
28+
SEC("usdt/./urandom_read:urand:read_with_sema")
29+
int BPF_USDT(urand_read_with_sema, int iter_num, int iter_cnt, int buf_sz)
30+
{
31+
if (urand_pid != (bpf_get_current_pid_tgid() >> 32))
32+
return 0;
33+
34+
__sync_fetch_and_add(&urand_read_with_sema_call_cnt, 1);
35+
__sync_fetch_and_add(&urand_read_with_sema_buf_sz_sum, buf_sz);
36+
37+
return 0;
38+
}
39+
40+
int urandlib_read_without_sema_call_cnt;
41+
int urandlib_read_without_sema_buf_sz_sum;
42+
43+
SEC("usdt/./liburandom_read.so:urandlib:read_without_sema")
44+
int BPF_USDT(urandlib_read_without_sema, int iter_num, int iter_cnt, int buf_sz)
45+
{
46+
if (urand_pid != (bpf_get_current_pid_tgid() >> 32))
47+
return 0;
48+
49+
__sync_fetch_and_add(&urandlib_read_without_sema_call_cnt, 1);
50+
__sync_fetch_and_add(&urandlib_read_without_sema_buf_sz_sum, buf_sz);
51+
52+
return 0;
53+
}
54+
55+
int urandlib_read_with_sema_call_cnt;
56+
int urandlib_read_with_sema_buf_sz_sum;
57+
58+
SEC("usdt/./liburandom_read.so:urandlib:read_with_sema")
59+
int BPF_USDT(urandlib_read_with_sema, int iter_num, int iter_cnt, int buf_sz)
60+
{
61+
if (urand_pid != (bpf_get_current_pid_tgid() >> 32))
62+
return 0;
63+
64+
__sync_fetch_and_add(&urandlib_read_with_sema_call_cnt, 1);
65+
__sync_fetch_and_add(&urandlib_read_with_sema_buf_sz_sum, buf_sz);
66+
67+
return 0;
68+
}
69+
70+
char _license[] SEC("license") = "GPL";

tools/testing/selftests/bpf/progs/test_usdt_multispec.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ int BPF_USDT(usdt_100, int x)
2626
__sync_fetch_and_add(&usdt_100_called, 1);
2727
__sync_fetch_and_add(&usdt_100_sum, x);
2828

29-
bpf_printk("X is %d, sum is %d", x, usdt_100_sum);
30-
3129
return 0;
3230
}
3331

tools/testing/selftests/bpf/urandom_read.c

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,85 @@
1+
#include <stdbool.h>
12
#include <stdio.h>
23
#include <unistd.h>
4+
#include <errno.h>
35
#include <sys/types.h>
46
#include <sys/stat.h>
57
#include <fcntl.h>
68
#include <stdlib.h>
9+
#include <signal.h>
10+
11+
#define _SDT_HAS_SEMAPHORES 1
12+
#include "sdt.h"
13+
14+
#define SEC(name) __attribute__((section(name), used))
715

816
#define BUF_SIZE 256
917

18+
/* defined in urandom_read_aux.c */
19+
void urand_read_without_sema(int iter_num, int iter_cnt, int read_sz);
20+
/* these are coming from urandom_read_lib{1,2}.c */
21+
void urandlib_read_with_sema(int iter_num, int iter_cnt, int read_sz);
22+
void urandlib_read_without_sema(int iter_num, int iter_cnt, int read_sz);
23+
24+
unsigned short urand_read_with_sema_semaphore SEC(".probes");
25+
1026
static __attribute__((noinline))
1127
void urandom_read(int fd, int count)
1228
{
13-
char buf[BUF_SIZE];
14-
int i;
29+
char buf[BUF_SIZE];
30+
int i;
31+
32+
for (i = 0; i < count; ++i) {
33+
read(fd, buf, BUF_SIZE);
34+
35+
/* trigger USDTs defined in executable itself */
36+
urand_read_without_sema(i, count, BUF_SIZE);
37+
STAP_PROBE3(urand, read_with_sema, i, count, BUF_SIZE);
1538

16-
for (i = 0; i < count; ++i)
17-
read(fd, buf, BUF_SIZE);
39+
/* trigger USDTs defined in shared lib */
40+
urandlib_read_without_sema(i, count, BUF_SIZE);
41+
urandlib_read_with_sema(i, count, BUF_SIZE);
42+
}
43+
}
44+
45+
static volatile bool parent_ready;
46+
47+
static void handle_sigpipe(int sig)
48+
{
49+
parent_ready = true;
1850
}
1951

2052
int main(int argc, char *argv[])
2153
{
2254
int fd = open("/dev/urandom", O_RDONLY);
2355
int count = 4;
56+
bool report_pid = false;
2457

2558
if (fd < 0)
2659
return 1;
2760

28-
if (argc == 2)
61+
if (argc >= 2)
2962
count = atoi(argv[1]);
63+
if (argc >= 3) {
64+
report_pid = true;
65+
/* install SIGPIPE handler to catch when parent closes their
66+
* end of the pipe (on the other side of our stdout)
67+
*/
68+
signal(SIGPIPE, handle_sigpipe);
69+
}
70+
71+
/* report PID and wait for parent process to send us "signal" by
72+
* closing stdout
73+
*/
74+
if (report_pid) {
75+
while (!parent_ready) {
76+
fprintf(stdout, "%d\n", getpid());
77+
fflush(stdout);
78+
}
79+
/* at this point stdout is closed, parent process knows our
80+
* PID and is ready to trace us
81+
*/
82+
}
3083

3184
urandom_read(fd, count);
3285

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
3+
#include "sdt.h"
4+
5+
void urand_read_without_sema(int iter_num, int iter_cnt, int read_sz)
6+
{
7+
/* semaphore-less USDT */
8+
STAP_PROBE3(urand, read_without_sema, iter_num, iter_cnt, read_sz);
9+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
3+
#define _SDT_HAS_SEMAPHORES 1
4+
#include "sdt.h"
5+
6+
#define SEC(name) __attribute__((section(name), used))
7+
8+
unsigned short urandlib_read_with_sema_semaphore SEC(".probes");
9+
10+
void urandlib_read_with_sema(int iter_num, int iter_cnt, int read_sz)
11+
{
12+
STAP_PROBE3(urandlib, read_with_sema, iter_num, iter_cnt, read_sz);
13+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
3+
#include "sdt.h"
4+
5+
void urandlib_read_without_sema(int iter_num, int iter_cnt, int read_sz)
6+
{
7+
STAP_PROBE3(urandlib, read_without_sema, iter_num, iter_cnt, read_sz);
8+
}

0 commit comments

Comments
 (0)