The unpinned subtests pass "name prot_map" or "name not_prot_map" to bpftool. Both maps are used by programs attached through trampolines, which are freed only after RCU grace periods and hold their map references until then. A run's maps therefore stay in the map ID table after its skeleton is destroyed, for about 300 ms in an x86-64 VM. If the test runs again within that window, the name matches maps from both runs, bpftool fails with "several maps match this handle", and the unpinned subtests fail. Look up these maps by ID instead and drop the now unused map names. Fixes: 2d96bbdfd3b5 ("selftests/bpf: convert test_bpftool_map_access.sh into test_progs framework") Assisted-by: LLM Signed-off-by: Tianyi Chen --- Tested in an x86-64 VM with unmodified bpftool. Back-to-back runs of this test fail before the fix; with the fix, all six subtests pass in ten consecutive runs, including ID lookups while stale same-name maps are still present. .../bpf/prog_tests/bpftool_maps_access.c | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_maps_access.c b/tools/testing/selftests/bpf/prog_tests/bpftool_maps_access.c index e0eb869cb1b47..5763cb19236a6 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpftool_maps_access.c +++ b/tools/testing/selftests/bpf/prog_tests/bpftool_maps_access.c @@ -13,8 +13,6 @@ #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" @@ -32,7 +30,6 @@ 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; @@ -93,13 +90,10 @@ static void update_test_desc(struct security_bpf_map *skel, /* Now that the skeleton is loaded, update all missing fields to * have the subtest properly configured */ - if (test->protection == PROTECTED) { + if (test->protection == PROTECTED) test->map = skel->maps.prot_map; - test->map_name = PROTECTED_MAP_NAME; - } else { + 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) @@ -253,16 +247,25 @@ static int add_outer_map_entry(int key) static void test_basic_access(struct test_desc *desc) { + struct bpf_map_info info = {}; + __u32 len = sizeof(info); char map_handle[MAP_NAME_MAX_LEN]; char iter_pin_path[PATH_MAX_LEN]; int ret; - if (desc->pinned) + 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); + } else { + /* + * Maps from an earlier run stay visible until its trampoline + * programs are freed, so a name can match more than one map. + */ + ret = bpf_map_get_info_by_fd(bpf_map__fd(desc->map), &info, &len); + if (!ASSERT_OK(ret, "get map info")) + return; + ret = snprintf(map_handle, MAP_NAME_MAX_LEN, "id %u", info.id); + } if (!ASSERT_GT(ret, 0, "format map handle")) return; @@ -312,28 +315,24 @@ 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, } base-commit: 05441d697f7f9a927264f4058cbbc6a03dfcac9d -- 2.56.0