Skip to content

Commit 0958f1c

Browse files
committed
selftests/bpf: convert test_bpftool_map_access.sh into test_progs framework
The test_bpftool_map.sh script tests that maps read/write accesses are being properly allowed/refused by the kernel depending on a specific fmod_ret program being attached on security_bpf_map function. Rewrite this test to integrate it in the test_progs. The new test spawns a few subtests: #36/1 bpftool_maps_access/unprotected_unpinned:OK #36/2 bpftool_maps_access/unprotected_pinned:OK #36/3 bpftool_maps_access/protected_unpinned:OK #36/4 bpftool_maps_access/protected_pinned:OK #36/5 bpftool_maps_access/nested_maps:OK #36/6 bpftool_maps_access/btf_list:OK #36 bpftool_maps_access:OK Summary: 1/6 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com> Acked-by: Quentin Monnet <qmo@kernel.org> --- Changes in v2: - move the new test in prog_tests directory - collect Quentin's AB
1 parent 50e793e commit 0958f1c

File tree

3 files changed

+371
-399
lines changed

3 files changed

+371
-399
lines changed

tools/testing/selftests/bpf/Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ TEST_PROGS := test_kmod.sh \
108108
test_xdping.sh \
109109
test_bpftool_build.sh \
110110
test_bpftool.sh \
111-
test_bpftool_map.sh \
112111
test_doc_build.sh \
113112
test_xsk.sh \
114113
test_xdp_features.sh
Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
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+

0 commit comments

Comments
 (0)