// SPDX-License-Identifier: GPL-2.0-only #include #include #include #include #include #include #include #include #include #include #include #include "security_bpf_map.skel.h" #define PROTECTED_MAP_NAME "prot_map" #define UNPROTECTED_MAP_NAME "not_prot_map" #define BPF_ITER_FILE "bpf_iter_map_elem.bpf.o" #define BPFFS_PIN_DIR "/sys/fs/bpf/test_bpftool_map" #define INNER_MAP_NAME "inner_map_tt" #define OUTER_MAP_NAME "outer_map_tt" #define MAP_NAME_MAX_LEN 64 #define PATH_MAX_LEN 128 enum map_protection { PROTECTED, UNPROTECTED }; struct test_desc { char *name; enum map_protection protection; struct bpf_map *map; char *map_name; bool pinned; char pin_path[PATH_MAX_LEN]; bool write_must_fail; }; static struct security_bpf_map *general_setup(void) { struct security_bpf_map *skel; uint32_t key, value; int ret, i; skel = security_bpf_map__open_and_load(); if (!ASSERT_OK_PTR(skel, "open and load skeleton")) goto end; struct bpf_map *maps[] = {skel->maps.prot_map, skel->maps.not_prot_map}; ret = security_bpf_map__attach(skel); if (!ASSERT_OK(ret, "attach maps security programs")) goto end_destroy; for (i = 0; i < sizeof(maps)/sizeof(struct bpf_map *); i++) { for (key = 0; key < 2; key++) { int ret = bpf_map__update_elem(maps[i], &key, sizeof(key), &key, sizeof(key), 0); if (!ASSERT_OK(ret, "set initial map value")) goto end_destroy; } } key = 0; value = 1; ret = bpf_map__update_elem(skel->maps.prot_status_map, &key, sizeof(key), &value, sizeof(value), 0); if (!ASSERT_OK(ret, "configure map protection")) goto end_destroy; if (!ASSERT_OK(mkdir(BPFFS_PIN_DIR, S_IFDIR), "create bpffs pin dir")) goto end_destroy; return skel; end_destroy: security_bpf_map__destroy(skel); end: return NULL; } static void general_cleanup(struct security_bpf_map *skel) { rmdir(BPFFS_PIN_DIR); security_bpf_map__destroy(skel); } static void update_test_desc(struct security_bpf_map *skel, struct test_desc *test) { /* Now that the skeleton is loaded, update all missing fields to * have the subtest properly configured */ if (test->protection == PROTECTED) { test->map = skel->maps.prot_map; test->map_name = PROTECTED_MAP_NAME; } else { test->map = skel->maps.not_prot_map; test->map_name = UNPROTECTED_MAP_NAME; } } static int test_setup(struct security_bpf_map *skel, struct test_desc *desc) { int ret; update_test_desc(skel, desc); if (desc->pinned) { ret = snprintf(desc->pin_path, PATH_MAX_LEN, "%s/%s", BPFFS_PIN_DIR, desc->name); if (!ASSERT_GT(ret, 0, "format pin path")) return 1; ret = bpf_map__pin(desc->map, desc->pin_path); if (!ASSERT_OK(ret, "pin map")) return 1; } return 0; } static void test_cleanup(struct test_desc *desc) { if (desc->pinned) bpf_map__unpin(desc->map, NULL); } static int lookup_map_value(char *map_handle) { char cmd[MAX_BPFTOOL_CMD_LEN]; int ret = 0; ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "map lookup %s key 0 0 0 0", map_handle); if (!ASSERT_GT(ret, 0, "format map lookup cmd")) return 1; return run_bpftool_command(cmd); } static int read_map_btf_data(char *map_handle) { char cmd[MAX_BPFTOOL_CMD_LEN]; int ret = 0; ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "btf dump map %s", map_handle); if (!ASSERT_GT(ret, 0, "format map btf dump cmd")) return 1; return run_bpftool_command(cmd); } static int write_map_value(char *map_handle) { char cmd[MAX_BPFTOOL_CMD_LEN]; int ret = 0; ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "map update %s key 0 0 0 0 value 1 1 1 1", map_handle); if (!ASSERT_GT(ret, 0, "format value write cmd")) return 1; return run_bpftool_command(cmd); } static int delete_map_value(char *map_handle) { char cmd[MAX_BPFTOOL_CMD_LEN]; int ret = 0; ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "map delete %s key 0 0 0 0", map_handle); if (!ASSERT_GT(ret, 0, "format value deletion cmd")) return 1; return run_bpftool_command(cmd); } static int iterate_on_map_values(char *map_handle, char *iter_pin_path) { char cmd[MAX_BPFTOOL_CMD_LEN]; int ret = 0; ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "iter pin %s %s map %s", BPF_ITER_FILE, iter_pin_path, map_handle); if (!ASSERT_GT(ret, 0, "format iterator creation cmd")) return 1; ret = run_bpftool_command(cmd); if (ret) return ret; ret = snprintf(cmd, MAP_NAME_MAX_LEN, "cat %s", iter_pin_path); if (ret < 0) goto cleanup; ret = system(cmd); cleanup: unlink(iter_pin_path); return ret; } static int create_inner_map(void) { char cmd[MAX_BPFTOOL_CMD_LEN]; int ret = 0; ret = snprintf( cmd, MAX_BPFTOOL_CMD_LEN, "map create %s/%s type array key 4 value 4 entries 4 name %s", BPFFS_PIN_DIR, INNER_MAP_NAME, INNER_MAP_NAME); if (!ASSERT_GT(ret, 0, "format inner map create cmd")) return 1; return run_bpftool_command(cmd); } static int create_outer_map(void) { char cmd[MAX_BPFTOOL_CMD_LEN]; int ret = 0; ret = snprintf( cmd, MAX_BPFTOOL_CMD_LEN, "map create %s/%s type hash_of_maps key 4 value 4 entries 2 name %s inner_map name %s", BPFFS_PIN_DIR, OUTER_MAP_NAME, OUTER_MAP_NAME, INNER_MAP_NAME); if (!ASSERT_GT(ret, 0, "format outer map create cmd")) return 1; return run_bpftool_command(cmd); } static void delete_pinned_map(char *map_name) { char pin_path[PATH_MAX_LEN]; int ret; ret = snprintf(pin_path, PATH_MAX_LEN, "%s/%s", BPFFS_PIN_DIR, map_name); if (ret >= 0) unlink(pin_path); } static int add_outer_map_entry(int key) { char cmd[MAX_BPFTOOL_CMD_LEN]; int ret = 0; ret = snprintf( cmd, MAX_BPFTOOL_CMD_LEN, "map update pinned %s/%s key %d 0 0 0 value name %s", BPFFS_PIN_DIR, OUTER_MAP_NAME, key, INNER_MAP_NAME); if (!ASSERT_GT(ret, 0, "format outer map value addition cmd")) return 1; return run_bpftool_command(cmd); } static void test_basic_access(struct test_desc *desc) { char map_handle[MAP_NAME_MAX_LEN]; char iter_pin_path[PATH_MAX_LEN]; int ret; if (desc->pinned) ret = snprintf(map_handle, MAP_NAME_MAX_LEN, "pinned %s", desc->pin_path); else ret = snprintf(map_handle, MAP_NAME_MAX_LEN, "name %s", desc->map_name); if (!ASSERT_GT(ret, 0, "format map handle")) return; ret = lookup_map_value(map_handle); ASSERT_OK(ret, "read map value"); ret = read_map_btf_data(map_handle); ASSERT_OK(ret, "read map btf data"); ret = write_map_value(map_handle); ASSERT_OK(desc->write_must_fail ? !ret : ret, "write map value"); ret = delete_map_value(map_handle); ASSERT_OK(desc->write_must_fail ? !ret : ret, "delete map value"); /* Restore deleted value */ if (!ret) write_map_value(map_handle); ret = snprintf(iter_pin_path, PATH_MAX_LEN, "%s/iter", BPFFS_PIN_DIR); if (ASSERT_GT(ret, 0, "format iter pin path")) { ret = iterate_on_map_values(map_handle, iter_pin_path); ASSERT_OK(ret, "iterate on map values"); } } static void test_create_nested_maps(void) { if (!ASSERT_OK(create_inner_map(), "create inner map")) return; if (!ASSERT_OK(create_outer_map(), "create outer map")) goto end_cleanup_inner; ASSERT_OK(add_outer_map_entry(0), "add a first entry in outer map"); ASSERT_OK(add_outer_map_entry(1), "add a second entry in outer map"); ASSERT_NEQ(add_outer_map_entry(2), 0, "add a third entry in outer map"); delete_pinned_map(OUTER_MAP_NAME); end_cleanup_inner: delete_pinned_map(INNER_MAP_NAME); } static void test_btf_list(void) { ASSERT_OK(run_bpftool_command("btf list"), "list btf data"); } static struct test_desc tests[] = { { .name = "unprotected_unpinned", .protection = UNPROTECTED, .map_name = UNPROTECTED_MAP_NAME, .pinned = false, .write_must_fail = false, }, { .name = "unprotected_pinned", .protection = UNPROTECTED, .map_name = UNPROTECTED_MAP_NAME, .pinned = true, .write_must_fail = false, }, { .name = "protected_unpinned", .protection = PROTECTED, .map_name = UNPROTECTED_MAP_NAME, .pinned = false, .write_must_fail = true, }, { .name = "protected_pinned", .protection = PROTECTED, .map_name = UNPROTECTED_MAP_NAME, .pinned = true, .write_must_fail = true, } }; static const size_t tests_count = ARRAY_SIZE(tests); void test_bpftool_maps_access(void) { struct security_bpf_map *skel; struct test_desc *current; int i; skel = general_setup(); if (!ASSERT_OK_PTR(skel, "prepare programs")) goto cleanup; for (i = 0; i < tests_count; i++) { current = &tests[i]; if (!test__start_subtest(current->name)) continue; if (ASSERT_OK(test_setup(skel, current), "subtest setup")) { test_basic_access(current); test_cleanup(current); } } if (test__start_subtest("nested_maps")) test_create_nested_maps(); if (test__start_subtest("btf_list")) test_btf_list(); cleanup: general_cleanup(skel); }