1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2021 Facebook */
3 #define _GNU_SOURCE
4 #include <pthread.h>
5 #include <sched.h>
6 #include <sys/syscall.h>
7 #include <unistd.h>
8 #include <test_progs.h>
9 #include "test_bpf_cookie.skel.h"
10
kprobe_subtest(struct test_bpf_cookie * skel)11 static void kprobe_subtest(struct test_bpf_cookie *skel)
12 {
13 DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts);
14 struct bpf_link *link1 = NULL, *link2 = NULL;
15 struct bpf_link *retlink1 = NULL, *retlink2 = NULL;
16
17 /* attach two kprobes */
18 opts.bpf_cookie = 0x1;
19 opts.retprobe = false;
20 link1 = bpf_program__attach_kprobe_opts(skel->progs.handle_kprobe,
21 SYS_NANOSLEEP_KPROBE_NAME, &opts);
22 if (!ASSERT_OK_PTR(link1, "link1"))
23 goto cleanup;
24
25 opts.bpf_cookie = 0x2;
26 opts.retprobe = false;
27 link2 = bpf_program__attach_kprobe_opts(skel->progs.handle_kprobe,
28 SYS_NANOSLEEP_KPROBE_NAME, &opts);
29 if (!ASSERT_OK_PTR(link2, "link2"))
30 goto cleanup;
31
32 /* attach two kretprobes */
33 opts.bpf_cookie = 0x10;
34 opts.retprobe = true;
35 retlink1 = bpf_program__attach_kprobe_opts(skel->progs.handle_kretprobe,
36 SYS_NANOSLEEP_KPROBE_NAME, &opts);
37 if (!ASSERT_OK_PTR(retlink1, "retlink1"))
38 goto cleanup;
39
40 opts.bpf_cookie = 0x20;
41 opts.retprobe = true;
42 retlink2 = bpf_program__attach_kprobe_opts(skel->progs.handle_kretprobe,
43 SYS_NANOSLEEP_KPROBE_NAME, &opts);
44 if (!ASSERT_OK_PTR(retlink2, "retlink2"))
45 goto cleanup;
46
47 /* trigger kprobe && kretprobe */
48 usleep(1);
49
50 ASSERT_EQ(skel->bss->kprobe_res, 0x1 | 0x2, "kprobe_res");
51 ASSERT_EQ(skel->bss->kretprobe_res, 0x10 | 0x20, "kretprobe_res");
52
53 cleanup:
54 bpf_link__destroy(link1);
55 bpf_link__destroy(link2);
56 bpf_link__destroy(retlink1);
57 bpf_link__destroy(retlink2);
58 }
59
uprobe_subtest(struct test_bpf_cookie * skel)60 static void uprobe_subtest(struct test_bpf_cookie *skel)
61 {
62 DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
63 struct bpf_link *link1 = NULL, *link2 = NULL;
64 struct bpf_link *retlink1 = NULL, *retlink2 = NULL;
65 size_t uprobe_offset;
66 ssize_t base_addr;
67
68 base_addr = get_base_addr();
69 uprobe_offset = get_uprobe_offset(&get_base_addr, base_addr);
70
71 /* attach two uprobes */
72 opts.bpf_cookie = 0x100;
73 opts.retprobe = false;
74 link1 = bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe, 0 /* self pid */,
75 "/proc/self/exe", uprobe_offset, &opts);
76 if (!ASSERT_OK_PTR(link1, "link1"))
77 goto cleanup;
78
79 opts.bpf_cookie = 0x200;
80 opts.retprobe = false;
81 link2 = bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe, -1 /* any pid */,
82 "/proc/self/exe", uprobe_offset, &opts);
83 if (!ASSERT_OK_PTR(link2, "link2"))
84 goto cleanup;
85
86 /* attach two uretprobes */
87 opts.bpf_cookie = 0x1000;
88 opts.retprobe = true;
89 retlink1 = bpf_program__attach_uprobe_opts(skel->progs.handle_uretprobe, -1 /* any pid */,
90 "/proc/self/exe", uprobe_offset, &opts);
91 if (!ASSERT_OK_PTR(retlink1, "retlink1"))
92 goto cleanup;
93
94 opts.bpf_cookie = 0x2000;
95 opts.retprobe = true;
96 retlink2 = bpf_program__attach_uprobe_opts(skel->progs.handle_uretprobe, 0 /* self pid */,
97 "/proc/self/exe", uprobe_offset, &opts);
98 if (!ASSERT_OK_PTR(retlink2, "retlink2"))
99 goto cleanup;
100
101 /* trigger uprobe && uretprobe */
102 get_base_addr();
103
104 ASSERT_EQ(skel->bss->uprobe_res, 0x100 | 0x200, "uprobe_res");
105 ASSERT_EQ(skel->bss->uretprobe_res, 0x1000 | 0x2000, "uretprobe_res");
106
107 cleanup:
108 bpf_link__destroy(link1);
109 bpf_link__destroy(link2);
110 bpf_link__destroy(retlink1);
111 bpf_link__destroy(retlink2);
112 }
113
tp_subtest(struct test_bpf_cookie * skel)114 static void tp_subtest(struct test_bpf_cookie *skel)
115 {
116 DECLARE_LIBBPF_OPTS(bpf_tracepoint_opts, opts);
117 struct bpf_link *link1 = NULL, *link2 = NULL, *link3 = NULL;
118
119 /* attach first tp prog */
120 opts.bpf_cookie = 0x10000;
121 link1 = bpf_program__attach_tracepoint_opts(skel->progs.handle_tp1,
122 "syscalls", "sys_enter_nanosleep", &opts);
123 if (!ASSERT_OK_PTR(link1, "link1"))
124 goto cleanup;
125
126 /* attach second tp prog */
127 opts.bpf_cookie = 0x20000;
128 link2 = bpf_program__attach_tracepoint_opts(skel->progs.handle_tp2,
129 "syscalls", "sys_enter_nanosleep", &opts);
130 if (!ASSERT_OK_PTR(link2, "link2"))
131 goto cleanup;
132
133 /* trigger tracepoints */
134 usleep(1);
135
136 ASSERT_EQ(skel->bss->tp_res, 0x10000 | 0x20000, "tp_res1");
137
138 /* now we detach first prog and will attach third one, which causes
139 * two internal calls to bpf_prog_array_copy(), shuffling
140 * bpf_prog_array_items around. We test here that we don't lose track
141 * of associated bpf_cookies.
142 */
143 bpf_link__destroy(link1);
144 link1 = NULL;
145 kern_sync_rcu();
146 skel->bss->tp_res = 0;
147
148 /* attach third tp prog */
149 opts.bpf_cookie = 0x40000;
150 link3 = bpf_program__attach_tracepoint_opts(skel->progs.handle_tp3,
151 "syscalls", "sys_enter_nanosleep", &opts);
152 if (!ASSERT_OK_PTR(link3, "link3"))
153 goto cleanup;
154
155 /* trigger tracepoints */
156 usleep(1);
157
158 ASSERT_EQ(skel->bss->tp_res, 0x20000 | 0x40000, "tp_res2");
159
160 cleanup:
161 bpf_link__destroy(link1);
162 bpf_link__destroy(link2);
163 bpf_link__destroy(link3);
164 }
165
burn_cpu(void)166 static void burn_cpu(void)
167 {
168 volatile int j = 0;
169 cpu_set_t cpu_set;
170 int i, err;
171
172 /* generate some branches on cpu 0 */
173 CPU_ZERO(&cpu_set);
174 CPU_SET(0, &cpu_set);
175 err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
176 ASSERT_OK(err, "set_thread_affinity");
177
178 /* spin the loop for a while (random high number) */
179 for (i = 0; i < 1000000; ++i)
180 ++j;
181 }
182
pe_subtest(struct test_bpf_cookie * skel)183 static void pe_subtest(struct test_bpf_cookie *skel)
184 {
185 DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, opts);
186 struct bpf_link *link = NULL;
187 struct perf_event_attr attr;
188 int pfd = -1;
189
190 /* create perf event */
191 memset(&attr, 0, sizeof(attr));
192 attr.size = sizeof(attr);
193 attr.type = PERF_TYPE_SOFTWARE;
194 attr.config = PERF_COUNT_SW_CPU_CLOCK;
195 attr.freq = 1;
196 attr.sample_freq = 4000;
197 pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);
198 if (!ASSERT_GE(pfd, 0, "perf_fd"))
199 goto cleanup;
200
201 opts.bpf_cookie = 0x100000;
202 link = bpf_program__attach_perf_event_opts(skel->progs.handle_pe, pfd, &opts);
203 if (!ASSERT_OK_PTR(link, "link1"))
204 goto cleanup;
205
206 burn_cpu(); /* trigger BPF prog */
207
208 ASSERT_EQ(skel->bss->pe_res, 0x100000, "pe_res1");
209
210 /* prevent bpf_link__destroy() closing pfd itself */
211 bpf_link__disconnect(link);
212 /* close BPF link's FD explicitly */
213 close(bpf_link__fd(link));
214 /* free up memory used by struct bpf_link */
215 bpf_link__destroy(link);
216 link = NULL;
217 kern_sync_rcu();
218 skel->bss->pe_res = 0;
219
220 opts.bpf_cookie = 0x200000;
221 link = bpf_program__attach_perf_event_opts(skel->progs.handle_pe, pfd, &opts);
222 if (!ASSERT_OK_PTR(link, "link2"))
223 goto cleanup;
224
225 burn_cpu(); /* trigger BPF prog */
226
227 ASSERT_EQ(skel->bss->pe_res, 0x200000, "pe_res2");
228
229 cleanup:
230 close(pfd);
231 bpf_link__destroy(link);
232 }
233
test_bpf_cookie(void)234 void test_bpf_cookie(void)
235 {
236 struct test_bpf_cookie *skel;
237
238 skel = test_bpf_cookie__open_and_load();
239 if (!ASSERT_OK_PTR(skel, "skel_open"))
240 return;
241
242 skel->bss->my_tid = syscall(SYS_gettid);
243
244 if (test__start_subtest("kprobe"))
245 kprobe_subtest(skel);
246 if (test__start_subtest("uprobe"))
247 uprobe_subtest(skel);
248 if (test__start_subtest("tracepoint"))
249 tp_subtest(skel);
250 if (test__start_subtest("perf_event"))
251 pe_subtest(skel);
252
253 test_bpf_cookie__destroy(skel);
254 }
255