xref: /linux/tools/testing/selftests/bpf/prog_tests/fill_link_info.c (revision eed4edda910fe34dfae8c6bfbcf57f4593a54295)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2023 Yafang Shao <laoar.shao@gmail.com> */
3 
4 #include <string.h>
5 #include <linux/bpf.h>
6 #include <linux/limits.h>
7 #include <test_progs.h>
8 #include "trace_helpers.h"
9 #include "test_fill_link_info.skel.h"
10 #include "bpf/libbpf_internal.h"
11 
12 #define TP_CAT "sched"
13 #define TP_NAME "sched_switch"
14 
15 static const char *kmulti_syms[] = {
16 	"bpf_fentry_test2",
17 	"bpf_fentry_test1",
18 	"bpf_fentry_test3",
19 };
20 #define KMULTI_CNT ARRAY_SIZE(kmulti_syms)
21 static __u64 kmulti_addrs[KMULTI_CNT];
22 
23 #define KPROBE_FUNC "bpf_fentry_test1"
24 static __u64 kprobe_addr;
25 
26 #define UPROBE_FILE "/proc/self/exe"
27 static ssize_t uprobe_offset;
28 /* uprobe attach point */
29 static noinline void uprobe_func(void)
30 {
31 	asm volatile ("");
32 }
33 
34 static int verify_perf_link_info(int fd, enum bpf_perf_event_type type, long addr,
35 				 ssize_t offset, ssize_t entry_offset)
36 {
37 	struct bpf_link_info info;
38 	__u32 len = sizeof(info);
39 	char buf[PATH_MAX];
40 	int err;
41 
42 	memset(&info, 0, sizeof(info));
43 	buf[0] = '\0';
44 
45 again:
46 	err = bpf_link_get_info_by_fd(fd, &info, &len);
47 	if (!ASSERT_OK(err, "get_link_info"))
48 		return -1;
49 
50 	if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_PERF_EVENT, "link_type"))
51 		return -1;
52 	if (!ASSERT_EQ(info.perf_event.type, type, "perf_type_match"))
53 		return -1;
54 
55 	switch (info.perf_event.type) {
56 	case BPF_PERF_EVENT_KPROBE:
57 	case BPF_PERF_EVENT_KRETPROBE:
58 		ASSERT_EQ(info.perf_event.kprobe.offset, offset, "kprobe_offset");
59 
60 		/* In case kernel.kptr_restrict is not permitted or MAX_SYMS is reached */
61 		if (addr)
62 			ASSERT_EQ(info.perf_event.kprobe.addr, addr + entry_offset,
63 				  "kprobe_addr");
64 
65 		if (!info.perf_event.kprobe.func_name) {
66 			ASSERT_EQ(info.perf_event.kprobe.name_len, 0, "name_len");
67 			info.perf_event.kprobe.func_name = ptr_to_u64(&buf);
68 			info.perf_event.kprobe.name_len = sizeof(buf);
69 			goto again;
70 		}
71 
72 		err = strncmp(u64_to_ptr(info.perf_event.kprobe.func_name), KPROBE_FUNC,
73 			      strlen(KPROBE_FUNC));
74 		ASSERT_EQ(err, 0, "cmp_kprobe_func_name");
75 		break;
76 	case BPF_PERF_EVENT_TRACEPOINT:
77 		if (!info.perf_event.tracepoint.tp_name) {
78 			ASSERT_EQ(info.perf_event.tracepoint.name_len, 0, "name_len");
79 			info.perf_event.tracepoint.tp_name = ptr_to_u64(&buf);
80 			info.perf_event.tracepoint.name_len = sizeof(buf);
81 			goto again;
82 		}
83 
84 		err = strncmp(u64_to_ptr(info.perf_event.tracepoint.tp_name), TP_NAME,
85 			      strlen(TP_NAME));
86 		ASSERT_EQ(err, 0, "cmp_tp_name");
87 		break;
88 	case BPF_PERF_EVENT_UPROBE:
89 	case BPF_PERF_EVENT_URETPROBE:
90 		ASSERT_EQ(info.perf_event.uprobe.offset, offset, "uprobe_offset");
91 
92 		if (!info.perf_event.uprobe.file_name) {
93 			ASSERT_EQ(info.perf_event.uprobe.name_len, 0, "name_len");
94 			info.perf_event.uprobe.file_name = ptr_to_u64(&buf);
95 			info.perf_event.uprobe.name_len = sizeof(buf);
96 			goto again;
97 		}
98 
99 		err = strncmp(u64_to_ptr(info.perf_event.uprobe.file_name), UPROBE_FILE,
100 			      strlen(UPROBE_FILE));
101 			ASSERT_EQ(err, 0, "cmp_file_name");
102 		break;
103 	default:
104 		err = -1;
105 		break;
106 	}
107 	return err;
108 }
109 
110 static void kprobe_fill_invalid_user_buffer(int fd)
111 {
112 	struct bpf_link_info info;
113 	__u32 len = sizeof(info);
114 	int err;
115 
116 	memset(&info, 0, sizeof(info));
117 
118 	info.perf_event.kprobe.func_name = 0x1; /* invalid address */
119 	err = bpf_link_get_info_by_fd(fd, &info, &len);
120 	ASSERT_EQ(err, -EINVAL, "invalid_buff_and_len");
121 
122 	info.perf_event.kprobe.name_len = 64;
123 	err = bpf_link_get_info_by_fd(fd, &info, &len);
124 	ASSERT_EQ(err, -EFAULT, "invalid_buff");
125 
126 	info.perf_event.kprobe.func_name = 0;
127 	err = bpf_link_get_info_by_fd(fd, &info, &len);
128 	ASSERT_EQ(err, -EINVAL, "invalid_len");
129 
130 	ASSERT_EQ(info.perf_event.kprobe.addr, 0, "func_addr");
131 	ASSERT_EQ(info.perf_event.kprobe.offset, 0, "func_offset");
132 	ASSERT_EQ(info.perf_event.type, 0, "type");
133 }
134 
135 static void test_kprobe_fill_link_info(struct test_fill_link_info *skel,
136 				       enum bpf_perf_event_type type,
137 				       bool invalid)
138 {
139 	DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts,
140 		.attach_mode = PROBE_ATTACH_MODE_LINK,
141 		.retprobe = type == BPF_PERF_EVENT_KRETPROBE,
142 	);
143 	ssize_t entry_offset = 0;
144 	struct bpf_link *link;
145 	int link_fd, err;
146 
147 	link = bpf_program__attach_kprobe_opts(skel->progs.kprobe_run, KPROBE_FUNC, &opts);
148 	if (!ASSERT_OK_PTR(link, "attach_kprobe"))
149 		return;
150 
151 	link_fd = bpf_link__fd(link);
152 	if (!invalid) {
153 		/* See also arch_adjust_kprobe_addr(). */
154 		if (skel->kconfig->CONFIG_X86_KERNEL_IBT)
155 			entry_offset = 4;
156 		err = verify_perf_link_info(link_fd, type, kprobe_addr, 0, entry_offset);
157 		ASSERT_OK(err, "verify_perf_link_info");
158 	} else {
159 		kprobe_fill_invalid_user_buffer(link_fd);
160 	}
161 	bpf_link__destroy(link);
162 }
163 
164 static void test_tp_fill_link_info(struct test_fill_link_info *skel)
165 {
166 	struct bpf_link *link;
167 	int link_fd, err;
168 
169 	link = bpf_program__attach_tracepoint(skel->progs.tp_run, TP_CAT, TP_NAME);
170 	if (!ASSERT_OK_PTR(link, "attach_tp"))
171 		return;
172 
173 	link_fd = bpf_link__fd(link);
174 	err = verify_perf_link_info(link_fd, BPF_PERF_EVENT_TRACEPOINT, 0, 0, 0);
175 	ASSERT_OK(err, "verify_perf_link_info");
176 	bpf_link__destroy(link);
177 }
178 
179 static void test_uprobe_fill_link_info(struct test_fill_link_info *skel,
180 				       enum bpf_perf_event_type type)
181 {
182 	struct bpf_link *link;
183 	int link_fd, err;
184 
185 	link = bpf_program__attach_uprobe(skel->progs.uprobe_run,
186 					  type == BPF_PERF_EVENT_URETPROBE,
187 					  0, /* self pid */
188 					  UPROBE_FILE, uprobe_offset);
189 	if (!ASSERT_OK_PTR(link, "attach_uprobe"))
190 		return;
191 
192 	link_fd = bpf_link__fd(link);
193 	err = verify_perf_link_info(link_fd, type, 0, uprobe_offset, 0);
194 	ASSERT_OK(err, "verify_perf_link_info");
195 	bpf_link__destroy(link);
196 }
197 
198 static int verify_kmulti_link_info(int fd, bool retprobe)
199 {
200 	struct bpf_link_info info;
201 	__u32 len = sizeof(info);
202 	__u64 addrs[KMULTI_CNT];
203 	int flags, i, err;
204 
205 	memset(&info, 0, sizeof(info));
206 
207 again:
208 	err = bpf_link_get_info_by_fd(fd, &info, &len);
209 	if (!ASSERT_OK(err, "get_link_info"))
210 		return -1;
211 
212 	if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_KPROBE_MULTI, "kmulti_type"))
213 		return -1;
214 
215 	ASSERT_EQ(info.kprobe_multi.count, KMULTI_CNT, "func_cnt");
216 	flags = info.kprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN;
217 	if (!retprobe)
218 		ASSERT_EQ(flags, 0, "kmulti_flags");
219 	else
220 		ASSERT_NEQ(flags, 0, "kretmulti_flags");
221 
222 	if (!info.kprobe_multi.addrs) {
223 		info.kprobe_multi.addrs = ptr_to_u64(addrs);
224 		goto again;
225 	}
226 	for (i = 0; i < KMULTI_CNT; i++)
227 		ASSERT_EQ(addrs[i], kmulti_addrs[i], "kmulti_addrs");
228 	return 0;
229 }
230 
231 static void verify_kmulti_invalid_user_buffer(int fd)
232 {
233 	struct bpf_link_info info;
234 	__u32 len = sizeof(info);
235 	__u64 addrs[KMULTI_CNT];
236 	int err, i;
237 
238 	memset(&info, 0, sizeof(info));
239 
240 	info.kprobe_multi.count = KMULTI_CNT;
241 	err = bpf_link_get_info_by_fd(fd, &info, &len);
242 	ASSERT_EQ(err, -EINVAL, "no_addr");
243 
244 	info.kprobe_multi.addrs = ptr_to_u64(addrs);
245 	info.kprobe_multi.count = 0;
246 	err = bpf_link_get_info_by_fd(fd, &info, &len);
247 	ASSERT_EQ(err, -EINVAL, "no_cnt");
248 
249 	for (i = 0; i < KMULTI_CNT; i++)
250 		addrs[i] = 0;
251 	info.kprobe_multi.count = KMULTI_CNT - 1;
252 	err = bpf_link_get_info_by_fd(fd, &info, &len);
253 	ASSERT_EQ(err, -ENOSPC, "smaller_cnt");
254 	for (i = 0; i < KMULTI_CNT - 1; i++)
255 		ASSERT_EQ(addrs[i], kmulti_addrs[i], "kmulti_addrs");
256 	ASSERT_EQ(addrs[i], 0, "kmulti_addrs");
257 
258 	for (i = 0; i < KMULTI_CNT; i++)
259 		addrs[i] = 0;
260 	info.kprobe_multi.count = KMULTI_CNT + 1;
261 	err = bpf_link_get_info_by_fd(fd, &info, &len);
262 	ASSERT_EQ(err, 0, "bigger_cnt");
263 	for (i = 0; i < KMULTI_CNT; i++)
264 		ASSERT_EQ(addrs[i], kmulti_addrs[i], "kmulti_addrs");
265 
266 	info.kprobe_multi.count = KMULTI_CNT;
267 	info.kprobe_multi.addrs = 0x1; /* invalid addr */
268 	err = bpf_link_get_info_by_fd(fd, &info, &len);
269 	ASSERT_EQ(err, -EFAULT, "invalid_buff");
270 }
271 
272 static int symbols_cmp_r(const void *a, const void *b)
273 {
274 	const char **str_a = (const char **) a;
275 	const char **str_b = (const char **) b;
276 
277 	return strcmp(*str_a, *str_b);
278 }
279 
280 static void test_kprobe_multi_fill_link_info(struct test_fill_link_info *skel,
281 					     bool retprobe, bool invalid)
282 {
283 	LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
284 	struct bpf_link *link;
285 	int link_fd, err;
286 
287 	opts.syms = kmulti_syms;
288 	opts.cnt = KMULTI_CNT;
289 	opts.retprobe = retprobe;
290 	link = bpf_program__attach_kprobe_multi_opts(skel->progs.kmulti_run, NULL, &opts);
291 	if (!ASSERT_OK_PTR(link, "attach_kprobe_multi"))
292 		return;
293 
294 	link_fd = bpf_link__fd(link);
295 	if (!invalid) {
296 		err = verify_kmulti_link_info(link_fd, retprobe);
297 		ASSERT_OK(err, "verify_kmulti_link_info");
298 	} else {
299 		verify_kmulti_invalid_user_buffer(link_fd);
300 	}
301 	bpf_link__destroy(link);
302 }
303 
304 #define SEC(name) __attribute__((section(name), used))
305 
306 static short uprobe_link_info_sema_1 SEC(".probes");
307 static short uprobe_link_info_sema_2 SEC(".probes");
308 static short uprobe_link_info_sema_3 SEC(".probes");
309 
310 noinline void uprobe_link_info_func_1(void)
311 {
312 	asm volatile ("");
313 	uprobe_link_info_sema_1++;
314 }
315 
316 noinline void uprobe_link_info_func_2(void)
317 {
318 	asm volatile ("");
319 	uprobe_link_info_sema_2++;
320 }
321 
322 noinline void uprobe_link_info_func_3(void)
323 {
324 	asm volatile ("");
325 	uprobe_link_info_sema_3++;
326 }
327 
328 static int
329 verify_umulti_link_info(int fd, bool retprobe, __u64 *offsets,
330 			__u64 *cookies, __u64 *ref_ctr_offsets)
331 {
332 	char path[PATH_MAX], path_buf[PATH_MAX];
333 	struct bpf_link_info info;
334 	__u32 len = sizeof(info);
335 	__u64 ref_ctr_offsets_buf[3];
336 	__u64 offsets_buf[3];
337 	__u64 cookies_buf[3];
338 	int i, err, bit;
339 	__u32 count = 0;
340 
341 	memset(path, 0, sizeof(path));
342 	err = readlink("/proc/self/exe", path, sizeof(path));
343 	if (!ASSERT_NEQ(err, -1, "readlink"))
344 		return -1;
345 
346 	for (bit = 0; bit < 8; bit++) {
347 		memset(&info, 0, sizeof(info));
348 		info.uprobe_multi.path = ptr_to_u64(path_buf);
349 		info.uprobe_multi.path_size = sizeof(path_buf);
350 		info.uprobe_multi.count = count;
351 
352 		if (bit & 0x1)
353 			info.uprobe_multi.offsets = ptr_to_u64(offsets_buf);
354 		if (bit & 0x2)
355 			info.uprobe_multi.cookies = ptr_to_u64(cookies_buf);
356 		if (bit & 0x4)
357 			info.uprobe_multi.ref_ctr_offsets = ptr_to_u64(ref_ctr_offsets_buf);
358 
359 		err = bpf_link_get_info_by_fd(fd, &info, &len);
360 		if (!ASSERT_OK(err, "bpf_link_get_info_by_fd"))
361 			return -1;
362 
363 		if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_UPROBE_MULTI, "info.type"))
364 			return -1;
365 
366 		ASSERT_EQ(info.uprobe_multi.pid, getpid(), "info.uprobe_multi.pid");
367 		ASSERT_EQ(info.uprobe_multi.count, 3, "info.uprobe_multi.count");
368 		ASSERT_EQ(info.uprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN,
369 			  retprobe, "info.uprobe_multi.flags.retprobe");
370 		ASSERT_EQ(info.uprobe_multi.path_size, strlen(path) + 1, "info.uprobe_multi.path_size");
371 		ASSERT_STREQ(path_buf, path, "info.uprobe_multi.path");
372 
373 		for (i = 0; i < info.uprobe_multi.count; i++) {
374 			if (info.uprobe_multi.offsets)
375 				ASSERT_EQ(offsets_buf[i], offsets[i], "info.uprobe_multi.offsets");
376 			if (info.uprobe_multi.cookies)
377 				ASSERT_EQ(cookies_buf[i], cookies[i], "info.uprobe_multi.cookies");
378 			if (info.uprobe_multi.ref_ctr_offsets) {
379 				ASSERT_EQ(ref_ctr_offsets_buf[i], ref_ctr_offsets[i],
380 					  "info.uprobe_multi.ref_ctr_offsets");
381 			}
382 		}
383 		count = count ?: info.uprobe_multi.count;
384 	}
385 
386 	return 0;
387 }
388 
389 static void verify_umulti_invalid_user_buffer(int fd)
390 {
391 	struct bpf_link_info info;
392 	__u32 len = sizeof(info);
393 	__u64 buf[3];
394 	int err;
395 
396 	/* upath_size defined, not path */
397 	memset(&info, 0, sizeof(info));
398 	info.uprobe_multi.path_size = 3;
399 	err = bpf_link_get_info_by_fd(fd, &info, &len);
400 	ASSERT_EQ(err, -EINVAL, "failed_upath_size");
401 
402 	/* path defined, but small */
403 	memset(&info, 0, sizeof(info));
404 	info.uprobe_multi.path = ptr_to_u64(buf);
405 	info.uprobe_multi.path_size = 3;
406 	err = bpf_link_get_info_by_fd(fd, &info, &len);
407 	ASSERT_LT(err, 0, "failed_upath_small");
408 
409 	/* path has wrong pointer */
410 	memset(&info, 0, sizeof(info));
411 	info.uprobe_multi.path_size = PATH_MAX;
412 	info.uprobe_multi.path = 123;
413 	err = bpf_link_get_info_by_fd(fd, &info, &len);
414 	ASSERT_EQ(err, -EFAULT, "failed_bad_path_ptr");
415 
416 	/* count zero, with offsets */
417 	memset(&info, 0, sizeof(info));
418 	info.uprobe_multi.offsets = ptr_to_u64(buf);
419 	err = bpf_link_get_info_by_fd(fd, &info, &len);
420 	ASSERT_EQ(err, -EINVAL, "failed_count");
421 
422 	/* offsets not big enough */
423 	memset(&info, 0, sizeof(info));
424 	info.uprobe_multi.offsets = ptr_to_u64(buf);
425 	info.uprobe_multi.count = 2;
426 	err = bpf_link_get_info_by_fd(fd, &info, &len);
427 	ASSERT_EQ(err, -ENOSPC, "failed_small_count");
428 
429 	/* offsets has wrong pointer */
430 	memset(&info, 0, sizeof(info));
431 	info.uprobe_multi.offsets = 123;
432 	info.uprobe_multi.count = 3;
433 	err = bpf_link_get_info_by_fd(fd, &info, &len);
434 	ASSERT_EQ(err, -EFAULT, "failed_wrong_offsets");
435 }
436 
437 static void test_uprobe_multi_fill_link_info(struct test_fill_link_info *skel,
438 					     bool retprobe, bool invalid)
439 {
440 	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts,
441 		.retprobe = retprobe,
442 	);
443 	const char *syms[3] = {
444 		"uprobe_link_info_func_1",
445 		"uprobe_link_info_func_2",
446 		"uprobe_link_info_func_3",
447 	};
448 	__u64 cookies[3] = {
449 		0xdead,
450 		0xbeef,
451 		0xcafe,
452 	};
453 	const char *sema[3] = {
454 		"uprobe_link_info_sema_1",
455 		"uprobe_link_info_sema_2",
456 		"uprobe_link_info_sema_3",
457 	};
458 	__u64 *offsets = NULL, *ref_ctr_offsets;
459 	struct bpf_link *link;
460 	int link_fd, err;
461 
462 	err = elf_resolve_syms_offsets("/proc/self/exe", 3, sema,
463 				       (unsigned long **) &ref_ctr_offsets, STT_OBJECT);
464 	if (!ASSERT_OK(err, "elf_resolve_syms_offsets_object"))
465 		return;
466 
467 	err = elf_resolve_syms_offsets("/proc/self/exe", 3, syms,
468 				       (unsigned long **) &offsets, STT_FUNC);
469 	if (!ASSERT_OK(err, "elf_resolve_syms_offsets_func"))
470 		goto out;
471 
472 	opts.syms = syms;
473 	opts.cookies = &cookies[0];
474 	opts.ref_ctr_offsets = (unsigned long *) &ref_ctr_offsets[0];
475 	opts.cnt = ARRAY_SIZE(syms);
476 
477 	link = bpf_program__attach_uprobe_multi(skel->progs.umulti_run, 0,
478 						"/proc/self/exe", NULL, &opts);
479 	if (!ASSERT_OK_PTR(link, "bpf_program__attach_uprobe_multi"))
480 		goto out;
481 
482 	link_fd = bpf_link__fd(link);
483 	if (invalid)
484 		verify_umulti_invalid_user_buffer(link_fd);
485 	else
486 		verify_umulti_link_info(link_fd, retprobe, offsets, cookies, ref_ctr_offsets);
487 
488 	bpf_link__destroy(link);
489 out:
490 	free(ref_ctr_offsets);
491 	free(offsets);
492 }
493 
494 void test_fill_link_info(void)
495 {
496 	struct test_fill_link_info *skel;
497 	int i;
498 
499 	skel = test_fill_link_info__open_and_load();
500 	if (!ASSERT_OK_PTR(skel, "skel_open"))
501 		return;
502 
503 	/* load kallsyms to compare the addr */
504 	if (!ASSERT_OK(load_kallsyms(), "load_kallsyms"))
505 		goto cleanup;
506 
507 	kprobe_addr = ksym_get_addr(KPROBE_FUNC);
508 	if (test__start_subtest("kprobe_link_info"))
509 		test_kprobe_fill_link_info(skel, BPF_PERF_EVENT_KPROBE, false);
510 	if (test__start_subtest("kretprobe_link_info"))
511 		test_kprobe_fill_link_info(skel, BPF_PERF_EVENT_KRETPROBE, false);
512 	if (test__start_subtest("kprobe_invalid_ubuff"))
513 		test_kprobe_fill_link_info(skel, BPF_PERF_EVENT_KPROBE, true);
514 	if (test__start_subtest("tracepoint_link_info"))
515 		test_tp_fill_link_info(skel);
516 
517 	uprobe_offset = get_uprobe_offset(&uprobe_func);
518 	if (test__start_subtest("uprobe_link_info"))
519 		test_uprobe_fill_link_info(skel, BPF_PERF_EVENT_UPROBE);
520 	if (test__start_subtest("uretprobe_link_info"))
521 		test_uprobe_fill_link_info(skel, BPF_PERF_EVENT_URETPROBE);
522 
523 	qsort(kmulti_syms, KMULTI_CNT, sizeof(kmulti_syms[0]), symbols_cmp_r);
524 	for (i = 0; i < KMULTI_CNT; i++)
525 		kmulti_addrs[i] = ksym_get_addr(kmulti_syms[i]);
526 	if (test__start_subtest("kprobe_multi_link_info"))
527 		test_kprobe_multi_fill_link_info(skel, false, false);
528 	if (test__start_subtest("kretprobe_multi_link_info"))
529 		test_kprobe_multi_fill_link_info(skel, true, false);
530 	if (test__start_subtest("kprobe_multi_invalid_ubuff"))
531 		test_kprobe_multi_fill_link_info(skel, true, true);
532 
533 	if (test__start_subtest("uprobe_multi_link_info"))
534 		test_uprobe_multi_fill_link_info(skel, false, false);
535 	if (test__start_subtest("uretprobe_multi_link_info"))
536 		test_uprobe_multi_fill_link_info(skel, true, false);
537 	if (test__start_subtest("uprobe_multi_invalid"))
538 		test_uprobe_multi_fill_link_info(skel, false, true);
539 
540 cleanup:
541 	test_fill_link_info__destroy(skel);
542 }
543