xref: /linux/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c (revision ae22a94997b8a03dcb3c922857c203246711f9d4)
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 					 const char *exp_msg)
353 {
354 	/*
355 	 * standalone test that asserts failure to load freplace prog
356 	 * because of invalid return code.
357 	 */
358 	struct bpf_object *obj = NULL, *pkt_obj;
359 	struct bpf_program *prog;
360 	char log_buf[64 * 1024];
361 	int err, pkt_fd;
362 	__u32 duration = 0;
363 
364 	err = bpf_prog_test_load(target_obj_file, BPF_PROG_TYPE_UNSPEC,
365 			    &pkt_obj, &pkt_fd);
366 	/* the target prog should load fine */
367 	if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n",
368 		  target_obj_file, err, errno))
369 		return;
370 
371 	obj = bpf_object__open_file(obj_file, NULL);
372 	if (!ASSERT_OK_PTR(obj, "obj_open"))
373 		goto close_prog;
374 
375 	prog = bpf_object__next_program(obj, NULL);
376 	err = bpf_program__set_attach_target(prog, pkt_fd, NULL);
377 	ASSERT_OK(err, "set_attach_target");
378 
379 	log_buf[0] = '\0';
380 	if (exp_msg)
381 		bpf_program__set_log_buf(prog, log_buf, sizeof(log_buf));
382 	if (env.verbosity > VERBOSE_NONE)
383 		bpf_program__set_log_level(prog, 2);
384 
385 	/* It should fail to load the program */
386 	err = bpf_object__load(obj);
387 	if (env.verbosity > VERBOSE_NONE && exp_msg) /* we overtook log */
388 		printf("VERIFIER LOG:\n================\n%s\n================\n", log_buf);
389 	if (CHECK(!err, "bpf_obj_load should fail", "err %d\n", err))
390 		goto close_prog;
391 
392 	if (exp_msg)
393 		ASSERT_HAS_SUBSTR(log_buf, exp_msg, "fail_msg");
394 close_prog:
395 	bpf_object__close(obj);
396 	bpf_object__close(pkt_obj);
397 }
398 
399 static void test_func_replace_return_code(void)
400 {
401 	/* test invalid return code in the replaced program */
402 	test_obj_load_failure_common("./freplace_connect_v4_prog.bpf.o",
403 				     "./connect4_prog.bpf.o", NULL);
404 }
405 
406 static void test_func_map_prog_compatibility(void)
407 {
408 	/* test with spin lock map value in the replaced program */
409 	test_obj_load_failure_common("./freplace_attach_probe.bpf.o",
410 				     "./test_attach_probe.bpf.o", NULL);
411 }
412 
413 static void test_func_replace_unreliable(void)
414 {
415 	/* freplace'ing unreliable main prog should fail with error
416 	 * "Cannot replace static functions"
417 	 */
418 	test_obj_load_failure_common("freplace_unreliable_prog.bpf.o",
419 				     "./verifier_btf_unreliable_prog.bpf.o",
420 				     "Cannot replace static functions");
421 }
422 
423 static void test_func_replace_global_func(void)
424 {
425 	const char *prog_name[] = {
426 		"freplace/test_pkt_access",
427 	};
428 
429 	test_fexit_bpf2bpf_common("./freplace_global_func.bpf.o",
430 				  "./test_pkt_access.bpf.o",
431 				  ARRAY_SIZE(prog_name),
432 				  prog_name, false, NULL);
433 }
434 
435 static int find_prog_btf_id(const char *name, __u32 attach_prog_fd)
436 {
437 	struct bpf_prog_info info = {};
438 	__u32 info_len = sizeof(info);
439 	struct btf *btf;
440 	int ret;
441 
442 	ret = bpf_prog_get_info_by_fd(attach_prog_fd, &info, &info_len);
443 	if (ret)
444 		return ret;
445 
446 	if (!info.btf_id)
447 		return -EINVAL;
448 
449 	btf = btf__load_from_kernel_by_id(info.btf_id);
450 	ret = libbpf_get_error(btf);
451 	if (ret)
452 		return ret;
453 
454 	ret = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
455 	btf__free(btf);
456 	return ret;
457 }
458 
459 static int load_fentry(int attach_prog_fd, int attach_btf_id)
460 {
461 	LIBBPF_OPTS(bpf_prog_load_opts, opts,
462 		    .expected_attach_type = BPF_TRACE_FENTRY,
463 		    .attach_prog_fd = attach_prog_fd,
464 		    .attach_btf_id = attach_btf_id,
465 	);
466 	struct bpf_insn insns[] = {
467 		BPF_MOV64_IMM(BPF_REG_0, 0),
468 		BPF_EXIT_INSN(),
469 	};
470 
471 	return bpf_prog_load(BPF_PROG_TYPE_TRACING,
472 			     "bind4_fentry",
473 			     "GPL",
474 			     insns,
475 			     ARRAY_SIZE(insns),
476 			     &opts);
477 }
478 
479 static void test_fentry_to_cgroup_bpf(void)
480 {
481 	struct bind4_prog *skel = NULL;
482 	struct bpf_prog_info info = {};
483 	__u32 info_len = sizeof(info);
484 	int cgroup_fd = -1;
485 	int fentry_fd = -1;
486 	int btf_id;
487 
488 	cgroup_fd = test__join_cgroup("/fentry_to_cgroup_bpf");
489 	if (!ASSERT_GE(cgroup_fd, 0, "cgroup_fd"))
490 		return;
491 
492 	skel = bind4_prog__open_and_load();
493 	if (!ASSERT_OK_PTR(skel, "skel"))
494 		goto cleanup;
495 
496 	skel->links.bind_v4_prog = bpf_program__attach_cgroup(skel->progs.bind_v4_prog, cgroup_fd);
497 	if (!ASSERT_OK_PTR(skel->links.bind_v4_prog, "bpf_program__attach_cgroup"))
498 		goto cleanup;
499 
500 	btf_id = find_prog_btf_id("bind_v4_prog", bpf_program__fd(skel->progs.bind_v4_prog));
501 	if (!ASSERT_GE(btf_id, 0, "find_prog_btf_id"))
502 		goto cleanup;
503 
504 	fentry_fd = load_fentry(bpf_program__fd(skel->progs.bind_v4_prog), btf_id);
505 	if (!ASSERT_GE(fentry_fd, 0, "load_fentry"))
506 		goto cleanup;
507 
508 	/* Make sure bpf_prog_get_info_by_fd works correctly when attaching
509 	 * to another BPF program.
510 	 */
511 
512 	ASSERT_OK(bpf_prog_get_info_by_fd(fentry_fd, &info, &info_len),
513 		  "bpf_prog_get_info_by_fd");
514 
515 	ASSERT_EQ(info.btf_id, 0, "info.btf_id");
516 	ASSERT_EQ(info.attach_btf_id, btf_id, "info.attach_btf_id");
517 	ASSERT_GT(info.attach_btf_obj_id, 0, "info.attach_btf_obj_id");
518 
519 cleanup:
520 	if (cgroup_fd >= 0)
521 		close(cgroup_fd);
522 	if (fentry_fd >= 0)
523 		close(fentry_fd);
524 	bind4_prog__destroy(skel);
525 }
526 
527 static void test_func_replace_progmap(void)
528 {
529 	struct bpf_cpumap_val value = { .qsize = 1 };
530 	struct freplace_progmap *skel = NULL;
531 	struct xdp_dummy *tgt_skel = NULL;
532 	__u32 key = 0;
533 	int err;
534 
535 	skel = freplace_progmap__open();
536 	if (!ASSERT_OK_PTR(skel, "prog_open"))
537 		return;
538 
539 	tgt_skel = xdp_dummy__open_and_load();
540 	if (!ASSERT_OK_PTR(tgt_skel, "tgt_prog_load"))
541 		goto out;
542 
543 	err = bpf_program__set_attach_target(skel->progs.xdp_cpumap_prog,
544 					     bpf_program__fd(tgt_skel->progs.xdp_dummy_prog),
545 					     "xdp_dummy_prog");
546 	if (!ASSERT_OK(err, "set_attach_target"))
547 		goto out;
548 
549 	err = freplace_progmap__load(skel);
550 	if (!ASSERT_OK(err, "obj_load"))
551 		goto out;
552 
553 	/* Prior to fixing the kernel, loading the PROG_TYPE_EXT 'redirect'
554 	 * program above will cause the map owner type of 'cpumap' to be set to
555 	 * PROG_TYPE_EXT. This in turn will cause the bpf_map_update_elem()
556 	 * below to fail, because the program we are inserting into the map is
557 	 * of PROG_TYPE_XDP. After fixing the kernel, the initial ownership will
558 	 * be correctly resolved to the *target* of the PROG_TYPE_EXT program
559 	 * (i.e., PROG_TYPE_XDP) and the map update will succeed.
560 	 */
561 	value.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_drop_prog);
562 	err = bpf_map_update_elem(bpf_map__fd(skel->maps.cpu_map),
563 				  &key, &value, 0);
564 	ASSERT_OK(err, "map_update");
565 
566 out:
567 	xdp_dummy__destroy(tgt_skel);
568 	freplace_progmap__destroy(skel);
569 }
570 
571 /* NOTE: affect other tests, must run in serial mode */
572 void serial_test_fexit_bpf2bpf(void)
573 {
574 	if (test__start_subtest("target_no_callees"))
575 		test_target_no_callees();
576 	if (test__start_subtest("target_yes_callees"))
577 		test_target_yes_callees();
578 	if (test__start_subtest("func_replace"))
579 		test_func_replace();
580 	if (test__start_subtest("func_replace_verify"))
581 		test_func_replace_verify();
582 	if (test__start_subtest("func_sockmap_update"))
583 		test_func_sockmap_update();
584 	if (test__start_subtest("func_replace_return_code"))
585 		test_func_replace_return_code();
586 	if (test__start_subtest("func_map_prog_compatibility"))
587 		test_func_map_prog_compatibility();
588 	if (test__start_subtest("func_replace_unreliable"))
589 		test_func_replace_unreliable();
590 	if (test__start_subtest("func_replace_multi"))
591 		test_func_replace_multi();
592 	if (test__start_subtest("fmod_ret_freplace"))
593 		test_fmod_ret_freplace();
594 	if (test__start_subtest("func_replace_global_func"))
595 		test_func_replace_global_func();
596 	if (test__start_subtest("fentry_to_cgroup_bpf"))
597 		test_fentry_to_cgroup_bpf();
598 	if (test__start_subtest("func_replace_progmap"))
599 		test_func_replace_progmap();
600 }
601