xref: /linux/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c (revision 3229675be841932879d5f1b2fb38ba9c2777a088)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <unistd.h>
4 #include <pthread.h>
5 #include <fcntl.h>
6 #include <test_progs.h>
7 #include "uprobe_multi.skel.h"
8 #include "uprobe_multi_bench.skel.h"
9 #include "uprobe_multi_usdt.skel.h"
10 #include "uprobe_multi_consumers.skel.h"
11 #include "uprobe_multi_pid_filter.skel.h"
12 #include "uprobe_multi_session.skel.h"
13 #include "uprobe_multi_session_single.skel.h"
14 #include "uprobe_multi_session_cookie.skel.h"
15 #include "uprobe_multi_session_recursive.skel.h"
16 #include "uprobe_multi_verifier.skel.h"
17 #include "bpf/libbpf_internal.h"
18 #include "testing_helpers.h"
19 #include "../sdt.h"
20 
21 static char test_data[] = "test_data";
22 
23 noinline void uprobe_multi_func_1(void)
24 {
25 	asm volatile ("");
26 }
27 
28 noinline void uprobe_multi_func_2(void)
29 {
30 	asm volatile ("");
31 }
32 
33 noinline void uprobe_multi_func_3(void)
34 {
35 	asm volatile ("");
36 }
37 
38 noinline void usdt_trigger(void)
39 {
40 	STAP_PROBE(test, pid_filter_usdt);
41 }
42 
43 noinline void uprobe_session_recursive(int i)
44 {
45 	if (i)
46 		uprobe_session_recursive(i - 1);
47 }
48 
49 struct child {
50 	int go[2];
51 	int c2p[2]; /* child -> parent channel */
52 	int pid;
53 	int tid;
54 	pthread_t thread;
55 	char stack[65536];
56 };
57 
58 static void release_child(struct child *child)
59 {
60 	int child_status;
61 
62 	if (!child)
63 		return;
64 	close(child->go[1]);
65 	close(child->go[0]);
66 	if (child->thread) {
67 		pthread_join(child->thread, NULL);
68 		child->thread = 0;
69 	}
70 	close(child->c2p[0]);
71 	close(child->c2p[1]);
72 	if (child->pid > 0)
73 		waitpid(child->pid, &child_status, 0);
74 }
75 
76 static void kick_child(struct child *child)
77 {
78 	char c = 1;
79 
80 	if (child) {
81 		write(child->go[1], &c, 1);
82 		release_child(child);
83 	}
84 	fflush(NULL);
85 }
86 
87 static int child_func(void *arg)
88 {
89 	struct child *child = arg;
90 	int err, c;
91 
92 	close(child->go[1]);
93 
94 	/* wait for parent's kick */
95 	err = read(child->go[0], &c, 1);
96 	if (err != 1)
97 		exit(err);
98 
99 	uprobe_multi_func_1();
100 	uprobe_multi_func_2();
101 	uprobe_multi_func_3();
102 	usdt_trigger();
103 
104 	exit(errno);
105 }
106 
107 static int spawn_child_flag(struct child *child, bool clone_vm)
108 {
109 	/* pipe to notify child to execute the trigger functions */
110 	if (pipe(child->go))
111 		return -1;
112 
113 	if (clone_vm) {
114 		child->pid = child->tid = clone(child_func, child->stack + sizeof(child->stack)/2,
115 						CLONE_VM|SIGCHLD, child);
116 	} else {
117 		child->pid = child->tid = fork();
118 	}
119 	if (child->pid < 0) {
120 		release_child(child);
121 		errno = EINVAL;
122 		return -1;
123 	}
124 
125 	/* fork-ed child */
126 	if (!clone_vm && child->pid == 0)
127 		child_func(child);
128 
129 	return 0;
130 }
131 
132 static int spawn_child(struct child *child)
133 {
134 	return spawn_child_flag(child, false);
135 }
136 
137 static void *child_thread(void *ctx)
138 {
139 	struct child *child = ctx;
140 	int c = 0, err;
141 
142 	child->tid = sys_gettid();
143 
144 	/* let parent know we are ready */
145 	err = write(child->c2p[1], &c, 1);
146 	if (err != 1)
147 		pthread_exit(&err);
148 
149 	/* wait for parent's kick */
150 	err = read(child->go[0], &c, 1);
151 	if (err != 1)
152 		pthread_exit(&err);
153 
154 	uprobe_multi_func_1();
155 	uprobe_multi_func_2();
156 	uprobe_multi_func_3();
157 	usdt_trigger();
158 
159 	err = 0;
160 	pthread_exit(&err);
161 }
162 
163 static int spawn_thread(struct child *child)
164 {
165 	int c, err;
166 
167 	/* pipe to notify child to execute the trigger functions */
168 	if (pipe(child->go))
169 		return -1;
170 	/* pipe to notify parent that child thread is ready */
171 	if (pipe(child->c2p)) {
172 		close(child->go[0]);
173 		close(child->go[1]);
174 		return -1;
175 	}
176 
177 	child->pid = getpid();
178 
179 	err = pthread_create(&child->thread, NULL, child_thread, child);
180 	if (err) {
181 		err = -errno;
182 		close(child->go[0]);
183 		close(child->go[1]);
184 		close(child->c2p[0]);
185 		close(child->c2p[1]);
186 		errno = -err;
187 		return -1;
188 	}
189 
190 	err = read(child->c2p[0], &c, 1);
191 	if (!ASSERT_EQ(err, 1, "child_thread_ready"))
192 		return -1;
193 
194 	return 0;
195 }
196 
197 static void uprobe_multi_test_run(struct uprobe_multi *skel, struct child *child)
198 {
199 	skel->bss->uprobe_multi_func_1_addr = (__u64) uprobe_multi_func_1;
200 	skel->bss->uprobe_multi_func_2_addr = (__u64) uprobe_multi_func_2;
201 	skel->bss->uprobe_multi_func_3_addr = (__u64) uprobe_multi_func_3;
202 
203 	skel->bss->user_ptr = test_data;
204 
205 	/*
206 	 * Disable pid check in bpf program if we are pid filter test,
207 	 * because the probe should be executed only by child->pid
208 	 * passed at the probe attach.
209 	 */
210 	skel->bss->pid = child ? 0 : getpid();
211 	skel->bss->expect_pid = child ? child->pid : 0;
212 
213 	/* trigger all probes, if we are testing child *process*, just to make
214 	 * sure that PID filtering doesn't let through activations from wrong
215 	 * PIDs; when we test child *thread*, we don't want to do this to
216 	 * avoid double counting number of triggering events
217 	 */
218 	if (!child || !child->thread) {
219 		uprobe_multi_func_1();
220 		uprobe_multi_func_2();
221 		uprobe_multi_func_3();
222 		usdt_trigger();
223 	}
224 
225 	if (child)
226 		kick_child(child);
227 
228 	/*
229 	 * There are 2 entry and 2 exit probe called for each uprobe_multi_func_[123]
230 	 * function and each sleepable probe (6) increments uprobe_multi_sleep_result.
231 	 */
232 	ASSERT_EQ(skel->bss->uprobe_multi_func_1_result, 2, "uprobe_multi_func_1_result");
233 	ASSERT_EQ(skel->bss->uprobe_multi_func_2_result, 2, "uprobe_multi_func_2_result");
234 	ASSERT_EQ(skel->bss->uprobe_multi_func_3_result, 2, "uprobe_multi_func_3_result");
235 
236 	ASSERT_EQ(skel->bss->uretprobe_multi_func_1_result, 2, "uretprobe_multi_func_1_result");
237 	ASSERT_EQ(skel->bss->uretprobe_multi_func_2_result, 2, "uretprobe_multi_func_2_result");
238 	ASSERT_EQ(skel->bss->uretprobe_multi_func_3_result, 2, "uretprobe_multi_func_3_result");
239 
240 	ASSERT_EQ(skel->bss->uprobe_multi_sleep_result, 6, "uprobe_multi_sleep_result");
241 
242 	ASSERT_FALSE(skel->bss->bad_pid_seen, "bad_pid_seen");
243 
244 	if (child) {
245 		ASSERT_EQ(skel->bss->child_pid, child->pid, "uprobe_multi_child_pid");
246 		ASSERT_EQ(skel->bss->child_tid, child->tid, "uprobe_multi_child_tid");
247 	}
248 }
249 
250 static void test_skel_api(void)
251 {
252 	struct uprobe_multi *skel = NULL;
253 	int err;
254 
255 	skel = uprobe_multi__open_and_load();
256 	if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
257 		goto cleanup;
258 
259 	err = uprobe_multi__attach(skel);
260 	if (!ASSERT_OK(err, "uprobe_multi__attach"))
261 		goto cleanup;
262 
263 	uprobe_multi_test_run(skel, NULL);
264 
265 cleanup:
266 	uprobe_multi__destroy(skel);
267 }
268 
269 static void
270 __test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi_opts *opts,
271 		  struct child *child)
272 {
273 	pid_t pid = child ? child->pid : -1;
274 	struct uprobe_multi *skel = NULL;
275 
276 	skel = uprobe_multi__open_and_load();
277 	if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
278 		goto cleanup;
279 
280 	opts->retprobe = false;
281 	skel->links.uprobe = bpf_program__attach_uprobe_multi(skel->progs.uprobe, pid,
282 							      binary, pattern, opts);
283 	if (!ASSERT_OK_PTR(skel->links.uprobe, "bpf_program__attach_uprobe_multi"))
284 		goto cleanup;
285 
286 	opts->retprobe = true;
287 	skel->links.uretprobe = bpf_program__attach_uprobe_multi(skel->progs.uretprobe, pid,
288 								 binary, pattern, opts);
289 	if (!ASSERT_OK_PTR(skel->links.uretprobe, "bpf_program__attach_uprobe_multi"))
290 		goto cleanup;
291 
292 	opts->retprobe = false;
293 	skel->links.uprobe_sleep = bpf_program__attach_uprobe_multi(skel->progs.uprobe_sleep, pid,
294 								    binary, pattern, opts);
295 	if (!ASSERT_OK_PTR(skel->links.uprobe_sleep, "bpf_program__attach_uprobe_multi"))
296 		goto cleanup;
297 
298 	opts->retprobe = true;
299 	skel->links.uretprobe_sleep = bpf_program__attach_uprobe_multi(skel->progs.uretprobe_sleep,
300 								       pid, binary, pattern, opts);
301 	if (!ASSERT_OK_PTR(skel->links.uretprobe_sleep, "bpf_program__attach_uprobe_multi"))
302 		goto cleanup;
303 
304 	opts->retprobe = false;
305 	skel->links.uprobe_extra = bpf_program__attach_uprobe_multi(skel->progs.uprobe_extra, -1,
306 								    binary, pattern, opts);
307 	if (!ASSERT_OK_PTR(skel->links.uprobe_extra, "bpf_program__attach_uprobe_multi"))
308 		goto cleanup;
309 
310 	/* Attach (uprobe-backed) USDTs */
311 	skel->links.usdt_pid = bpf_program__attach_usdt(skel->progs.usdt_pid, pid, binary,
312 							"test", "pid_filter_usdt", NULL);
313 	if (!ASSERT_OK_PTR(skel->links.usdt_pid, "attach_usdt_pid"))
314 		goto cleanup;
315 
316 	skel->links.usdt_extra = bpf_program__attach_usdt(skel->progs.usdt_extra, -1, binary,
317 							  "test", "pid_filter_usdt", NULL);
318 	if (!ASSERT_OK_PTR(skel->links.usdt_extra, "attach_usdt_extra"))
319 		goto cleanup;
320 
321 	uprobe_multi_test_run(skel, child);
322 
323 	ASSERT_FALSE(skel->bss->bad_pid_seen_usdt, "bad_pid_seen_usdt");
324 	if (child) {
325 		ASSERT_EQ(skel->bss->child_pid_usdt, child->pid, "usdt_multi_child_pid");
326 		ASSERT_EQ(skel->bss->child_tid_usdt, child->tid, "usdt_multi_child_tid");
327 	}
328 cleanup:
329 	uprobe_multi__destroy(skel);
330 }
331 
332 static void
333 test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi_opts *opts)
334 {
335 	static struct child child;
336 
337 	memset(&child, 0, sizeof(child));
338 
339 	/* no pid filter */
340 	__test_attach_api(binary, pattern, opts, NULL);
341 
342 	/* pid filter */
343 	if (!ASSERT_OK(spawn_child(&child), "spawn_child"))
344 		return;
345 
346 	__test_attach_api(binary, pattern, opts, &child);
347 
348 	/* pid filter (thread) */
349 	if (!ASSERT_OK(spawn_thread(&child), "spawn_thread"))
350 		return;
351 
352 	__test_attach_api(binary, pattern, opts, &child);
353 }
354 
355 static void test_attach_api_pattern(void)
356 {
357 	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
358 
359 	test_attach_api("/proc/self/exe", "uprobe_multi_func_*", &opts);
360 	test_attach_api("/proc/self/exe", "uprobe_multi_func_?", &opts);
361 }
362 
363 static void test_attach_api_syms(void)
364 {
365 	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
366 	const char *syms[3] = {
367 		"uprobe_multi_func_1",
368 		"uprobe_multi_func_2",
369 		"uprobe_multi_func_3",
370 	};
371 
372 	opts.syms = syms;
373 	opts.cnt = ARRAY_SIZE(syms);
374 	test_attach_api("/proc/self/exe", NULL, &opts);
375 }
376 
377 static void test_attach_api_fails(void)
378 {
379 	LIBBPF_OPTS(bpf_link_create_opts, opts);
380 	const char *path = "/proc/self/exe";
381 	struct uprobe_multi *skel = NULL;
382 	int prog_fd, link_fd = -1;
383 	unsigned long offset = 0;
384 
385 	skel = uprobe_multi__open_and_load();
386 	if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
387 		goto cleanup;
388 
389 	prog_fd = bpf_program__fd(skel->progs.uprobe_extra);
390 
391 	/* abnormal cnt */
392 	opts.uprobe_multi.path = path;
393 	opts.uprobe_multi.offsets = &offset;
394 	opts.uprobe_multi.cnt = INT_MAX;
395 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
396 	if (!ASSERT_ERR(link_fd, "link_fd"))
397 		goto cleanup;
398 	if (!ASSERT_EQ(link_fd, -E2BIG, "big cnt"))
399 		goto cleanup;
400 
401 	/* cnt is 0 */
402 	LIBBPF_OPTS_RESET(opts,
403 		.uprobe_multi.path = path,
404 		.uprobe_multi.offsets = (unsigned long *) &offset,
405 	);
406 
407 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
408 	if (!ASSERT_ERR(link_fd, "link_fd"))
409 		goto cleanup;
410 	if (!ASSERT_EQ(link_fd, -EINVAL, "cnt_is_zero"))
411 		goto cleanup;
412 
413 	/* negative offset */
414 	offset = -1;
415 	opts.uprobe_multi.path = path;
416 	opts.uprobe_multi.offsets = (unsigned long *) &offset;
417 	opts.uprobe_multi.cnt = 1;
418 
419 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
420 	if (!ASSERT_ERR(link_fd, "link_fd"))
421 		goto cleanup;
422 	if (!ASSERT_EQ(link_fd, -EINVAL, "offset_is_negative"))
423 		goto cleanup;
424 
425 	/* offsets is NULL */
426 	LIBBPF_OPTS_RESET(opts,
427 		.uprobe_multi.path = path,
428 		.uprobe_multi.cnt = 1,
429 	);
430 
431 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
432 	if (!ASSERT_ERR(link_fd, "link_fd"))
433 		goto cleanup;
434 	if (!ASSERT_EQ(link_fd, -EINVAL, "offsets_is_null"))
435 		goto cleanup;
436 
437 	/* wrong offsets pointer */
438 	LIBBPF_OPTS_RESET(opts,
439 		.uprobe_multi.path = path,
440 		.uprobe_multi.offsets = (unsigned long *) 1,
441 		.uprobe_multi.cnt = 1,
442 	);
443 
444 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
445 	if (!ASSERT_ERR(link_fd, "link_fd"))
446 		goto cleanup;
447 	if (!ASSERT_EQ(link_fd, -EFAULT, "offsets_is_wrong"))
448 		goto cleanup;
449 
450 	/* path is NULL */
451 	offset = 1;
452 	LIBBPF_OPTS_RESET(opts,
453 		.uprobe_multi.offsets = (unsigned long *) &offset,
454 		.uprobe_multi.cnt = 1,
455 	);
456 
457 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
458 	if (!ASSERT_ERR(link_fd, "link_fd"))
459 		goto cleanup;
460 	if (!ASSERT_EQ(link_fd, -EINVAL, "path_is_null"))
461 		goto cleanup;
462 
463 	/* wrong path pointer  */
464 	LIBBPF_OPTS_RESET(opts,
465 		.uprobe_multi.path = (const char *) 1,
466 		.uprobe_multi.offsets = (unsigned long *) &offset,
467 		.uprobe_multi.cnt = 1,
468 	);
469 
470 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
471 	if (!ASSERT_ERR(link_fd, "link_fd"))
472 		goto cleanup;
473 	if (!ASSERT_EQ(link_fd, -EFAULT, "path_is_wrong"))
474 		goto cleanup;
475 
476 	/* wrong path type */
477 	LIBBPF_OPTS_RESET(opts,
478 		.uprobe_multi.path = "/",
479 		.uprobe_multi.offsets = (unsigned long *) &offset,
480 		.uprobe_multi.cnt = 1,
481 	);
482 
483 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
484 	if (!ASSERT_ERR(link_fd, "link_fd"))
485 		goto cleanup;
486 	if (!ASSERT_EQ(link_fd, -EBADF, "path_is_wrong_type"))
487 		goto cleanup;
488 
489 	/* wrong cookies pointer */
490 	LIBBPF_OPTS_RESET(opts,
491 		.uprobe_multi.path = path,
492 		.uprobe_multi.offsets = (unsigned long *) &offset,
493 		.uprobe_multi.cookies = (__u64 *) 1ULL,
494 		.uprobe_multi.cnt = 1,
495 	);
496 
497 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
498 	if (!ASSERT_ERR(link_fd, "link_fd"))
499 		goto cleanup;
500 	if (!ASSERT_EQ(link_fd, -EFAULT, "cookies_is_wrong"))
501 		goto cleanup;
502 
503 	/* wrong ref_ctr_offsets pointer */
504 	LIBBPF_OPTS_RESET(opts,
505 		.uprobe_multi.path = path,
506 		.uprobe_multi.offsets = (unsigned long *) &offset,
507 		.uprobe_multi.cookies = (__u64 *) &offset,
508 		.uprobe_multi.ref_ctr_offsets = (unsigned long *) 1,
509 		.uprobe_multi.cnt = 1,
510 	);
511 
512 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
513 	if (!ASSERT_ERR(link_fd, "link_fd"))
514 		goto cleanup;
515 	if (!ASSERT_EQ(link_fd, -EFAULT, "ref_ctr_offsets_is_wrong"))
516 		goto cleanup;
517 
518 	/* wrong flags */
519 	LIBBPF_OPTS_RESET(opts,
520 		.uprobe_multi.flags = 1 << 31,
521 	);
522 
523 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
524 	if (!ASSERT_ERR(link_fd, "link_fd"))
525 		goto cleanup;
526 	if (!ASSERT_EQ(link_fd, -EINVAL, "wrong_flags"))
527 		goto cleanup;
528 
529 	/* wrong pid */
530 	LIBBPF_OPTS_RESET(opts,
531 		.uprobe_multi.path = path,
532 		.uprobe_multi.offsets = (unsigned long *) &offset,
533 		.uprobe_multi.cnt = 1,
534 		.uprobe_multi.pid = -2,
535 	);
536 
537 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
538 	if (!ASSERT_ERR(link_fd, "link_fd"))
539 		goto cleanup;
540 	if (!ASSERT_EQ(link_fd, -EINVAL, "pid_is_wrong"))
541 		goto cleanup;
542 
543 	/* wrong path_fd */
544 	LIBBPF_OPTS_RESET(opts,
545 		.uprobe_multi.path = NULL,
546 		.uprobe_multi.path_fd = -1,
547 		.uprobe_multi.flags = BPF_F_UPROBE_MULTI_PATH_FD,
548 		.uprobe_multi.offsets = (unsigned long *)&offset,
549 		.uprobe_multi.cnt = 1,
550 	);
551 
552 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
553 	if (!ASSERT_ERR(link_fd, "link_fd"))
554 		goto cleanup;
555 	if (!ASSERT_EQ(link_fd, -EBADF, "path_fd_is_wrong"))
556 		goto cleanup;
557 
558 	/* path and path_fd both set with BPF_F_UPROBE_MULTI_PATH_FD flag */
559 	LIBBPF_OPTS_RESET(opts,
560 		.uprobe_multi.path = path,
561 		.uprobe_multi.path_fd = 1,
562 		.uprobe_multi.flags = BPF_F_UPROBE_MULTI_PATH_FD,
563 		.uprobe_multi.offsets = (unsigned long *)&offset,
564 		.uprobe_multi.cnt = 1,
565 	);
566 
567 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
568 	if (!ASSERT_ERR(link_fd, "link_fd"))
569 		goto cleanup;
570 	ASSERT_EQ(link_fd, -EINVAL, "path_and_path_fd_together");
571 
572 cleanup:
573 	if (link_fd >= 0)
574 		close(link_fd);
575 	uprobe_multi__destroy(skel);
576 }
577 
578 #ifdef __x86_64__
579 noinline void uprobe_multi_error_func(void)
580 {
581 	/*
582 	 * If --fcf-protection=branch is enabled the gcc generates endbr as
583 	 * first instruction, so marking the exact address of int3 with the
584 	 * symbol to be used in the attach_uprobe_fail_trap test below.
585 	 */
586 	asm volatile (
587 		".globl uprobe_multi_error_func_int3;	\n"
588 		"uprobe_multi_error_func_int3:		\n"
589 		"int3					\n"
590 	);
591 }
592 
593 /*
594  * Attaching uprobe on uprobe_multi_error_func results in error
595  * because it already starts with int3 instruction.
596  */
597 static void attach_uprobe_fail_trap(struct uprobe_multi *skel)
598 {
599 	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
600 	const char *syms[4] = {
601 		"uprobe_multi_func_1",
602 		"uprobe_multi_func_2",
603 		"uprobe_multi_func_3",
604 		"uprobe_multi_error_func_int3",
605 	};
606 
607 	opts.syms = syms;
608 	opts.cnt = ARRAY_SIZE(syms);
609 
610 	skel->links.uprobe = bpf_program__attach_uprobe_multi(skel->progs.uprobe, -1,
611 							      "/proc/self/exe", NULL, &opts);
612 	if (!ASSERT_ERR_PTR(skel->links.uprobe, "bpf_program__attach_uprobe_multi")) {
613 		bpf_link__destroy(skel->links.uprobe);
614 		skel->links.uprobe = NULL;
615 	}
616 }
617 #else
618 static void attach_uprobe_fail_trap(struct uprobe_multi *skel) { }
619 #endif
620 
621 short sema_1 __used, sema_2 __used;
622 
623 static void attach_uprobe_fail_refctr(struct uprobe_multi *skel)
624 {
625 	unsigned long *tmp_offsets = NULL, *tmp_ref_ctr_offsets = NULL;
626 	unsigned long offsets[3], ref_ctr_offsets[3];
627 	LIBBPF_OPTS(bpf_link_create_opts, opts);
628 	const char *path = "/proc/self/exe";
629 	const char *syms[3] = {
630 		"uprobe_multi_func_1",
631 		"uprobe_multi_func_2",
632 	};
633 	const char *sema[3] = {
634 		"sema_1",
635 		"sema_2",
636 	};
637 	int prog_fd, link_fd, err;
638 
639 	prog_fd = bpf_program__fd(skel->progs.uprobe_extra);
640 
641 	err = elf_resolve_syms_offsets("/proc/self/exe", 2, (const char **) &syms,
642 				       &tmp_offsets, STT_FUNC);
643 	if (!ASSERT_OK(err, "elf_resolve_syms_offsets_func"))
644 		return;
645 
646 	err = elf_resolve_syms_offsets("/proc/self/exe", 2, (const char **) &sema,
647 				       &tmp_ref_ctr_offsets, STT_OBJECT);
648 	if (!ASSERT_OK(err, "elf_resolve_syms_offsets_sema"))
649 		goto cleanup;
650 
651 	/*
652 	 * We attach to 3 uprobes on 2 functions, so 2 uprobes share single function,
653 	 * but with different ref_ctr_offset which is not allowed and results in fail.
654 	 */
655 	offsets[0] = tmp_offsets[0]; /* uprobe_multi_func_1 */
656 	offsets[1] = tmp_offsets[1]; /* uprobe_multi_func_2 */
657 	offsets[2] = tmp_offsets[1]; /* uprobe_multi_func_2 */
658 
659 	ref_ctr_offsets[0] = tmp_ref_ctr_offsets[0]; /* sema_1 */
660 	ref_ctr_offsets[1] = tmp_ref_ctr_offsets[1]; /* sema_2 */
661 	ref_ctr_offsets[2] = tmp_ref_ctr_offsets[0]; /* sema_1, error */
662 
663 	opts.uprobe_multi.path = path;
664 	opts.uprobe_multi.offsets = (const unsigned long *) &offsets;
665 	opts.uprobe_multi.ref_ctr_offsets = (const unsigned long *) &ref_ctr_offsets;
666 	opts.uprobe_multi.cnt = 3;
667 
668 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
669 	if (!ASSERT_ERR(link_fd, "link_fd"))
670 		close(link_fd);
671 
672 cleanup:
673 	free(tmp_ref_ctr_offsets);
674 	free(tmp_offsets);
675 }
676 
677 static void test_attach_uprobe_fails(void)
678 {
679 	struct uprobe_multi *skel = NULL;
680 
681 	skel = uprobe_multi__open_and_load();
682 	if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
683 		return;
684 
685 	/* attach fails due to adding uprobe on trap instruction, x86_64 only */
686 	attach_uprobe_fail_trap(skel);
687 
688 	/* attach fail due to wrong ref_ctr_offs on one of the uprobes */
689 	attach_uprobe_fail_refctr(skel);
690 
691 	uprobe_multi__destroy(skel);
692 }
693 
694 static void __test_link_api(struct child *child)
695 {
696 	int prog_fd, link1_fd = -1, link2_fd = -1, link3_fd = -1, link4_fd = -1;
697 	LIBBPF_OPTS(bpf_link_create_opts, opts);
698 	const char *path = "/proc/self/exe";
699 	struct uprobe_multi *skel = NULL;
700 	unsigned long *offsets = NULL;
701 	const char *syms[3] = {
702 		"uprobe_multi_func_1",
703 		"uprobe_multi_func_2",
704 		"uprobe_multi_func_3",
705 	};
706 	int link_extra_fd = -1;
707 	int err;
708 
709 	err = elf_resolve_syms_offsets(path, 3, syms, (unsigned long **) &offsets, STT_FUNC);
710 	if (!ASSERT_OK(err, "elf_resolve_syms_offsets"))
711 		return;
712 
713 	opts.uprobe_multi.path = path;
714 	opts.uprobe_multi.offsets = offsets;
715 	opts.uprobe_multi.cnt = ARRAY_SIZE(syms);
716 	opts.uprobe_multi.pid = child ? child->pid : 0;
717 
718 	skel = uprobe_multi__open_and_load();
719 	if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
720 		goto cleanup;
721 
722 	opts.kprobe_multi.flags = 0;
723 	prog_fd = bpf_program__fd(skel->progs.uprobe);
724 	link1_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
725 	if (!ASSERT_GE(link1_fd, 0, "link1_fd"))
726 		goto cleanup;
727 
728 	opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN;
729 	prog_fd = bpf_program__fd(skel->progs.uretprobe);
730 	link2_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
731 	if (!ASSERT_GE(link2_fd, 0, "link2_fd"))
732 		goto cleanup;
733 
734 	opts.kprobe_multi.flags = 0;
735 	prog_fd = bpf_program__fd(skel->progs.uprobe_sleep);
736 	link3_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
737 	if (!ASSERT_GE(link3_fd, 0, "link3_fd"))
738 		goto cleanup;
739 
740 	opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN;
741 	prog_fd = bpf_program__fd(skel->progs.uretprobe_sleep);
742 	link4_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
743 	if (!ASSERT_GE(link4_fd, 0, "link4_fd"))
744 		goto cleanup;
745 
746 	opts.kprobe_multi.flags = 0;
747 	opts.uprobe_multi.pid = 0;
748 	prog_fd = bpf_program__fd(skel->progs.uprobe_extra);
749 	link_extra_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
750 	if (!ASSERT_GE(link_extra_fd, 0, "link_extra_fd"))
751 		goto cleanup;
752 
753 	uprobe_multi_test_run(skel, child);
754 
755 cleanup:
756 	if (link1_fd >= 0)
757 		close(link1_fd);
758 	if (link2_fd >= 0)
759 		close(link2_fd);
760 	if (link3_fd >= 0)
761 		close(link3_fd);
762 	if (link4_fd >= 0)
763 		close(link4_fd);
764 	if (link_extra_fd >= 0)
765 		close(link_extra_fd);
766 
767 	uprobe_multi__destroy(skel);
768 	free(offsets);
769 }
770 
771 static void test_link_api(void)
772 {
773 	static struct child child;
774 
775 	/* no pid filter */
776 	__test_link_api(NULL);
777 
778 	/* pid filter */
779 	if (!ASSERT_OK(spawn_child(&child), "spawn_child"))
780 		return;
781 
782 	__test_link_api(&child);
783 
784 	/* pid filter (thread) */
785 	if (!ASSERT_OK(spawn_thread(&child), "spawn_thread"))
786 		return;
787 
788 	__test_link_api(&child);
789 }
790 
791 static void test_link_api_path_fd(void)
792 {
793 	LIBBPF_OPTS(bpf_link_create_opts, opts);
794 	const char *resolve_path = "/proc/self/exe";
795 	int prog_fd, link_fd = -1, path_fd = -1;
796 	struct uprobe_multi *skel = NULL;
797 	unsigned long *offsets = NULL;
798 	const char *syms[3] = {
799 		"uprobe_multi_func_1",
800 		"uprobe_multi_func_2",
801 		"uprobe_multi_func_3",
802 	};
803 	int err;
804 
805 	err = elf_resolve_syms_offsets(resolve_path, ARRAY_SIZE(syms), syms,
806 				       &offsets, STT_FUNC);
807 	if (!ASSERT_OK(err, "elf_resolve_syms_offsets"))
808 		return;
809 
810 	path_fd = open(resolve_path, O_RDONLY);
811 	if (!ASSERT_GE(path_fd, 0, "path_fd"))
812 		goto cleanup;
813 
814 	opts.uprobe_multi.path_fd = path_fd;
815 	opts.uprobe_multi.offsets = offsets;
816 	opts.uprobe_multi.cnt = ARRAY_SIZE(syms);
817 	opts.uprobe_multi.flags = BPF_F_UPROBE_MULTI_PATH_FD;
818 
819 	skel = uprobe_multi__open_and_load();
820 	if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
821 		goto cleanup;
822 
823 	prog_fd = bpf_program__fd(skel->progs.uprobe);
824 	link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
825 	if (!ASSERT_GE(link_fd, 0, "bpf_link_create"))
826 		goto cleanup;
827 
828 	skel->bss->uprobe_multi_func_1_addr = (__u64)uprobe_multi_func_1;
829 	skel->bss->uprobe_multi_func_2_addr = (__u64)uprobe_multi_func_2;
830 	skel->bss->uprobe_multi_func_3_addr = (__u64)uprobe_multi_func_3;
831 	skel->bss->pid = getpid();
832 
833 	uprobe_multi_func_1();
834 	uprobe_multi_func_2();
835 	uprobe_multi_func_3();
836 
837 	ASSERT_EQ(skel->bss->uprobe_multi_func_1_result, 1, "uprobe_multi_func_1_result");
838 	ASSERT_EQ(skel->bss->uprobe_multi_func_2_result, 1, "uprobe_multi_func_2_result");
839 	ASSERT_EQ(skel->bss->uprobe_multi_func_3_result, 1, "uprobe_multi_func_3_result");
840 
841 cleanup:
842 	if (link_fd >= 0)
843 		close(link_fd);
844 	if (path_fd >= 0)
845 		close(path_fd);
846 	uprobe_multi__destroy(skel);
847 	free(offsets);
848 }
849 
850 static struct bpf_program *
851 get_program(struct uprobe_multi_consumers *skel, int prog)
852 {
853 	switch (prog) {
854 	case 0:
855 		return skel->progs.uprobe_0;
856 	case 1:
857 		return skel->progs.uprobe_1;
858 	case 2:
859 		return skel->progs.uprobe_2;
860 	case 3:
861 		return skel->progs.uprobe_3;
862 	default:
863 		ASSERT_FAIL("get_program");
864 		return NULL;
865 	}
866 }
867 
868 static struct bpf_link **
869 get_link(struct uprobe_multi_consumers *skel, int link)
870 {
871 	switch (link) {
872 	case 0:
873 		return &skel->links.uprobe_0;
874 	case 1:
875 		return &skel->links.uprobe_1;
876 	case 2:
877 		return &skel->links.uprobe_2;
878 	case 3:
879 		return &skel->links.uprobe_3;
880 	default:
881 		ASSERT_FAIL("get_link");
882 		return NULL;
883 	}
884 }
885 
886 static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx, unsigned long offset)
887 {
888 	struct bpf_program *prog = get_program(skel, idx);
889 	struct bpf_link **link = get_link(skel, idx);
890 	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
891 
892 	if (!prog || !link)
893 		return -1;
894 
895 	opts.offsets = &offset;
896 	opts.cnt = 1;
897 
898 	/*
899 	 * bit/prog: 0 uprobe entry
900 	 * bit/prog: 1 uprobe return
901 	 * bit/prog: 2 uprobe session without return
902 	 * bit/prog: 3 uprobe session with return
903 	 */
904 	opts.retprobe = idx == 1;
905 	opts.session  = idx == 2 || idx == 3;
906 
907 	*link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe", NULL, &opts);
908 	if (!ASSERT_OK_PTR(*link, "bpf_program__attach_uprobe_multi"))
909 		return -1;
910 	return 0;
911 }
912 
913 static void uprobe_detach(struct uprobe_multi_consumers *skel, int idx)
914 {
915 	struct bpf_link **link = get_link(skel, idx);
916 
917 	bpf_link__destroy(*link);
918 	*link = NULL;
919 }
920 
921 static bool test_bit(int bit, unsigned long val)
922 {
923 	return val & (1 << bit);
924 }
925 
926 noinline int
927 uprobe_consumer_test(struct uprobe_multi_consumers *skel,
928 		     unsigned long before, unsigned long after,
929 		     unsigned long offset)
930 {
931 	int idx;
932 
933 	/* detach uprobe for each unset programs in 'before' state ... */
934 	for (idx = 0; idx < 4; idx++) {
935 		if (test_bit(idx, before) && !test_bit(idx, after))
936 			uprobe_detach(skel, idx);
937 	}
938 
939 	/* ... and attach all new programs in 'after' state */
940 	for (idx = 0; idx < 4; idx++) {
941 		if (!test_bit(idx, before) && test_bit(idx, after)) {
942 			if (!ASSERT_OK(uprobe_attach(skel, idx, offset), "uprobe_attach_after"))
943 				return -1;
944 		}
945 	}
946 	return 0;
947 }
948 
949 /*
950  * We generate 16 consumer_testX functions that will have uprobe installed on
951  * and will be called in separate threads. All function pointer are stored in
952  * "consumers" section and each thread will pick one function based on index.
953  */
954 
955 extern const void *__start_consumers;
956 
957 #define __CONSUMER_TEST(func) 							\
958 noinline int func(struct uprobe_multi_consumers *skel, unsigned long before,	\
959 		  unsigned long after, unsigned long offset)			\
960 {										\
961 	return uprobe_consumer_test(skel, before, after, offset);		\
962 }										\
963 void *__ ## func __used __attribute__((section("consumers"))) = (void *) func;
964 
965 #define CONSUMER_TEST(func) __CONSUMER_TEST(func)
966 
967 #define C1  CONSUMER_TEST(__PASTE(consumer_test, __COUNTER__))
968 #define C4  C1 C1 C1 C1
969 #define C16 C4 C4 C4 C4
970 
971 C16
972 
973 typedef int (*test_t)(struct uprobe_multi_consumers *, unsigned long,
974 		      unsigned long, unsigned long);
975 
976 static int consumer_test(struct uprobe_multi_consumers *skel,
977 			 unsigned long before, unsigned long after,
978 			 test_t test, unsigned long offset)
979 {
980 	int err, idx, ret = -1;
981 
982 	printf("consumer_test before %lu after %lu\n", before, after);
983 
984 	/* 'before' is each, we attach uprobe for every set idx */
985 	for (idx = 0; idx < 4; idx++) {
986 		if (test_bit(idx, before)) {
987 			if (!ASSERT_OK(uprobe_attach(skel, idx, offset), "uprobe_attach_before"))
988 				goto cleanup;
989 		}
990 	}
991 
992 	err = test(skel, before, after, offset);
993 	if (!ASSERT_EQ(err, 0, "uprobe_consumer_test"))
994 		goto cleanup;
995 
996 	for (idx = 0; idx < 4; idx++) {
997 		bool uret_stays, uret_survives;
998 		const char *fmt = "BUG";
999 		__u64 val = 0;
1000 
1001 		switch (idx) {
1002 		case 0:
1003 			/*
1004 			 * uprobe entry
1005 			 *   +1 if define in 'before'
1006 			 */
1007 			if (test_bit(idx, before))
1008 				val++;
1009 			fmt = "prog 0: uprobe";
1010 			break;
1011 		case 1:
1012 			/*
1013 			 * To trigger uretprobe consumer, the uretprobe under test either stayed from
1014 			 * before to after (uret_stays + test_bit) or uretprobe instance survived and
1015 			 * we have uretprobe active in after (uret_survives + test_bit)
1016 			 */
1017 			uret_stays = before & after & 0b0110;
1018 			uret_survives = ((before & 0b0110) && (after & 0b0110) && (before & 0b1001));
1019 
1020 			if ((uret_stays || uret_survives) && test_bit(idx, after))
1021 				val++;
1022 			fmt = "prog 1: uretprobe";
1023 			break;
1024 		case 2:
1025 			/*
1026 			 * session with return
1027 			 *  +1 if defined in 'before'
1028 			 *  +1 if defined in 'after'
1029 			 */
1030 			if (test_bit(idx, before)) {
1031 				val++;
1032 				if (test_bit(idx, after))
1033 					val++;
1034 			}
1035 			fmt = "prog 2: session with return";
1036 			break;
1037 		case 3:
1038 			/*
1039 			 * session without return
1040 			 *   +1 if defined in 'before'
1041 			 */
1042 			if (test_bit(idx, before))
1043 				val++;
1044 			fmt = "prog 3: session with NO return";
1045 			break;
1046 		}
1047 
1048 		if (!ASSERT_EQ(skel->bss->uprobe_result[idx], val, fmt))
1049 			goto cleanup;
1050 		skel->bss->uprobe_result[idx] = 0;
1051 	}
1052 
1053 	ret = 0;
1054 
1055 cleanup:
1056 	for (idx = 0; idx < 4; idx++)
1057 		uprobe_detach(skel, idx);
1058 	return ret;
1059 }
1060 
1061 #define CONSUMER_MAX 16
1062 
1063 /*
1064  * Each thread runs 1/16 of the load by running test for single
1065  * 'before' number (based on thread index) and full scale of
1066  * 'after' numbers.
1067  */
1068 static void *consumer_thread(void *arg)
1069 {
1070 	unsigned long idx = (unsigned long) arg;
1071 	struct uprobe_multi_consumers *skel;
1072 	unsigned long offset;
1073 	const void *func;
1074 	int after;
1075 
1076 	skel = uprobe_multi_consumers__open_and_load();
1077 	if (!ASSERT_OK_PTR(skel, "uprobe_multi_consumers__open_and_load"))
1078 		return NULL;
1079 
1080 	func = *((&__start_consumers) + idx);
1081 
1082 	offset = get_uprobe_offset(func);
1083 	if (!ASSERT_GE(offset, 0, "uprobe_offset"))
1084 		goto out;
1085 
1086 	for (after = 0; after < CONSUMER_MAX; after++)
1087 		if (consumer_test(skel, idx, after, func, offset))
1088 			goto out;
1089 
1090 out:
1091 	uprobe_multi_consumers__destroy(skel);
1092 	return NULL;
1093 }
1094 
1095 
1096 static void test_consumers(void)
1097 {
1098 	pthread_t pt[CONSUMER_MAX];
1099 	unsigned long idx;
1100 	int err;
1101 
1102 	/*
1103 	 * The idea of this test is to try all possible combinations of
1104 	 * uprobes consumers attached on single function.
1105 	 *
1106 	 *  - 1 uprobe entry consumer
1107 	 *  - 1 uprobe exit consumer
1108 	 *  - 1 uprobe session with return
1109 	 *  - 1 uprobe session without return
1110 	 *
1111 	 * The test uses 4 uprobes attached on single function, but that
1112 	 * translates into single uprobe with 4 consumers in kernel.
1113 	 *
1114 	 * The before/after values present the state of attached consumers
1115 	 * before and after the probed function:
1116 	 *
1117 	 *  bit/prog 0 : uprobe entry
1118 	 *  bit/prog 1 : uprobe return
1119 	 *
1120 	 * For example for:
1121 	 *
1122 	 *   before = 0b01
1123 	 *   after  = 0b10
1124 	 *
1125 	 * it means that before we call 'uprobe_consumer_test' we attach
1126 	 * uprobes defined in 'before' value:
1127 	 *
1128 	 *   - bit/prog 1: uprobe entry
1129 	 *
1130 	 * uprobe_consumer_test is called and inside it we attach and detach
1131 	 * uprobes based on 'after' value:
1132 	 *
1133 	 *   - bit/prog 0: is detached
1134 	 *   - bit/prog 1: is attached
1135 	 *
1136 	 * uprobe_consumer_test returns and we check counters values increased
1137 	 * by bpf programs on each uprobe to match the expected count based on
1138 	 * before/after bits.
1139 	 */
1140 
1141 	for (idx = 0; idx < CONSUMER_MAX; idx++) {
1142 		err = pthread_create(&pt[idx], NULL, consumer_thread, (void *) idx);
1143 		if (!ASSERT_OK(err, "pthread_create"))
1144 			break;
1145 	}
1146 
1147 	while (idx)
1148 		pthread_join(pt[--idx], NULL);
1149 }
1150 
1151 static struct bpf_program *uprobe_multi_program(struct uprobe_multi_pid_filter *skel, int idx)
1152 {
1153 	switch (idx) {
1154 	case 0: return skel->progs.uprobe_multi_0;
1155 	case 1: return skel->progs.uprobe_multi_1;
1156 	case 2: return skel->progs.uprobe_multi_2;
1157 	}
1158 	return NULL;
1159 }
1160 
1161 #define TASKS 3
1162 
1163 static void run_pid_filter(struct uprobe_multi_pid_filter *skel, bool clone_vm, bool retprobe)
1164 {
1165 	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts, .retprobe = retprobe);
1166 	struct bpf_link *link[TASKS] = {};
1167 	struct child child[TASKS] = {};
1168 	int i;
1169 
1170 	memset(skel->bss->test, 0, sizeof(skel->bss->test));
1171 
1172 	for (i = 0; i < TASKS; i++) {
1173 		if (!ASSERT_OK(spawn_child_flag(&child[i], clone_vm), "spawn_child"))
1174 			goto cleanup;
1175 		skel->bss->pids[i] = child[i].pid;
1176 	}
1177 
1178 	for (i = 0; i < TASKS; i++) {
1179 		link[i] = bpf_program__attach_uprobe_multi(uprobe_multi_program(skel, i),
1180 							   child[i].pid, "/proc/self/exe",
1181 							   "uprobe_multi_func_1", &opts);
1182 		if (!ASSERT_OK_PTR(link[i], "bpf_program__attach_uprobe_multi"))
1183 			goto cleanup;
1184 	}
1185 
1186 	for (i = 0; i < TASKS; i++)
1187 		kick_child(&child[i]);
1188 
1189 	for (i = 0; i < TASKS; i++) {
1190 		ASSERT_EQ(skel->bss->test[i][0], 1, "pid");
1191 		ASSERT_EQ(skel->bss->test[i][1], 0, "unknown");
1192 	}
1193 
1194 cleanup:
1195 	for (i = 0; i < TASKS; i++)
1196 		bpf_link__destroy(link[i]);
1197 	for (i = 0; i < TASKS; i++)
1198 		release_child(&child[i]);
1199 }
1200 
1201 static void test_pid_filter_process(bool clone_vm)
1202 {
1203 	struct uprobe_multi_pid_filter *skel;
1204 
1205 	skel = uprobe_multi_pid_filter__open_and_load();
1206 	if (!ASSERT_OK_PTR(skel, "uprobe_multi_pid_filter__open_and_load"))
1207 		return;
1208 
1209 	run_pid_filter(skel, clone_vm, false);
1210 	run_pid_filter(skel, clone_vm, true);
1211 
1212 	uprobe_multi_pid_filter__destroy(skel);
1213 }
1214 
1215 static void test_session_skel_api(void)
1216 {
1217 	struct uprobe_multi_session *skel = NULL;
1218 	LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
1219 	struct bpf_link *link = NULL;
1220 	int err;
1221 
1222 	skel = uprobe_multi_session__open_and_load();
1223 	if (!ASSERT_OK_PTR(skel, "uprobe_multi_session__open_and_load"))
1224 		goto cleanup;
1225 
1226 	skel->bss->pid = getpid();
1227 	skel->bss->user_ptr = test_data;
1228 
1229 	err = uprobe_multi_session__attach(skel);
1230 	if (!ASSERT_OK(err, "uprobe_multi_session__attach"))
1231 		goto cleanup;
1232 
1233 	/* trigger all probes */
1234 	skel->bss->uprobe_multi_func_1_addr = (__u64) uprobe_multi_func_1;
1235 	skel->bss->uprobe_multi_func_2_addr = (__u64) uprobe_multi_func_2;
1236 	skel->bss->uprobe_multi_func_3_addr = (__u64) uprobe_multi_func_3;
1237 
1238 	uprobe_multi_func_1();
1239 	uprobe_multi_func_2();
1240 	uprobe_multi_func_3();
1241 
1242 	/*
1243 	 * We expect 2 for uprobe_multi_func_2 because it runs both entry/return probe,
1244 	 * uprobe_multi_func_[13] run just the entry probe. All expected numbers are
1245 	 * doubled, because we run extra test for sleepable session.
1246 	 */
1247 	ASSERT_EQ(skel->bss->uprobe_session_result[0], 2, "uprobe_multi_func_1_result");
1248 	ASSERT_EQ(skel->bss->uprobe_session_result[1], 4, "uprobe_multi_func_2_result");
1249 	ASSERT_EQ(skel->bss->uprobe_session_result[2], 2, "uprobe_multi_func_3_result");
1250 
1251 	/* We expect increase in 3 entry and 1 return session calls -> 4 */
1252 	ASSERT_EQ(skel->bss->uprobe_multi_sleep_result, 4, "uprobe_multi_sleep_result");
1253 
1254 cleanup:
1255 	bpf_link__destroy(link);
1256 	uprobe_multi_session__destroy(skel);
1257 }
1258 
1259 static void test_session_single_skel_api(void)
1260 {
1261 	struct uprobe_multi_session_single *skel = NULL;
1262 	LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
1263 	int err;
1264 
1265 	skel = uprobe_multi_session_single__open_and_load();
1266 	if (!ASSERT_OK_PTR(skel, "uprobe_multi_session_single__open_and_load"))
1267 		goto cleanup;
1268 
1269 	skel->bss->pid = getpid();
1270 
1271 	err = uprobe_multi_session_single__attach(skel);
1272 	if (!ASSERT_OK(err, "uprobe_multi_session_single__attach"))
1273 		goto cleanup;
1274 
1275 	uprobe_multi_func_1();
1276 
1277 	/*
1278 	 * We expect consumer 0 and 2 to trigger just entry handler (value 1)
1279 	 * and consumer 1 to hit both (value 2).
1280 	 */
1281 	ASSERT_EQ(skel->bss->uprobe_session_result[0], 1, "uprobe_session_result_0");
1282 	ASSERT_EQ(skel->bss->uprobe_session_result[1], 2, "uprobe_session_result_1");
1283 	ASSERT_EQ(skel->bss->uprobe_session_result[2], 1, "uprobe_session_result_2");
1284 
1285 cleanup:
1286 	uprobe_multi_session_single__destroy(skel);
1287 }
1288 
1289 static void test_session_cookie_skel_api(void)
1290 {
1291 	struct uprobe_multi_session_cookie *skel = NULL;
1292 	int err;
1293 
1294 	skel = uprobe_multi_session_cookie__open_and_load();
1295 	if (!ASSERT_OK_PTR(skel, "uprobe_multi_session_cookie__open_and_load"))
1296 		goto cleanup;
1297 
1298 	skel->bss->pid = getpid();
1299 
1300 	err = uprobe_multi_session_cookie__attach(skel);
1301 	if (!ASSERT_OK(err, "uprobe_multi_session_cookie__attach"))
1302 		goto cleanup;
1303 
1304 	/* trigger all probes */
1305 	uprobe_multi_func_1();
1306 	uprobe_multi_func_2();
1307 	uprobe_multi_func_3();
1308 
1309 	ASSERT_EQ(skel->bss->test_uprobe_1_result, 1, "test_uprobe_1_result");
1310 	ASSERT_EQ(skel->bss->test_uprobe_2_result, 2, "test_uprobe_2_result");
1311 	ASSERT_EQ(skel->bss->test_uprobe_3_result, 3, "test_uprobe_3_result");
1312 
1313 cleanup:
1314 	uprobe_multi_session_cookie__destroy(skel);
1315 }
1316 
1317 static void test_session_recursive_skel_api(void)
1318 {
1319 	struct uprobe_multi_session_recursive *skel = NULL;
1320 	int i, err;
1321 
1322 	skel = uprobe_multi_session_recursive__open_and_load();
1323 	if (!ASSERT_OK_PTR(skel, "uprobe_multi_session_recursive__open_and_load"))
1324 		goto cleanup;
1325 
1326 	skel->bss->pid = getpid();
1327 
1328 	err = uprobe_multi_session_recursive__attach(skel);
1329 	if (!ASSERT_OK(err, "uprobe_multi_session_recursive__attach"))
1330 		goto cleanup;
1331 
1332 	for (i = 0; i < ARRAY_SIZE(skel->bss->test_uprobe_cookie_entry); i++)
1333 		skel->bss->test_uprobe_cookie_entry[i] = i + 1;
1334 
1335 	uprobe_session_recursive(5);
1336 
1337 	/*
1338 	 *                                         entry uprobe:
1339 	 * uprobe_session_recursive(5) {             *cookie = 1, return 0
1340 	 *   uprobe_session_recursive(4) {           *cookie = 2, return 1
1341 	 *     uprobe_session_recursive(3) {         *cookie = 3, return 0
1342 	 *       uprobe_session_recursive(2) {       *cookie = 4, return 1
1343 	 *         uprobe_session_recursive(1) {     *cookie = 5, return 0
1344 	 *           uprobe_session_recursive(0) {   *cookie = 6, return 1
1345 	 *                                          return uprobe:
1346 	 *           } i = 0                          not executed
1347 	 *         } i = 1                            test_uprobe_cookie_return[0] = 5
1348 	 *       } i = 2                              not executed
1349 	 *     } i = 3                                test_uprobe_cookie_return[1] = 3
1350 	 *   } i = 4                                  not executed
1351 	 * } i = 5                                    test_uprobe_cookie_return[2] = 1
1352 	 */
1353 
1354 	ASSERT_EQ(skel->bss->idx_entry, 6, "idx_entry");
1355 	ASSERT_EQ(skel->bss->idx_return, 3, "idx_return");
1356 
1357 	ASSERT_EQ(skel->bss->test_uprobe_cookie_return[0], 5, "test_uprobe_cookie_return[0]");
1358 	ASSERT_EQ(skel->bss->test_uprobe_cookie_return[1], 3, "test_uprobe_cookie_return[1]");
1359 	ASSERT_EQ(skel->bss->test_uprobe_cookie_return[2], 1, "test_uprobe_cookie_return[2]");
1360 
1361 cleanup:
1362 	uprobe_multi_session_recursive__destroy(skel);
1363 }
1364 
1365 static void test_bench_attach_uprobe(void)
1366 {
1367 	long attach_start_ns = 0, attach_end_ns = 0;
1368 	struct uprobe_multi_bench *skel = NULL;
1369 	long detach_start_ns, detach_end_ns;
1370 	double attach_delta, detach_delta;
1371 	int err;
1372 
1373 	skel = uprobe_multi_bench__open_and_load();
1374 	if (!ASSERT_OK_PTR(skel, "uprobe_multi_bench__open_and_load"))
1375 		goto cleanup;
1376 
1377 	attach_start_ns = get_time_ns();
1378 
1379 	err = uprobe_multi_bench__attach(skel);
1380 	if (!ASSERT_OK(err, "uprobe_multi_bench__attach"))
1381 		goto cleanup;
1382 
1383 	attach_end_ns = get_time_ns();
1384 
1385 	system("./uprobe_multi bench");
1386 
1387 	ASSERT_EQ(skel->bss->count, 50000, "uprobes_count");
1388 
1389 cleanup:
1390 	detach_start_ns = get_time_ns();
1391 	uprobe_multi_bench__destroy(skel);
1392 	detach_end_ns = get_time_ns();
1393 
1394 	attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0;
1395 	detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0;
1396 
1397 	printf("%s: attached in %7.3lfs\n", __func__, attach_delta);
1398 	printf("%s: detached in %7.3lfs\n", __func__, detach_delta);
1399 }
1400 
1401 static void test_bench_attach_usdt(void)
1402 {
1403 	long attach_start_ns = 0, attach_end_ns = 0;
1404 	struct uprobe_multi_usdt *skel = NULL;
1405 	long detach_start_ns, detach_end_ns;
1406 	double attach_delta, detach_delta;
1407 
1408 	skel = uprobe_multi_usdt__open_and_load();
1409 	if (!ASSERT_OK_PTR(skel, "uprobe_multi__open"))
1410 		goto cleanup;
1411 
1412 	attach_start_ns = get_time_ns();
1413 
1414 	skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0, -1, "./uprobe_multi",
1415 						     "test", "usdt", NULL);
1416 	if (!ASSERT_OK_PTR(skel->links.usdt0, "bpf_program__attach_usdt"))
1417 		goto cleanup;
1418 
1419 	attach_end_ns = get_time_ns();
1420 
1421 	system("./uprobe_multi usdt");
1422 
1423 	ASSERT_EQ(skel->bss->count, 50000, "usdt_count");
1424 
1425 cleanup:
1426 	detach_start_ns = get_time_ns();
1427 	uprobe_multi_usdt__destroy(skel);
1428 	detach_end_ns = get_time_ns();
1429 
1430 	attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0;
1431 	detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0;
1432 
1433 	printf("%s: attached in %7.3lfs\n", __func__, attach_delta);
1434 	printf("%s: detached in %7.3lfs\n", __func__, detach_delta);
1435 }
1436 
1437 void test_uprobe_multi_test(void)
1438 {
1439 	if (test__start_subtest("skel_api"))
1440 		test_skel_api();
1441 	if (test__start_subtest("attach_api_pattern"))
1442 		test_attach_api_pattern();
1443 	if (test__start_subtest("attach_api_syms"))
1444 		test_attach_api_syms();
1445 	if (test__start_subtest("link_api"))
1446 		test_link_api();
1447 	if (test__start_subtest("link_api_path_fd"))
1448 		test_link_api_path_fd();
1449 	if (test__start_subtest("bench_uprobe"))
1450 		test_bench_attach_uprobe();
1451 	if (test__start_subtest("bench_usdt"))
1452 		test_bench_attach_usdt();
1453 	if (test__start_subtest("attach_api_fails"))
1454 		test_attach_api_fails();
1455 	if (test__start_subtest("attach_uprobe_fails"))
1456 		test_attach_uprobe_fails();
1457 	if (test__start_subtest("consumers"))
1458 		test_consumers();
1459 	if (test__start_subtest("filter_fork"))
1460 		test_pid_filter_process(false);
1461 	if (test__start_subtest("filter_clone_vm"))
1462 		test_pid_filter_process(true);
1463 	if (test__start_subtest("session"))
1464 		test_session_skel_api();
1465 	if (test__start_subtest("session_single"))
1466 		test_session_single_skel_api();
1467 	if (test__start_subtest("session_cookie"))
1468 		test_session_cookie_skel_api();
1469 	if (test__start_subtest("session_cookie_recursive"))
1470 		test_session_recursive_skel_api();
1471 	RUN_TESTS(uprobe_multi_verifier);
1472 }
1473