xref: /linux/tools/testing/selftests/bpf/prog_tests/bpftool_maps_access.c (revision 69050f8d6d075dc01af7a5f2f550a8067510366f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <stdint.h>
7 #include <sys/stat.h>
8 #include <stdbool.h>
9 #include <linux/bpf.h>
10 #include <bpf/libbpf.h>
11 #include <bpftool_helpers.h>
12 #include <test_progs.h>
13 #include <bpf/bpf.h>
14 #include "security_bpf_map.skel.h"
15 
16 #define PROTECTED_MAP_NAME	"prot_map"
17 #define UNPROTECTED_MAP_NAME	"not_prot_map"
18 #define BPF_ITER_FILE		"bpf_iter_map_elem.bpf.o"
19 #define BPFFS_PIN_DIR		"/sys/fs/bpf/test_bpftool_map"
20 #define INNER_MAP_NAME		"inner_map_tt"
21 #define OUTER_MAP_NAME		"outer_map_tt"
22 
23 #define MAP_NAME_MAX_LEN	64
24 #define PATH_MAX_LEN		128
25 
26 enum map_protection {
27 	PROTECTED,
28 	UNPROTECTED
29 };
30 
31 struct test_desc {
32 	char *name;
33 	enum map_protection protection;
34 	struct bpf_map *map;
35 	char *map_name;
36 	bool pinned;
37 	char pin_path[PATH_MAX_LEN];
38 	bool write_must_fail;
39 };
40 
41 static struct security_bpf_map *general_setup(void)
42 {
43 	struct security_bpf_map *skel;
44 	uint32_t key, value;
45 	int ret, i;
46 
47 	skel = security_bpf_map__open_and_load();
48 	if (!ASSERT_OK_PTR(skel, "open and load skeleton"))
49 		goto end;
50 
51 	struct bpf_map *maps[] = {skel->maps.prot_map, skel->maps.not_prot_map};
52 
53 	ret = security_bpf_map__attach(skel);
54 	if (!ASSERT_OK(ret, "attach maps security programs"))
55 		goto end_destroy;
56 
57 	for (i = 0; i < sizeof(maps)/sizeof(struct bpf_map *); i++) {
58 		for (key = 0; key < 2; key++) {
59 			int ret = bpf_map__update_elem(maps[i], &key,
60 					sizeof(key), &key, sizeof(key),
61 					0);
62 			if (!ASSERT_OK(ret, "set initial map value"))
63 				goto end_destroy;
64 		}
65 	}
66 
67 	key = 0;
68 	value = 1;
69 	ret = bpf_map__update_elem(skel->maps.prot_status_map, &key,
70 			sizeof(key), &value, sizeof(value), 0);
71 	if (!ASSERT_OK(ret, "configure map protection"))
72 		goto end_destroy;
73 
74 	if (!ASSERT_OK(mkdir(BPFFS_PIN_DIR, S_IFDIR), "create bpffs pin dir"))
75 		goto end_destroy;
76 
77 	return skel;
78 end_destroy:
79 	security_bpf_map__destroy(skel);
80 end:
81 	return NULL;
82 }
83 
84 static void general_cleanup(struct security_bpf_map *skel)
85 {
86 	rmdir(BPFFS_PIN_DIR);
87 	security_bpf_map__destroy(skel);
88 }
89 
90 static void update_test_desc(struct security_bpf_map *skel,
91 			      struct test_desc *test)
92 {
93 	/* Now that the skeleton is loaded, update all missing fields to
94 	 * have the subtest properly configured
95 	 */
96 	if (test->protection == PROTECTED) {
97 		test->map = skel->maps.prot_map;
98 		test->map_name = PROTECTED_MAP_NAME;
99 	} else {
100 		test->map = skel->maps.not_prot_map;
101 		test->map_name = UNPROTECTED_MAP_NAME;
102 	}
103 }
104 
105 static int test_setup(struct security_bpf_map *skel, struct test_desc *desc)
106 {
107 	int ret;
108 
109 	update_test_desc(skel, desc);
110 
111 	if (desc->pinned) {
112 		ret = snprintf(desc->pin_path, PATH_MAX_LEN, "%s/%s", BPFFS_PIN_DIR,
113 				desc->name);
114 		if (!ASSERT_GT(ret, 0, "format pin path"))
115 			return 1;
116 		ret = bpf_map__pin(desc->map, desc->pin_path);
117 		if (!ASSERT_OK(ret, "pin map"))
118 			return 1;
119 	}
120 
121 	return 0;
122 }
123 
124 static void test_cleanup(struct test_desc *desc)
125 {
126 	if (desc->pinned)
127 		bpf_map__unpin(desc->map, NULL);
128 }
129 
130 static int lookup_map_value(char *map_handle)
131 {
132 	char cmd[MAX_BPFTOOL_CMD_LEN];
133 	int ret = 0;
134 
135 	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "map lookup %s key 0 0 0 0",
136 			map_handle);
137 	if (!ASSERT_GT(ret, 0, "format map lookup cmd"))
138 		return 1;
139 	return run_bpftool_command(cmd);
140 }
141 
142 static int read_map_btf_data(char *map_handle)
143 {
144 	char cmd[MAX_BPFTOOL_CMD_LEN];
145 	int ret = 0;
146 
147 	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "btf dump map %s",
148 			map_handle);
149 	if (!ASSERT_GT(ret, 0, "format map btf dump cmd"))
150 		return 1;
151 	return run_bpftool_command(cmd);
152 }
153 
154 static int write_map_value(char *map_handle)
155 {
156 	char cmd[MAX_BPFTOOL_CMD_LEN];
157 	int ret = 0;
158 
159 	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN,
160 		       "map update %s key 0 0 0 0 value 1 1 1 1", map_handle);
161 	if (!ASSERT_GT(ret, 0, "format value write cmd"))
162 		return 1;
163 	return run_bpftool_command(cmd);
164 }
165 
166 static int delete_map_value(char *map_handle)
167 {
168 	char cmd[MAX_BPFTOOL_CMD_LEN];
169 	int ret = 0;
170 
171 	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN,
172 		       "map delete %s key 0 0 0 0", map_handle);
173 	if (!ASSERT_GT(ret, 0, "format value deletion cmd"))
174 		return 1;
175 	return run_bpftool_command(cmd);
176 }
177 
178 static int iterate_on_map_values(char *map_handle, char *iter_pin_path)
179 {
180 	char cmd[MAX_BPFTOOL_CMD_LEN];
181 	int ret = 0;
182 
183 
184 	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "iter pin %s %s map %s",
185 		       BPF_ITER_FILE, iter_pin_path, map_handle);
186 	if (!ASSERT_GT(ret, 0, "format iterator creation cmd"))
187 		return 1;
188 	ret = run_bpftool_command(cmd);
189 	if (ret)
190 		return ret;
191 	ret = snprintf(cmd, MAP_NAME_MAX_LEN, "cat %s", iter_pin_path);
192 	if (ret < 0)
193 		goto cleanup;
194 	ret = system(cmd);
195 
196 cleanup:
197 	unlink(iter_pin_path);
198 	return ret;
199 }
200 
201 static int create_inner_map(void)
202 {
203 	char cmd[MAX_BPFTOOL_CMD_LEN];
204 	int ret = 0;
205 
206 	ret = snprintf(
207 		cmd, MAX_BPFTOOL_CMD_LEN,
208 		"map create %s/%s type array key 4 value 4 entries 4 name %s",
209 		BPFFS_PIN_DIR, INNER_MAP_NAME, INNER_MAP_NAME);
210 	if (!ASSERT_GT(ret, 0, "format inner map create cmd"))
211 		return 1;
212 	return run_bpftool_command(cmd);
213 }
214 
215 static int create_outer_map(void)
216 {
217 	char cmd[MAX_BPFTOOL_CMD_LEN];
218 	int ret = 0;
219 
220 	ret = snprintf(
221 		cmd, MAX_BPFTOOL_CMD_LEN,
222 		"map create %s/%s type hash_of_maps key 4 value 4 entries 2 name %s inner_map name %s",
223 		BPFFS_PIN_DIR, OUTER_MAP_NAME, OUTER_MAP_NAME, INNER_MAP_NAME);
224 	if (!ASSERT_GT(ret, 0, "format outer map create cmd"))
225 		return 1;
226 	return run_bpftool_command(cmd);
227 }
228 
229 static void delete_pinned_map(char *map_name)
230 {
231 	char pin_path[PATH_MAX_LEN];
232 	int ret;
233 
234 	ret = snprintf(pin_path, PATH_MAX_LEN, "%s/%s", BPFFS_PIN_DIR,
235 		       map_name);
236 	if (ret >= 0)
237 		unlink(pin_path);
238 }
239 
240 static int add_outer_map_entry(int key)
241 {
242 	char cmd[MAX_BPFTOOL_CMD_LEN];
243 	int ret = 0;
244 
245 	ret = snprintf(
246 		cmd, MAX_BPFTOOL_CMD_LEN,
247 		"map update pinned %s/%s key %d 0 0 0 value name %s",
248 		BPFFS_PIN_DIR, OUTER_MAP_NAME, key, INNER_MAP_NAME);
249 	if (!ASSERT_GT(ret, 0, "format outer map value addition cmd"))
250 		return 1;
251 	return run_bpftool_command(cmd);
252 }
253 
254 static void test_basic_access(struct test_desc *desc)
255 {
256 	char map_handle[MAP_NAME_MAX_LEN];
257 	char iter_pin_path[PATH_MAX_LEN];
258 	int ret;
259 
260 	if (desc->pinned)
261 		ret = snprintf(map_handle, MAP_NAME_MAX_LEN, "pinned %s",
262 			       desc->pin_path);
263 	else
264 		ret = snprintf(map_handle, MAP_NAME_MAX_LEN, "name %s",
265 			       desc->map_name);
266 	if (!ASSERT_GT(ret, 0, "format map handle"))
267 		return;
268 
269 	ret = lookup_map_value(map_handle);
270 	ASSERT_OK(ret, "read map value");
271 
272 	ret = read_map_btf_data(map_handle);
273 	ASSERT_OK(ret, "read map btf data");
274 
275 	ret = write_map_value(map_handle);
276 	ASSERT_OK(desc->write_must_fail ? !ret : ret, "write map value");
277 
278 	ret = delete_map_value(map_handle);
279 	ASSERT_OK(desc->write_must_fail ? !ret : ret, "delete map value");
280 	/* Restore deleted value */
281 	if (!ret)
282 		write_map_value(map_handle);
283 
284 	ret = snprintf(iter_pin_path, PATH_MAX_LEN, "%s/iter", BPFFS_PIN_DIR);
285 	if (ASSERT_GT(ret, 0, "format iter pin path")) {
286 		ret = iterate_on_map_values(map_handle, iter_pin_path);
287 		ASSERT_OK(ret, "iterate on map values");
288 	}
289 }
290 
291 static void test_create_nested_maps(void)
292 {
293 	if (!ASSERT_OK(create_inner_map(), "create inner map"))
294 		return;
295 	if (!ASSERT_OK(create_outer_map(), "create outer map"))
296 		goto end_cleanup_inner;
297 	ASSERT_OK(add_outer_map_entry(0), "add a first entry in outer map");
298 	ASSERT_OK(add_outer_map_entry(1), "add a second entry in outer map");
299 	ASSERT_NEQ(add_outer_map_entry(2), 0, "add a third entry in outer map");
300 
301 	delete_pinned_map(OUTER_MAP_NAME);
302 end_cleanup_inner:
303 	delete_pinned_map(INNER_MAP_NAME);
304 }
305 
306 static void test_btf_list(void)
307 {
308 	ASSERT_OK(run_bpftool_command("btf list"), "list btf data");
309 }
310 
311 static struct test_desc tests[] = {
312 	{
313 		.name = "unprotected_unpinned",
314 		.protection = UNPROTECTED,
315 		.map_name = UNPROTECTED_MAP_NAME,
316 		.pinned = false,
317 		.write_must_fail = false,
318 	},
319 	{
320 		.name = "unprotected_pinned",
321 		.protection = UNPROTECTED,
322 		.map_name = UNPROTECTED_MAP_NAME,
323 		.pinned = true,
324 		.write_must_fail = false,
325 	},
326 	{
327 		.name = "protected_unpinned",
328 		.protection = PROTECTED,
329 		.map_name = UNPROTECTED_MAP_NAME,
330 		.pinned = false,
331 		.write_must_fail = true,
332 	},
333 	{
334 		.name = "protected_pinned",
335 		.protection = PROTECTED,
336 		.map_name = UNPROTECTED_MAP_NAME,
337 		.pinned = true,
338 		.write_must_fail = true,
339 	}
340 };
341 
342 static const size_t tests_count = ARRAY_SIZE(tests);
343 
344 void test_bpftool_maps_access(void)
345 {
346 	struct security_bpf_map *skel;
347 	struct test_desc *current;
348 	int i;
349 
350 	skel = general_setup();
351 	if (!ASSERT_OK_PTR(skel, "prepare programs"))
352 		goto cleanup;
353 
354 	for (i = 0; i < tests_count; i++) {
355 		current = &tests[i];
356 		if (!test__start_subtest(current->name))
357 			continue;
358 		if (ASSERT_OK(test_setup(skel, current), "subtest setup")) {
359 			test_basic_access(current);
360 			test_cleanup(current);
361 		}
362 	}
363 	if (test__start_subtest("nested_maps"))
364 		test_create_nested_maps();
365 	if (test__start_subtest("btf_list"))
366 		test_btf_list();
367 
368 cleanup:
369 	general_cleanup(skel);
370 }
371 
372