xref: /linux/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c (revision 8e07e0e3964ca4e23ce7b68e2096fe660a888942)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019 Facebook */
3 #include <test_progs.h>
4 #include <network_helpers.h>
5 #include <bpf/btf.h>
6 #include "bind4_prog.skel.h"
7 #include "freplace_progmap.skel.h"
8 #include "xdp_dummy.skel.h"
9 
10 typedef int (*test_cb)(struct bpf_object *obj);
11 
12 static int check_data_map(struct bpf_object *obj, int prog_cnt, bool reset)
13 {
14 	struct bpf_map *data_map = NULL, *map;
15 	__u64 *result = NULL;
16 	const int zero = 0;
17 	__u32 duration = 0;
18 	int ret = -1, i;
19 
20 	result = malloc((prog_cnt + 32 /* spare */) * sizeof(__u64));
21 	if (CHECK(!result, "alloc_memory", "failed to alloc memory"))
22 		return -ENOMEM;
23 
24 	bpf_object__for_each_map(map, obj)
25 		if (bpf_map__is_internal(map)) {
26 			data_map = map;
27 			break;
28 		}
29 	if (CHECK(!data_map, "find_data_map", "data map not found\n"))
30 		goto out;
31 
32 	ret = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, result);
33 	if (CHECK(ret, "get_result",
34 		  "failed to get output data: %d\n", ret))
35 		goto out;
36 
37 	for (i = 0; i < prog_cnt; i++) {
38 		if (CHECK(result[i] != 1, "result",
39 			  "fexit_bpf2bpf result[%d] failed err %llu\n",
40 			  i, result[i]))
41 			goto out;
42 		result[i] = 0;
43 	}
44 	if (reset) {
45 		ret = bpf_map_update_elem(bpf_map__fd(data_map), &zero, result, 0);
46 		if (CHECK(ret, "reset_result", "failed to reset result\n"))
47 			goto out;
48 	}
49 
50 	ret = 0;
51 out:
52 	free(result);
53 	return ret;
54 }
55 
56 static void test_fexit_bpf2bpf_common(const char *obj_file,
57 				      const char *target_obj_file,
58 				      int prog_cnt,
59 				      const char **prog_name,
60 				      bool run_prog,
61 				      test_cb cb)
62 {
63 	struct bpf_object *obj = NULL, *tgt_obj;
64 	__u32 tgt_prog_id, info_len;
65 	struct bpf_prog_info prog_info = {};
66 	struct bpf_program **prog = NULL, *p;
67 	struct bpf_link **link = NULL;
68 	int err, tgt_fd, i;
69 	struct btf *btf;
70 	LIBBPF_OPTS(bpf_test_run_opts, topts,
71 		.data_in = &pkt_v6,
72 		.data_size_in = sizeof(pkt_v6),
73 		.repeat = 1,
74 	);
75 
76 	err = bpf_prog_test_load(target_obj_file, BPF_PROG_TYPE_UNSPEC,
77 			    &tgt_obj, &tgt_fd);
78 	if (!ASSERT_OK(err, "tgt_prog_load"))
79 		return;
80 
81 	info_len = sizeof(prog_info);
82 	err = bpf_prog_get_info_by_fd(tgt_fd, &prog_info, &info_len);
83 	if (!ASSERT_OK(err, "tgt_fd_get_info"))
84 		goto close_prog;
85 
86 	tgt_prog_id = prog_info.id;
87 	btf = bpf_object__btf(tgt_obj);
88 
89 	link = calloc(sizeof(struct bpf_link *), prog_cnt);
90 	if (!ASSERT_OK_PTR(link, "link_ptr"))
91 		goto close_prog;
92 
93 	prog = calloc(sizeof(struct bpf_program *), prog_cnt);
94 	if (!ASSERT_OK_PTR(prog, "prog_ptr"))
95 		goto close_prog;
96 
97 	obj = bpf_object__open_file(obj_file, NULL);
98 	if (!ASSERT_OK_PTR(obj, "obj_open"))
99 		goto close_prog;
100 
101 	bpf_object__for_each_program(p, obj) {
102 		err = bpf_program__set_attach_target(p, tgt_fd, NULL);
103 		ASSERT_OK(err, "set_attach_target");
104 	}
105 
106 	err = bpf_object__load(obj);
107 	if (!ASSERT_OK(err, "obj_load"))
108 		goto close_prog;
109 
110 	for (i = 0; i < prog_cnt; i++) {
111 		struct bpf_link_info link_info;
112 		struct bpf_program *pos;
113 		const char *pos_sec_name;
114 		char *tgt_name;
115 		__s32 btf_id;
116 
117 		tgt_name = strstr(prog_name[i], "/");
118 		if (!ASSERT_OK_PTR(tgt_name, "tgt_name"))
119 			goto close_prog;
120 		btf_id = btf__find_by_name_kind(btf, tgt_name + 1, BTF_KIND_FUNC);
121 
122 		prog[i] = NULL;
123 		bpf_object__for_each_program(pos, obj) {
124 			pos_sec_name = bpf_program__section_name(pos);
125 			if (pos_sec_name && !strcmp(pos_sec_name, prog_name[i])) {
126 				prog[i] = pos;
127 				break;
128 			}
129 		}
130 		if (!ASSERT_OK_PTR(prog[i], prog_name[i]))
131 			goto close_prog;
132 
133 		link[i] = bpf_program__attach_trace(prog[i]);
134 		if (!ASSERT_OK_PTR(link[i], "attach_trace"))
135 			goto close_prog;
136 
137 		info_len = sizeof(link_info);
138 		memset(&link_info, 0, sizeof(link_info));
139 		err = bpf_link_get_info_by_fd(bpf_link__fd(link[i]),
140 					      &link_info, &info_len);
141 		ASSERT_OK(err, "link_fd_get_info");
142 		ASSERT_EQ(link_info.tracing.attach_type,
143 			  bpf_program__expected_attach_type(prog[i]),
144 			  "link_attach_type");
145 		ASSERT_EQ(link_info.tracing.target_obj_id, tgt_prog_id, "link_tgt_obj_id");
146 		ASSERT_EQ(link_info.tracing.target_btf_id, btf_id, "link_tgt_btf_id");
147 	}
148 
149 	if (cb) {
150 		err = cb(obj);
151 		if (err)
152 			goto close_prog;
153 	}
154 
155 	if (!run_prog)
156 		goto close_prog;
157 
158 	err = bpf_prog_test_run_opts(tgt_fd, &topts);
159 	ASSERT_OK(err, "prog_run");
160 	ASSERT_EQ(topts.retval, 0, "prog_run_ret");
161 
162 	if (check_data_map(obj, prog_cnt, false))
163 		goto close_prog;
164 
165 close_prog:
166 	for (i = 0; i < prog_cnt; i++)
167 		bpf_link__destroy(link[i]);
168 	bpf_object__close(obj);
169 	bpf_object__close(tgt_obj);
170 	free(link);
171 	free(prog);
172 }
173 
174 static void test_target_no_callees(void)
175 {
176 	const char *prog_name[] = {
177 		"fexit/test_pkt_md_access",
178 	};
179 	test_fexit_bpf2bpf_common("./fexit_bpf2bpf_simple.bpf.o",
180 				  "./test_pkt_md_access.bpf.o",
181 				  ARRAY_SIZE(prog_name),
182 				  prog_name, true, NULL);
183 }
184 
185 static void test_target_yes_callees(void)
186 {
187 	const char *prog_name[] = {
188 		"fexit/test_pkt_access",
189 		"fexit/test_pkt_access_subprog1",
190 		"fexit/test_pkt_access_subprog2",
191 		"fexit/test_pkt_access_subprog3",
192 	};
193 	test_fexit_bpf2bpf_common("./fexit_bpf2bpf.bpf.o",
194 				  "./test_pkt_access.bpf.o",
195 				  ARRAY_SIZE(prog_name),
196 				  prog_name, true, NULL);
197 }
198 
199 static void test_func_replace(void)
200 {
201 	const char *prog_name[] = {
202 		"fexit/test_pkt_access",
203 		"fexit/test_pkt_access_subprog1",
204 		"fexit/test_pkt_access_subprog2",
205 		"fexit/test_pkt_access_subprog3",
206 		"freplace/get_skb_len",
207 		"freplace/get_skb_ifindex",
208 		"freplace/get_constant",
209 		"freplace/test_pkt_write_access_subprog",
210 	};
211 	test_fexit_bpf2bpf_common("./fexit_bpf2bpf.bpf.o",
212 				  "./test_pkt_access.bpf.o",
213 				  ARRAY_SIZE(prog_name),
214 				  prog_name, true, NULL);
215 }
216 
217 static void test_func_replace_verify(void)
218 {
219 	const char *prog_name[] = {
220 		"freplace/do_bind",
221 	};
222 	test_fexit_bpf2bpf_common("./freplace_connect4.bpf.o",
223 				  "./connect4_prog.bpf.o",
224 				  ARRAY_SIZE(prog_name),
225 				  prog_name, false, NULL);
226 }
227 
228 static int test_second_attach(struct bpf_object *obj)
229 {
230 	const char *prog_name = "security_new_get_constant";
231 	const char *tgt_name = "get_constant";
232 	const char *tgt_obj_file = "./test_pkt_access.bpf.o";
233 	struct bpf_program *prog = NULL;
234 	struct bpf_object *tgt_obj;
235 	struct bpf_link *link;
236 	int err = 0, tgt_fd;
237 	LIBBPF_OPTS(bpf_test_run_opts, topts,
238 		.data_in = &pkt_v6,
239 		.data_size_in = sizeof(pkt_v6),
240 		.repeat = 1,
241 	);
242 
243 	prog = bpf_object__find_program_by_name(obj, prog_name);
244 	if (!ASSERT_OK_PTR(prog, "find_prog"))
245 		return -ENOENT;
246 
247 	err = bpf_prog_test_load(tgt_obj_file, BPF_PROG_TYPE_UNSPEC,
248 			    &tgt_obj, &tgt_fd);
249 	if (!ASSERT_OK(err, "second_prog_load"))
250 		return err;
251 
252 	link = bpf_program__attach_freplace(prog, tgt_fd, tgt_name);
253 	if (!ASSERT_OK_PTR(link, "second_link"))
254 		goto out;
255 
256 	err = bpf_prog_test_run_opts(tgt_fd, &topts);
257 	if (!ASSERT_OK(err, "ipv6 test_run"))
258 		goto out;
259 	if (!ASSERT_OK(topts.retval, "ipv6 retval"))
260 		goto out;
261 
262 	err = check_data_map(obj, 1, true);
263 	if (err)
264 		goto out;
265 
266 out:
267 	bpf_link__destroy(link);
268 	bpf_object__close(tgt_obj);
269 	return err;
270 }
271 
272 static void test_func_replace_multi(void)
273 {
274 	const char *prog_name[] = {
275 		"freplace/get_constant",
276 	};
277 	test_fexit_bpf2bpf_common("./freplace_get_constant.bpf.o",
278 				  "./test_pkt_access.bpf.o",
279 				  ARRAY_SIZE(prog_name),
280 				  prog_name, true, test_second_attach);
281 }
282 
283 static void test_fmod_ret_freplace(void)
284 {
285 	struct bpf_object *freplace_obj = NULL, *pkt_obj, *fmod_obj = NULL;
286 	const char *freplace_name = "./freplace_get_constant.bpf.o";
287 	const char *fmod_ret_name = "./fmod_ret_freplace.bpf.o";
288 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
289 	const char *tgt_name = "./test_pkt_access.bpf.o";
290 	struct bpf_link *freplace_link = NULL;
291 	struct bpf_program *prog;
292 	__u32 duration = 0;
293 	int err, pkt_fd, attach_prog_fd;
294 
295 	err = bpf_prog_test_load(tgt_name, BPF_PROG_TYPE_UNSPEC,
296 			    &pkt_obj, &pkt_fd);
297 	/* the target prog should load fine */
298 	if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n",
299 		  tgt_name, err, errno))
300 		return;
301 
302 	freplace_obj = bpf_object__open_file(freplace_name, NULL);
303 	if (!ASSERT_OK_PTR(freplace_obj, "freplace_obj_open"))
304 		goto out;
305 
306 	prog = bpf_object__next_program(freplace_obj, NULL);
307 	err = bpf_program__set_attach_target(prog, pkt_fd, NULL);
308 	ASSERT_OK(err, "freplace__set_attach_target");
309 
310 	err = bpf_object__load(freplace_obj);
311 	if (CHECK(err, "freplace_obj_load", "err %d\n", err))
312 		goto out;
313 
314 	freplace_link = bpf_program__attach_trace(prog);
315 	if (!ASSERT_OK_PTR(freplace_link, "freplace_attach_trace"))
316 		goto out;
317 
318 	fmod_obj = bpf_object__open_file(fmod_ret_name, NULL);
319 	if (!ASSERT_OK_PTR(fmod_obj, "fmod_obj_open"))
320 		goto out;
321 
322 	attach_prog_fd = bpf_program__fd(prog);
323 	prog = bpf_object__next_program(fmod_obj, NULL);
324 	err = bpf_program__set_attach_target(prog, attach_prog_fd, NULL);
325 	ASSERT_OK(err, "fmod_ret_set_attach_target");
326 
327 	err = bpf_object__load(fmod_obj);
328 	if (CHECK(!err, "fmod_obj_load", "loading fmod_ret should fail\n"))
329 		goto out;
330 
331 out:
332 	bpf_link__destroy(freplace_link);
333 	bpf_object__close(freplace_obj);
334 	bpf_object__close(fmod_obj);
335 	bpf_object__close(pkt_obj);
336 }
337 
338 
339 static void test_func_sockmap_update(void)
340 {
341 	const char *prog_name[] = {
342 		"freplace/cls_redirect",
343 	};
344 	test_fexit_bpf2bpf_common("./freplace_cls_redirect.bpf.o",
345 				  "./test_cls_redirect.bpf.o",
346 				  ARRAY_SIZE(prog_name),
347 				  prog_name, false, NULL);
348 }
349 
350 static void test_obj_load_failure_common(const char *obj_file,
351 					 const char *target_obj_file)
352 {
353 	/*
354 	 * standalone test that asserts failure to load freplace prog
355 	 * because of invalid return code.
356 	 */
357 	struct bpf_object *obj = NULL, *pkt_obj;
358 	struct bpf_program *prog;
359 	int err, pkt_fd;
360 	__u32 duration = 0;
361 
362 	err = bpf_prog_test_load(target_obj_file, BPF_PROG_TYPE_UNSPEC,
363 			    &pkt_obj, &pkt_fd);
364 	/* the target prog should load fine */
365 	if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n",
366 		  target_obj_file, err, errno))
367 		return;
368 
369 	obj = bpf_object__open_file(obj_file, NULL);
370 	if (!ASSERT_OK_PTR(obj, "obj_open"))
371 		goto close_prog;
372 
373 	prog = bpf_object__next_program(obj, NULL);
374 	err = bpf_program__set_attach_target(prog, pkt_fd, NULL);
375 	ASSERT_OK(err, "set_attach_target");
376 
377 	/* It should fail to load the program */
378 	err = bpf_object__load(obj);
379 	if (CHECK(!err, "bpf_obj_load should fail", "err %d\n", err))
380 		goto close_prog;
381 
382 close_prog:
383 	bpf_object__close(obj);
384 	bpf_object__close(pkt_obj);
385 }
386 
387 static void test_func_replace_return_code(void)
388 {
389 	/* test invalid return code in the replaced program */
390 	test_obj_load_failure_common("./freplace_connect_v4_prog.bpf.o",
391 				     "./connect4_prog.bpf.o");
392 }
393 
394 static void test_func_map_prog_compatibility(void)
395 {
396 	/* test with spin lock map value in the replaced program */
397 	test_obj_load_failure_common("./freplace_attach_probe.bpf.o",
398 				     "./test_attach_probe.bpf.o");
399 }
400 
401 static void test_func_replace_global_func(void)
402 {
403 	const char *prog_name[] = {
404 		"freplace/test_pkt_access",
405 	};
406 
407 	test_fexit_bpf2bpf_common("./freplace_global_func.bpf.o",
408 				  "./test_pkt_access.bpf.o",
409 				  ARRAY_SIZE(prog_name),
410 				  prog_name, false, NULL);
411 }
412 
413 static int find_prog_btf_id(const char *name, __u32 attach_prog_fd)
414 {
415 	struct bpf_prog_info info = {};
416 	__u32 info_len = sizeof(info);
417 	struct btf *btf;
418 	int ret;
419 
420 	ret = bpf_prog_get_info_by_fd(attach_prog_fd, &info, &info_len);
421 	if (ret)
422 		return ret;
423 
424 	if (!info.btf_id)
425 		return -EINVAL;
426 
427 	btf = btf__load_from_kernel_by_id(info.btf_id);
428 	ret = libbpf_get_error(btf);
429 	if (ret)
430 		return ret;
431 
432 	ret = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
433 	btf__free(btf);
434 	return ret;
435 }
436 
437 static int load_fentry(int attach_prog_fd, int attach_btf_id)
438 {
439 	LIBBPF_OPTS(bpf_prog_load_opts, opts,
440 		    .expected_attach_type = BPF_TRACE_FENTRY,
441 		    .attach_prog_fd = attach_prog_fd,
442 		    .attach_btf_id = attach_btf_id,
443 	);
444 	struct bpf_insn insns[] = {
445 		BPF_MOV64_IMM(BPF_REG_0, 0),
446 		BPF_EXIT_INSN(),
447 	};
448 
449 	return bpf_prog_load(BPF_PROG_TYPE_TRACING,
450 			     "bind4_fentry",
451 			     "GPL",
452 			     insns,
453 			     ARRAY_SIZE(insns),
454 			     &opts);
455 }
456 
457 static void test_fentry_to_cgroup_bpf(void)
458 {
459 	struct bind4_prog *skel = NULL;
460 	struct bpf_prog_info info = {};
461 	__u32 info_len = sizeof(info);
462 	int cgroup_fd = -1;
463 	int fentry_fd = -1;
464 	int btf_id;
465 
466 	cgroup_fd = test__join_cgroup("/fentry_to_cgroup_bpf");
467 	if (!ASSERT_GE(cgroup_fd, 0, "cgroup_fd"))
468 		return;
469 
470 	skel = bind4_prog__open_and_load();
471 	if (!ASSERT_OK_PTR(skel, "skel"))
472 		goto cleanup;
473 
474 	skel->links.bind_v4_prog = bpf_program__attach_cgroup(skel->progs.bind_v4_prog, cgroup_fd);
475 	if (!ASSERT_OK_PTR(skel->links.bind_v4_prog, "bpf_program__attach_cgroup"))
476 		goto cleanup;
477 
478 	btf_id = find_prog_btf_id("bind_v4_prog", bpf_program__fd(skel->progs.bind_v4_prog));
479 	if (!ASSERT_GE(btf_id, 0, "find_prog_btf_id"))
480 		goto cleanup;
481 
482 	fentry_fd = load_fentry(bpf_program__fd(skel->progs.bind_v4_prog), btf_id);
483 	if (!ASSERT_GE(fentry_fd, 0, "load_fentry"))
484 		goto cleanup;
485 
486 	/* Make sure bpf_prog_get_info_by_fd works correctly when attaching
487 	 * to another BPF program.
488 	 */
489 
490 	ASSERT_OK(bpf_prog_get_info_by_fd(fentry_fd, &info, &info_len),
491 		  "bpf_prog_get_info_by_fd");
492 
493 	ASSERT_EQ(info.btf_id, 0, "info.btf_id");
494 	ASSERT_EQ(info.attach_btf_id, btf_id, "info.attach_btf_id");
495 	ASSERT_GT(info.attach_btf_obj_id, 0, "info.attach_btf_obj_id");
496 
497 cleanup:
498 	if (cgroup_fd >= 0)
499 		close(cgroup_fd);
500 	if (fentry_fd >= 0)
501 		close(fentry_fd);
502 	bind4_prog__destroy(skel);
503 }
504 
505 static void test_func_replace_progmap(void)
506 {
507 	struct bpf_cpumap_val value = { .qsize = 1 };
508 	struct freplace_progmap *skel = NULL;
509 	struct xdp_dummy *tgt_skel = NULL;
510 	__u32 key = 0;
511 	int err;
512 
513 	skel = freplace_progmap__open();
514 	if (!ASSERT_OK_PTR(skel, "prog_open"))
515 		return;
516 
517 	tgt_skel = xdp_dummy__open_and_load();
518 	if (!ASSERT_OK_PTR(tgt_skel, "tgt_prog_load"))
519 		goto out;
520 
521 	err = bpf_program__set_attach_target(skel->progs.xdp_cpumap_prog,
522 					     bpf_program__fd(tgt_skel->progs.xdp_dummy_prog),
523 					     "xdp_dummy_prog");
524 	if (!ASSERT_OK(err, "set_attach_target"))
525 		goto out;
526 
527 	err = freplace_progmap__load(skel);
528 	if (!ASSERT_OK(err, "obj_load"))
529 		goto out;
530 
531 	/* Prior to fixing the kernel, loading the PROG_TYPE_EXT 'redirect'
532 	 * program above will cause the map owner type of 'cpumap' to be set to
533 	 * PROG_TYPE_EXT. This in turn will cause the bpf_map_update_elem()
534 	 * below to fail, because the program we are inserting into the map is
535 	 * of PROG_TYPE_XDP. After fixing the kernel, the initial ownership will
536 	 * be correctly resolved to the *target* of the PROG_TYPE_EXT program
537 	 * (i.e., PROG_TYPE_XDP) and the map update will succeed.
538 	 */
539 	value.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_drop_prog);
540 	err = bpf_map_update_elem(bpf_map__fd(skel->maps.cpu_map),
541 				  &key, &value, 0);
542 	ASSERT_OK(err, "map_update");
543 
544 out:
545 	xdp_dummy__destroy(tgt_skel);
546 	freplace_progmap__destroy(skel);
547 }
548 
549 /* NOTE: affect other tests, must run in serial mode */
550 void serial_test_fexit_bpf2bpf(void)
551 {
552 	if (test__start_subtest("target_no_callees"))
553 		test_target_no_callees();
554 	if (test__start_subtest("target_yes_callees"))
555 		test_target_yes_callees();
556 	if (test__start_subtest("func_replace"))
557 		test_func_replace();
558 	if (test__start_subtest("func_replace_verify"))
559 		test_func_replace_verify();
560 	if (test__start_subtest("func_sockmap_update"))
561 		test_func_sockmap_update();
562 	if (test__start_subtest("func_replace_return_code"))
563 		test_func_replace_return_code();
564 	if (test__start_subtest("func_map_prog_compatibility"))
565 		test_func_map_prog_compatibility();
566 	if (test__start_subtest("func_replace_multi"))
567 		test_func_replace_multi();
568 	if (test__start_subtest("fmod_ret_freplace"))
569 		test_fmod_ret_freplace();
570 	if (test__start_subtest("func_replace_global_func"))
571 		test_func_replace_global_func();
572 	if (test__start_subtest("fentry_to_cgroup_bpf"))
573 		test_fentry_to_cgroup_bpf();
574 	if (test__start_subtest("func_replace_progmap"))
575 		test_func_replace_progmap();
576 }
577