File Index
Files and directories
Sign in
FILE

uprobe_syscall.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c C
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#include <test_progs.h>
  4
  5#ifdef __x86_64__
  6
  7#include <unistd.h>
  8#include <asm/ptrace.h>
  9#include <linux/compiler.h>
 10#include <linux/stringify.h>
 11#include <linux/kernel.h>
 12#include <sys/wait.h>
 13#include <sys/syscall.h>
 14#include <sys/prctl.h>
 15#include <asm/prctl.h>
 16#include "uprobe_syscall.skel.h"
 17#include "uprobe_syscall_executed.skel.h"
 18#include "bpf/libbpf_internal.h"
 19
 20#define USDT_NOP .byte 0x0f, 0x1f, 0x44, 0x00, 0x00
 21#include "usdt.h"
 22
 23#pragma GCC diagnostic ignored "-Wattributes"
 24
 25__attribute__((aligned(16)))
 26__nocf_check __weak __naked unsigned long uprobe_regs_trigger(void)
 27{
 28	asm volatile (
 29		".byte 0x0f, 0x1f, 0x44, 0x00, 0x00\n" /* nop5 */
 30		"movq $0xdeadbeef, %rax\n"
 31		"ret\n"
 32	);
 33}
 34
 35__naked void uprobe_regs(struct pt_regs *before, struct pt_regs *after)
 36{
 37	asm volatile (
 38		"movq %r15,   0(%rdi)\n"
 39		"movq %r14,   8(%rdi)\n"
 40		"movq %r13,  16(%rdi)\n"
 41		"movq %r12,  24(%rdi)\n"
 42		"movq %rbp,  32(%rdi)\n"
 43		"movq %rbx,  40(%rdi)\n"
 44		"movq %r11,  48(%rdi)\n"
 45		"movq %r10,  56(%rdi)\n"
 46		"movq  %r9,  64(%rdi)\n"
 47		"movq  %r8,  72(%rdi)\n"
 48		"movq %rax,  80(%rdi)\n"
 49		"movq %rcx,  88(%rdi)\n"
 50		"movq %rdx,  96(%rdi)\n"
 51		"movq %rsi, 104(%rdi)\n"
 52		"movq %rdi, 112(%rdi)\n"
 53		"movq   $0, 120(%rdi)\n" /* orig_rax */
 54		"movq   $0, 128(%rdi)\n" /* rip      */
 55		"movq   $0, 136(%rdi)\n" /* cs       */
 56		"pushq %rax\n"
 57		"pushf\n"
 58		"pop %rax\n"
 59		"movq %rax, 144(%rdi)\n" /* eflags   */
 60		"pop %rax\n"
 61		"movq %rsp, 152(%rdi)\n" /* rsp      */
 62		"movq   $0, 160(%rdi)\n" /* ss       */
 63
 64		/* save 2nd argument */
 65		"pushq %rsi\n"
 66		"call uprobe_regs_trigger\n"
 67
 68		/* save  return value and load 2nd argument pointer to rax */
 69		"pushq %rax\n"
 70		"movq 8(%rsp), %rax\n"
 71
 72		"movq %r15,   0(%rax)\n"
 73		"movq %r14,   8(%rax)\n"
 74		"movq %r13,  16(%rax)\n"
 75		"movq %r12,  24(%rax)\n"
 76		"movq %rbp,  32(%rax)\n"
 77		"movq %rbx,  40(%rax)\n"
 78		"movq %r11,  48(%rax)\n"
 79		"movq %r10,  56(%rax)\n"
 80		"movq  %r9,  64(%rax)\n"
 81		"movq  %r8,  72(%rax)\n"
 82		"movq %rcx,  88(%rax)\n"
 83		"movq %rdx,  96(%rax)\n"
 84		"movq %rsi, 104(%rax)\n"
 85		"movq %rdi, 112(%rax)\n"
 86		"movq   $0, 120(%rax)\n" /* orig_rax */
 87		"movq   $0, 128(%rax)\n" /* rip      */
 88		"movq   $0, 136(%rax)\n" /* cs       */
 89
 90		/* restore return value and 2nd argument */
 91		"pop %rax\n"
 92		"pop %rsi\n"
 93
 94		"movq %rax,  80(%rsi)\n"
 95
 96		"pushf\n"
 97		"pop %rax\n"
 98
 99		"movq %rax, 144(%rsi)\n" /* eflags   */
100		"movq %rsp, 152(%rsi)\n" /* rsp      */
101		"movq   $0, 160(%rsi)\n" /* ss       */
102		"ret\n"
103);
104}
105
106static void test_uprobe_regs_equal(bool retprobe)
107{
108	LIBBPF_OPTS(bpf_uprobe_opts, opts,
109		.retprobe = retprobe,
110	);
111	struct uprobe_syscall *skel = NULL;
112	struct pt_regs before = {}, after = {};
113	unsigned long *pb = (unsigned long *) &before;
114	unsigned long *pa = (unsigned long *) &after;
115	unsigned long *pp;
116	unsigned long offset;
117	unsigned int i, cnt;
118
119	offset = get_uprobe_offset(&uprobe_regs_trigger);
120	if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
121		return;
122
123	skel = uprobe_syscall__open_and_load();
124	if (!ASSERT_OK_PTR(skel, "uprobe_syscall__open_and_load"))
125		goto cleanup;
126
127	skel->links.probe = bpf_program__attach_uprobe_opts(skel->progs.probe,
128				0, "/proc/self/exe", offset, &opts);
129	if (!ASSERT_OK_PTR(skel->links.probe, "bpf_program__attach_uprobe_opts"))
130		goto cleanup;
131
132	/* make sure uprobe gets optimized */
133	if (!retprobe)
134		uprobe_regs_trigger();
135
136	uprobe_regs(&before, &after);
137
138	pp = (unsigned long *) &skel->bss->regs;
139	cnt = sizeof(before)/sizeof(*pb);
140
141	for (i = 0; i < cnt; i++) {
142		unsigned int offset = i * sizeof(unsigned long);
143
144		/*
145		 * Check register before and after uprobe_regs_trigger call
146		 * that triggers the uretprobe.
147		 */
148		switch (offset) {
149		case offsetof(struct pt_regs, rax):
150			ASSERT_EQ(pa[i], 0xdeadbeef, "return value");
151			break;
152		default:
153			if (!ASSERT_EQ(pb[i], pa[i], "register before-after value check"))
154				fprintf(stdout, "failed register offset %u\n", offset);
155		}
156
157		/*
158		 * Check register seen from bpf program and register after
159		 * uprobe_regs_trigger call (with rax exception, check below).
160		 */
161		switch (offset) {
162		/*
163		 * These values will be different (not set in uretprobe_regs),
164		 * we don't care.
165		 */
166		case offsetof(struct pt_regs, orig_rax):
167		case offsetof(struct pt_regs, rip):
168		case offsetof(struct pt_regs, cs):
169		case offsetof(struct pt_regs, rsp):
170		case offsetof(struct pt_regs, ss):
171			break;
172		/*
173		 * uprobe does not see return value in rax, it needs to see the
174		 * original (before) rax value
175		 */
176		case offsetof(struct pt_regs, rax):
177			if (!retprobe) {
178				ASSERT_EQ(pp[i], pb[i], "uprobe rax prog-before value check");
179				break;
180			}
181		default:
182			if (!ASSERT_EQ(pp[i], pa[i], "register prog-after value check"))
183				fprintf(stdout, "failed register offset %u\n", offset);
184		}
185	}
186
187cleanup:
188	uprobe_syscall__destroy(skel);
189}
190
191#define BPF_TESTMOD_UPROBE_TEST_FILE "/sys/kernel/bpf_testmod_uprobe"
192
193static int write_bpf_testmod_uprobe(unsigned long offset)
194{
195	size_t n, ret;
196	char buf[30];
197	int fd;
198
199	n = sprintf(buf, "%lu", offset);
200
201	fd = open(BPF_TESTMOD_UPROBE_TEST_FILE, O_WRONLY);
202	if (fd < 0)
203		return -errno;
204
205	ret = write(fd, buf, n);
206	close(fd);
207	return ret != n ? (int) ret : 0;
208}
209
210static void test_regs_change(void)
211{
212	struct pt_regs before = {}, after = {};
213	unsigned long *pb = (unsigned long *) &before;
214	unsigned long *pa = (unsigned long *) &after;
215	unsigned long cnt = sizeof(before)/sizeof(*pb);
216	unsigned int i, err, offset;
217
218	offset = get_uprobe_offset(uprobe_regs_trigger);
219
220	err = write_bpf_testmod_uprobe(offset);
221	if (!ASSERT_OK(err, "register_uprobe"))
222		return;
223
224	/* make sure uprobe gets optimized */
225	uprobe_regs_trigger();
226
227	uprobe_regs(&before, &after);
228
229	err = write_bpf_testmod_uprobe(0);
230	if (!ASSERT_OK(err, "unregister_uprobe"))
231		return;
232
233	for (i = 0; i < cnt; i++) {
234		unsigned int offset = i * sizeof(unsigned long);
235
236		switch (offset) {
237		case offsetof(struct pt_regs, rax):
238			ASSERT_EQ(pa[i], 0x12345678deadbeef, "rax");
239			break;
240		case offsetof(struct pt_regs, rcx):
241			ASSERT_EQ(pa[i], 0x87654321feebdaed, "rcx");
242			break;
243		case offsetof(struct pt_regs, r11):
244			ASSERT_EQ(pa[i], (__u64) -1, "r11");
245			break;
246		default:
247			if (!ASSERT_EQ(pa[i], pb[i], "register before-after value check"))
248				fprintf(stdout, "failed register offset %u\n", offset);
249		}
250	}
251}
252
253#ifndef __NR_uretprobe
254#define __NR_uretprobe 335
255#endif
256
257__naked unsigned long uretprobe_syscall_call_1(void)
258{
259	/*
260	 * Pretend we are uretprobe trampoline to trigger the return
261	 * probe invocation in order to verify we get SIGILL.
262	 */
263	asm volatile (
264		"pushq %rax\n"
265		"pushq %rcx\n"
266		"pushq %r11\n"
267		"movq $" __stringify(__NR_uretprobe) ", %rax\n"
268		"syscall\n"
269		"popq %r11\n"
270		"popq %rcx\n"
271		"retq\n"
272	);
273}
274
275__naked unsigned long uretprobe_syscall_call(void)
276{
277	asm volatile (
278		"call uretprobe_syscall_call_1\n"
279		"retq\n"
280	);
281}
282
283static void test_uretprobe_syscall_call(void)
284{
285	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts,
286		.retprobe = true,
287	);
288	struct uprobe_syscall_executed *skel;
289	int pid, status, err, go[2], c = 0;
290	struct bpf_link *link;
291
292	if (!ASSERT_OK(pipe(go), "pipe"))
293		return;
294
295	skel = uprobe_syscall_executed__open_and_load();
296	if (!ASSERT_OK_PTR(skel, "uprobe_syscall_executed__open_and_load"))
297		goto cleanup;
298
299	pid = fork();
300	if (!ASSERT_GE(pid, 0, "fork"))
301		goto cleanup;
302
303	/* child */
304	if (pid == 0) {
305		close(go[1]);
306
307		/* wait for parent's kick */
308		err = read(go[0], &c, 1);
309		if (err != 1)
310			exit(-1);
311
312		uretprobe_syscall_call();
313		_exit(0);
314	}
315
316	skel->bss->pid = pid;
317
318	link = bpf_program__attach_uprobe_multi(skel->progs.test_uretprobe_multi,
319						pid, "/proc/self/exe",
320						"uretprobe_syscall_call", &opts);
321	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
322		goto cleanup;
323	skel->links.test_uretprobe_multi = link;
324
325	/* kick the child */
326	write(go[1], &c, 1);
327	err = waitpid(pid, &status, 0);
328	ASSERT_EQ(err, pid, "waitpid");
329
330	/* verify the child got killed with SIGILL */
331	ASSERT_EQ(WIFSIGNALED(status), 1, "WIFSIGNALED");
332	ASSERT_EQ(WTERMSIG(status), SIGILL, "WTERMSIG");
333
334	/* verify the uretprobe program wasn't called */
335	ASSERT_EQ(skel->bss->executed, 0, "executed");
336
337cleanup:
338	uprobe_syscall_executed__destroy(skel);
339	close(go[1]);
340	close(go[0]);
341}
342
343#define TRAMP "[uprobes-trampoline]"
344
345__attribute__((aligned(16)))
346__nocf_check __weak __naked void uprobe_test(void)
347{
348	asm volatile ("					\n"
349		".byte 0x0f, 0x1f, 0x44, 0x00, 0x00	\n"
350		"ret					\n"
351	);
352}
353
354__attribute__((aligned(16)))
355__nocf_check __weak void usdt_test(void)
356{
357	USDT(optimized_uprobe, usdt);
358}
359
360static int find_uprobes_trampoline(void *tramp_addr)
361{
362	void *start, *end;
363	char line[128];
364	int ret = -1;
365	FILE *maps;
366
367	maps = fopen("/proc/self/maps", "r");
368	if (!maps) {
369		fprintf(stderr, "cannot open maps\n");
370		return -1;
371	}
372
373	while (fgets(line, sizeof(line), maps)) {
374		int m = -1;
375
376		/* We care only about private r-x mappings. */
377		if (sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n", &start, &end, &m) != 2)
378			continue;
379		if (m < 0)
380			continue;
381		if (!strncmp(&line[m], TRAMP, sizeof(TRAMP)-1) && (start == tramp_addr)) {
382			ret = 0;
383			break;
384		}
385	}
386
387	fclose(maps);
388	return ret;
389}
390
391static unsigned char nop5[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
392
393static void *find_nop5(void *fn)
394{
395	int i;
396
397	for (i = 0; i < 10; i++) {
398		if (!memcmp(nop5, fn + i, 5))
399			return fn + i;
400	}
401	return NULL;
402}
403
404typedef void (__attribute__((nocf_check)) *trigger_t)(void);
405
406static void *check_attach(struct uprobe_syscall_executed *skel, trigger_t trigger,
407			  void *addr, int executed)
408{
409	struct __arch_relative_insn {
410		__u8 op;
411		__s32 raddr;
412	} __packed *call;
413	void *tramp = NULL;
414
415	/* Uprobe gets optimized after first trigger, so let's press twice. */
416	trigger();
417	trigger();
418
419	/* Make sure bpf program got executed.. */
420	ASSERT_EQ(skel->bss->executed, executed, "executed");
421
422	/* .. and check the trampoline is as expected. */
423	call = (struct __arch_relative_insn *) addr;
424	tramp = (void *) (call + 1) + call->raddr;
425	ASSERT_EQ(call->op, 0xe8, "call");
426	ASSERT_OK(find_uprobes_trampoline(tramp), "uprobes_trampoline");
427
428	return tramp;
429}
430
431static void check_detach(void *addr, void *tramp)
432{
433	/* [uprobes_trampoline] stays after detach */
434	ASSERT_OK(find_uprobes_trampoline(tramp), "uprobes_trampoline");
435	ASSERT_OK(memcmp(addr, nop5, 5), "nop5");
436}
437
438static void check(struct uprobe_syscall_executed *skel, struct bpf_link *link,
439		  trigger_t trigger, void *addr, int executed)
440{
441	void *tramp;
442
443	tramp = check_attach(skel, trigger, addr, executed);
444	bpf_link__destroy(link);
445	check_detach(addr, tramp);
446}
447
448static void test_uprobe_legacy(void)
449{
450	struct uprobe_syscall_executed *skel = NULL;
451	LIBBPF_OPTS(bpf_uprobe_opts, opts,
452		.retprobe = true,
453	);
454	struct bpf_link *link;
455	unsigned long offset;
456
457	offset = get_uprobe_offset(&uprobe_test);
458	if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
459		goto cleanup;
460
461	/* uprobe */
462	skel = uprobe_syscall_executed__open_and_load();
463	if (!ASSERT_OK_PTR(skel, "uprobe_syscall_executed__open_and_load"))
464		return;
465
466	skel->bss->pid = getpid();
467
468	link = bpf_program__attach_uprobe_opts(skel->progs.test_uprobe,
469				0, "/proc/self/exe", offset, NULL);
470	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_opts"))
471		goto cleanup;
472
473	check(skel, link, uprobe_test, uprobe_test, 2);
474
475	/* uretprobe */
476	skel->bss->executed = 0;
477
478	link = bpf_program__attach_uprobe_opts(skel->progs.test_uretprobe,
479				0, "/proc/self/exe", offset, &opts);
480	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_opts"))
481		goto cleanup;
482
483	check(skel, link, uprobe_test, uprobe_test, 2);
484
485cleanup:
486	uprobe_syscall_executed__destroy(skel);
487}
488
489static void test_uprobe_multi(void)
490{
491	struct uprobe_syscall_executed *skel = NULL;
492	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
493	struct bpf_link *link;
494	unsigned long offset;
495
496	offset = get_uprobe_offset(&uprobe_test);
497	if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
498		goto cleanup;
499
500	opts.offsets = &offset;
501	opts.cnt = 1;
502
503	skel = uprobe_syscall_executed__open_and_load();
504	if (!ASSERT_OK_PTR(skel, "uprobe_syscall_executed__open_and_load"))
505		return;
506
507	skel->bss->pid = getpid();
508
509	/* uprobe.multi */
510	link = bpf_program__attach_uprobe_multi(skel->progs.test_uprobe_multi,
511				0, "/proc/self/exe", NULL, &opts);
512	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
513		goto cleanup;
514
515	check(skel, link, uprobe_test, uprobe_test, 2);
516
517	/* uretprobe.multi */
518	skel->bss->executed = 0;
519	opts.retprobe = true;
520	link = bpf_program__attach_uprobe_multi(skel->progs.test_uretprobe_multi,
521				0, "/proc/self/exe", NULL, &opts);
522	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
523		goto cleanup;
524
525	check(skel, link, uprobe_test, uprobe_test, 2);
526
527cleanup:
528	uprobe_syscall_executed__destroy(skel);
529}
530
531static void test_uprobe_session(void)
532{
533	struct uprobe_syscall_executed *skel = NULL;
534	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts,
535		.session = true,
536	);
537	struct bpf_link *link;
538	unsigned long offset;
539
540	offset = get_uprobe_offset(&uprobe_test);
541	if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
542		goto cleanup;
543
544	opts.offsets = &offset;
545	opts.cnt = 1;
546
547	skel = uprobe_syscall_executed__open_and_load();
548	if (!ASSERT_OK_PTR(skel, "uprobe_syscall_executed__open_and_load"))
549		return;
550
551	skel->bss->pid = getpid();
552
553	link = bpf_program__attach_uprobe_multi(skel->progs.test_uprobe_session,
554				0, "/proc/self/exe", NULL, &opts);
555	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
556		goto cleanup;
557
558	check(skel, link, uprobe_test, uprobe_test, 4);
559
560cleanup:
561	uprobe_syscall_executed__destroy(skel);
562}
563
564static void test_uprobe_usdt(void)
565{
566	struct uprobe_syscall_executed *skel;
567	struct bpf_link *link;
568	void *addr;
569
570	errno = 0;
571	addr = find_nop5(usdt_test);
572	if (!ASSERT_OK_PTR(addr, "find_nop5"))
573		return;
574
575	skel = uprobe_syscall_executed__open_and_load();
576	if (!ASSERT_OK_PTR(skel, "uprobe_syscall_executed__open_and_load"))
577		return;
578
579	skel->bss->pid = getpid();
580
581	link = bpf_program__attach_usdt(skel->progs.test_usdt,
582				-1 /* all PIDs */, "/proc/self/exe",
583				"optimized_uprobe", "usdt", NULL);
584	if (!ASSERT_OK_PTR(link, "bpf_program__attach_usdt"))
585		goto cleanup;
586
587	check(skel, link, usdt_test, addr, 2);
588
589cleanup:
590	uprobe_syscall_executed__destroy(skel);
591}
592
593/*
594 * Borrowed from tools/testing/selftests/x86/test_shadow_stack.c.
595 *
596 * For use in inline enablement of shadow stack.
597 *
598 * The program can't return from the point where shadow stack gets enabled
599 * because there will be no address on the shadow stack. So it can't use
600 * syscall() for enablement, since it is a function.
601 *
602 * Based on code from nolibc.h. Keep a copy here because this can't pull
603 * in all of nolibc.h.
604 */
605#define ARCH_PRCTL(arg1, arg2)					\
606({								\
607	long _ret;						\
608	register long _num  asm("eax") = __NR_arch_prctl;	\
609	register long _arg1 asm("rdi") = (long)(arg1);		\
610	register long _arg2 asm("rsi") = (long)(arg2);		\
611								\
612	asm volatile (						\
613		"syscall\n"					\
614		: "=a"(_ret)					\
615		: "r"(_arg1), "r"(_arg2),			\
616		  "0"(_num)					\
617		: "rcx", "r11", "memory", "cc"			\
618	);							\
619	_ret;							\
620})
621
622#ifndef ARCH_SHSTK_ENABLE
623#define ARCH_SHSTK_ENABLE	0x5001
624#define ARCH_SHSTK_DISABLE	0x5002
625#define ARCH_SHSTK_SHSTK	(1ULL <<  0)
626#endif
627
628static void test_uretprobe_shadow_stack(void)
629{
630	if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) {
631		test__skip();
632		return;
633	}
634
635	/* Run all the tests with shadow stack in place. */
636
637	test_uprobe_regs_equal(false);
638	test_uprobe_regs_equal(true);
639	test_uretprobe_syscall_call();
640
641	test_uprobe_legacy();
642	test_uprobe_multi();
643	test_uprobe_session();
644	test_uprobe_usdt();
645
646	test_regs_change();
647
648	ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK);
649}
650
651static volatile bool race_stop;
652
653static USDT_DEFINE_SEMA(race);
654
655static void *worker_trigger(void *arg)
656{
657	unsigned long rounds = 0;
658
659	while (!race_stop) {
660		uprobe_test();
661		rounds++;
662	}
663
664	printf("tid %ld trigger rounds: %lu\n", sys_gettid(), rounds);
665	return NULL;
666}
667
668static void *worker_attach(void *arg)
669{
670	LIBBPF_OPTS(bpf_uprobe_opts, opts);
671	struct uprobe_syscall_executed *skel;
672	unsigned long rounds = 0, offset;
673	const char *sema[2] = {
674		__stringify(USDT_SEMA(race)),
675		NULL,
676	};
677	unsigned long *ref;
678	int err;
679
680	offset = get_uprobe_offset(&uprobe_test);
681	if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
682		return NULL;
683
684	err = elf_resolve_syms_offsets("/proc/self/exe", 1, (const char **) &sema, &ref, STT_OBJECT);
685	if (!ASSERT_OK(err, "elf_resolve_syms_offsets_sema"))
686		return NULL;
687
688	opts.ref_ctr_offset = *ref;
689
690	skel = uprobe_syscall_executed__open_and_load();
691	if (!ASSERT_OK_PTR(skel, "uprobe_syscall_executed__open_and_load"))
692		return NULL;
693
694	skel->bss->pid = getpid();
695
696	while (!race_stop) {
697		skel->links.test_uprobe = bpf_program__attach_uprobe_opts(skel->progs.test_uprobe,
698					0, "/proc/self/exe", offset, &opts);
699		if (!ASSERT_OK_PTR(skel->links.test_uprobe, "bpf_program__attach_uprobe_opts"))
700			break;
701
702		bpf_link__destroy(skel->links.test_uprobe);
703		skel->links.test_uprobe = NULL;
704		rounds++;
705	}
706
707	printf("tid %ld attach rounds: %lu hits: %d\n", sys_gettid(), rounds, skel->bss->executed);
708	uprobe_syscall_executed__destroy(skel);
709	free(ref);
710	return NULL;
711}
712
713static useconds_t race_msec(void)
714{
715	char *env;
716
717	env = getenv("BPF_SELFTESTS_UPROBE_SYSCALL_RACE_MSEC");
718	if (env)
719		return atoi(env);
720
721	/* default duration is 500ms */
722	return 500;
723}
724
725static void test_uprobe_race(void)
726{
727	int err, i, nr_threads;
728	pthread_t *threads;
729
730	nr_threads = libbpf_num_possible_cpus();
731	if (!ASSERT_GT(nr_threads, 0, "libbpf_num_possible_cpus"))
732		return;
733	nr_threads = max(2, nr_threads);
734
735	threads = alloca(sizeof(*threads) * nr_threads);
736	if (!ASSERT_OK_PTR(threads, "malloc"))
737		return;
738
739	for (i = 0; i < nr_threads; i++) {
740		err = pthread_create(&threads[i], NULL, i % 2 ? worker_trigger : worker_attach,
741				     NULL);
742		if (!ASSERT_OK(err, "pthread_create"))
743			goto cleanup;
744	}
745
746	usleep(race_msec() * 1000);
747
748cleanup:
749	race_stop = true;
750	for (nr_threads = i, i = 0; i < nr_threads; i++)
751		pthread_join(threads[i], NULL);
752
753	ASSERT_FALSE(USDT_SEMA_IS_ACTIVE(race), "race_semaphore");
754}
755
756#ifndef __NR_uprobe
757#define __NR_uprobe 336
758#endif
759
760static void test_uprobe_error(void)
761{
762	long err = syscall(__NR_uprobe);
763
764	ASSERT_EQ(err, -1, "error");
765	ASSERT_EQ(errno, ENXIO, "errno");
766}
767
768static void __test_uprobe_syscall(void)
769{
770	if (test__start_subtest("uretprobe_regs_equal"))
771		test_uprobe_regs_equal(true);
772	if (test__start_subtest("uretprobe_syscall_call"))
773		test_uretprobe_syscall_call();
774	if (test__start_subtest("uretprobe_shadow_stack"))
775		test_uretprobe_shadow_stack();
776	if (test__start_subtest("uprobe_legacy"))
777		test_uprobe_legacy();
778	if (test__start_subtest("uprobe_multi"))
779		test_uprobe_multi();
780	if (test__start_subtest("uprobe_session"))
781		test_uprobe_session();
782	if (test__start_subtest("uprobe_usdt"))
783		test_uprobe_usdt();
784	if (test__start_subtest("uprobe_race"))
785		test_uprobe_race();
786	if (test__start_subtest("uprobe_error"))
787		test_uprobe_error();
788	if (test__start_subtest("uprobe_regs_equal"))
789		test_uprobe_regs_equal(false);
790	if (test__start_subtest("regs_change"))
791		test_regs_change();
792}
793#else
794static void __test_uprobe_syscall(void)
795{
796	test__skip();
797}
798#endif
799
800void test_uprobe_syscall(void)
801{
802	__test_uprobe_syscall();
803}
~/ Read-only file preview.