1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include <network_helpers.h>
4 
5 #include "map_kptr.skel.h"
6 #include "map_kptr_fail.skel.h"
7 
8 static char log_buf[1024 * 1024];
9 
10 struct {
11 	const char *prog_name;
12 	const char *err_msg;
13 } map_kptr_fail_tests[] = {
14 	{ "size_not_bpf_dw", "kptr access size must be BPF_DW" },
15 	{ "non_const_var_off", "kptr access cannot have variable offset" },
16 	{ "non_const_var_off_kptr_xchg", "R1 doesn't have constant offset. kptr has to be" },
17 	{ "misaligned_access_write", "kptr access misaligned expected=8 off=7" },
18 	{ "misaligned_access_read", "kptr access misaligned expected=8 off=1" },
19 	{ "reject_var_off_store", "variable untrusted_ptr_ access var_off=(0x0; 0x1e0)" },
20 	{ "reject_bad_type_match", "invalid kptr access, R1 type=untrusted_ptr_prog_test_ref_kfunc" },
21 	{ "marked_as_untrusted_or_null", "R1 type=untrusted_ptr_or_null_ expected=percpu_ptr_" },
22 	{ "correct_btf_id_check_size", "access beyond struct prog_test_ref_kfunc at off 32 size 4" },
23 	{ "inherit_untrusted_on_walk", "R1 type=untrusted_ptr_ expected=percpu_ptr_" },
24 	{ "reject_kptr_xchg_on_unref", "off=8 kptr isn't referenced kptr" },
25 	{ "reject_kptr_get_no_map_val", "arg#0 expected pointer to map value" },
26 	{ "reject_kptr_get_no_null_map_val", "arg#0 expected pointer to map value" },
27 	{ "reject_kptr_get_no_kptr", "arg#0 no referenced kptr at map value offset=0" },
28 	{ "reject_kptr_get_on_unref", "arg#0 no referenced kptr at map value offset=8" },
29 	{ "reject_kptr_get_bad_type_match", "kernel function bpf_kfunc_call_test_kptr_get args#0" },
30 	{ "mark_ref_as_untrusted_or_null", "R1 type=untrusted_ptr_or_null_ expected=percpu_ptr_" },
31 	{ "reject_untrusted_store_to_ref", "store to referenced kptr disallowed" },
32 	{ "reject_bad_type_xchg", "invalid kptr access, R2 type=ptr_prog_test_ref_kfunc expected=ptr_prog_test_member" },
33 	{ "reject_untrusted_xchg", "R2 type=untrusted_ptr_ expected=ptr_" },
34 	{ "reject_member_of_ref_xchg", "invalid kptr access, R2 type=ptr_prog_test_ref_kfunc" },
35 	{ "reject_indirect_helper_access", "kptr cannot be accessed indirectly by helper" },
36 	{ "reject_indirect_global_func_access", "kptr cannot be accessed indirectly by helper" },
37 	{ "kptr_xchg_ref_state", "Unreleased reference id=5 alloc_insn=" },
38 	{ "kptr_get_ref_state", "Unreleased reference id=3 alloc_insn=" },
39 };
40 
41 static void test_map_kptr_fail_prog(const char *prog_name, const char *err_msg)
42 {
43 	LIBBPF_OPTS(bpf_object_open_opts, opts, .kernel_log_buf = log_buf,
44 						.kernel_log_size = sizeof(log_buf),
45 						.kernel_log_level = 1);
46 	struct map_kptr_fail *skel;
47 	struct bpf_program *prog;
48 	int ret;
49 
50 	skel = map_kptr_fail__open_opts(&opts);
51 	if (!ASSERT_OK_PTR(skel, "map_kptr_fail__open_opts"))
52 		return;
53 
54 	prog = bpf_object__find_program_by_name(skel->obj, prog_name);
55 	if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name"))
56 		goto end;
57 
58 	bpf_program__set_autoload(prog, true);
59 
60 	ret = map_kptr_fail__load(skel);
61 	if (!ASSERT_ERR(ret, "map_kptr__load must fail"))
62 		goto end;
63 
64 	if (!ASSERT_OK_PTR(strstr(log_buf, err_msg), "expected error message")) {
65 		fprintf(stderr, "Expected: %s\n", err_msg);
66 		fprintf(stderr, "Verifier: %s\n", log_buf);
67 	}
68 
69 end:
70 	map_kptr_fail__destroy(skel);
71 }
72 
73 static void test_map_kptr_fail(void)
74 {
75 	int i;
76 
77 	for (i = 0; i < ARRAY_SIZE(map_kptr_fail_tests); i++) {
78 		if (!test__start_subtest(map_kptr_fail_tests[i].prog_name))
79 			continue;
80 		test_map_kptr_fail_prog(map_kptr_fail_tests[i].prog_name,
81 					map_kptr_fail_tests[i].err_msg);
82 	}
83 }
84 
85 static void test_map_kptr_success(bool test_run)
86 {
87 	LIBBPF_OPTS(bpf_test_run_opts, opts,
88 		.data_in = &pkt_v4,
89 		.data_size_in = sizeof(pkt_v4),
90 		.repeat = 1,
91 	);
92 	struct map_kptr *skel;
93 	int key = 0, ret;
94 	char buf[16];
95 
96 	skel = map_kptr__open_and_load();
97 	if (!ASSERT_OK_PTR(skel, "map_kptr__open_and_load"))
98 		return;
99 
100 	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref), &opts);
101 	ASSERT_OK(ret, "test_map_kptr_ref refcount");
102 	ASSERT_OK(opts.retval, "test_map_kptr_ref retval");
103 	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref2), &opts);
104 	ASSERT_OK(ret, "test_map_kptr_ref2 refcount");
105 	ASSERT_OK(opts.retval, "test_map_kptr_ref2 retval");
106 
107 	if (test_run)
108 		return;
109 
110 	ret = bpf_map__update_elem(skel->maps.array_map,
111 				   &key, sizeof(key), buf, sizeof(buf), 0);
112 	ASSERT_OK(ret, "array_map update");
113 	ret = bpf_map__update_elem(skel->maps.array_map,
114 				   &key, sizeof(key), buf, sizeof(buf), 0);
115 	ASSERT_OK(ret, "array_map update2");
116 
117 	ret = bpf_map__update_elem(skel->maps.hash_map,
118 				   &key, sizeof(key), buf, sizeof(buf), 0);
119 	ASSERT_OK(ret, "hash_map update");
120 	ret = bpf_map__delete_elem(skel->maps.hash_map, &key, sizeof(key), 0);
121 	ASSERT_OK(ret, "hash_map delete");
122 
123 	ret = bpf_map__update_elem(skel->maps.hash_malloc_map,
124 				   &key, sizeof(key), buf, sizeof(buf), 0);
125 	ASSERT_OK(ret, "hash_malloc_map update");
126 	ret = bpf_map__delete_elem(skel->maps.hash_malloc_map, &key, sizeof(key), 0);
127 	ASSERT_OK(ret, "hash_malloc_map delete");
128 
129 	ret = bpf_map__update_elem(skel->maps.lru_hash_map,
130 				   &key, sizeof(key), buf, sizeof(buf), 0);
131 	ASSERT_OK(ret, "lru_hash_map update");
132 	ret = bpf_map__delete_elem(skel->maps.lru_hash_map, &key, sizeof(key), 0);
133 	ASSERT_OK(ret, "lru_hash_map delete");
134 
135 	map_kptr__destroy(skel);
136 }
137 
138 void test_map_kptr(void)
139 {
140 	if (test__start_subtest("success")) {
141 		test_map_kptr_success(false);
142 		/* Do test_run twice, so that we see refcount going back to 1
143 		 * after we leave it in map from first iteration.
144 		 */
145 		test_map_kptr_success(true);
146 	}
147 	test_map_kptr_fail();
148 }
149