bpftool: Reset map info between queries
Subsystems (4)
cover bpftool: Reset map info between queries
Reset map info before each query in the existing show and dump loops to prevent BTF metadata from a preceding map leaking into a map without BTF.
Patch 1 fixes the four query loops. Patch 2 adds regression tests for full and named listings, JSON dumps, and plain dumps.
All four regression subtests fail on the unmodified bpf-next base and pass with the fix in an x86-64 VM. This series does not depend on recursive dumping or Maxim's series.
Tianyi Chen (2):
bpftool: Reset map info before each query in show and dump loops selftests/bpf: Check bpftool map info across mixed BTF maps
tools/bpf/bpftool/map.c | 8 ++ .../bpf/prog_tests/bpftool_map_info.c | 126 ++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/bpftool_map_info.c
base-commit: ea9358e1270ab2c3ba6f36bd9bdda68617665516
Signature · 1 line
2.55.0
PATCH 1/2 bpftool: Reset map info before each query in show and dump loops
The kernel copies bpf_map_info from userspace and only fills in the BTF fields when a map has BTF. Reusing the structure for a later map without BTF therefore retains the preceding map's BTF IDs.
Clear the structure and restore its length before each query in map listing, named-map listing, BTF detection and dumping. This prevents reporting stale BTF IDs, choosing BTF output for mixed map selections, and formatting a map with another map's key and value types.
--- tools/bpf/bpftool/map.c | 8 ++++++++ 1 file changed, 8 insertions(+)
tools/bpf/bpftool/map.c +8 -0
@@ -647,6 +647,8 @@ static int do_show_subset(int argc, char **argv)if (json_output && nb_fds >1 )jsonw_start_array (json_wtr);/* root array */ for (i =0 ; i < nb_fds; i++) { +memset (&info,0 ,sizeof (info)); + len =sizeof (info); err =bpf_map_get_info_by_fd (fds[i], &info, &len);if (err) { p_err("can't get map info: %s",
@@ -718,6 +720,8 @@ static int do_show(int argc, char **argv)break ; } +memset (&info,0 ,sizeof (info)); + len =sizeof (info); err =bpf_map_get_info_by_fd (fd, &info, &len);if (err) {p_err ("can't get map info: %s" ,strerror (errno));
@@ -774,6 +778,8 @@ static int maps_have_btf(int *fds, int nb_fds)int err, i;for (i =0 ; i < nb_fds; i++) { +memset (&info,0 ,sizeof (info)); + len =sizeof (info); err =bpf_map_get_info_by_fd (fds[i], &info, &len);if (err) {p_err ("can't get map info: %s" ,strerror (errno));
@@ -952,6 +958,8 @@ static int do_dump(int argc, char **argv)if (wtr && nb_fds >1 )jsonw_start_array (wtr);/* root array */ for (i =0 ; i < nb_fds; i++) { +memset (&info,0 ,sizeof (info)); + len =sizeof (info);if (bpf_map_get_info_by_fd (fds[i], &info, &len)) {p_err ("can't get map info: %s" ,strerror (errno)); err =-1 ;
Signature · 1 line
2.55.0
Re: [PATCH bpf-next 1/2] bpftool: Reset map info before each query in show and dump loops
2026-09-29 02:13 UTC+0900 ~ Tianyi Chen <[email protected]>
Quoted 9 lines
The kernel copies
bpf_map_infofrom userspace and only fills in the BTF fields when a map has BTF. Reusing the structure for a later map without BTF therefore retains the preceding map's BTF IDs.Clear the structure and restore its length before each query in map listing, named-map listing, BTF detection and dumping. This prevents reporting stale BTF IDs, choosing BTF output for mixed map selections, and formatting a map with another map's key and value types.
Quoted 4 lines
Reported-by Quentin Monnet <[email protected]>Assisted-by LLMSigned-off-by Tianyi Chen <[email protected]>
Thanks for this.
Quentin Monnet
maintainerRe: [PATCH bpf-next 1/2] bpftool: Reset map info before each query in show and dump loops
2026-09-29 11:28 UTC+0100 ~ Quentin Monnet <[email protected]>
Quoted 16 lines
2026-09-29 02:13 UTC+0900 ~ Tianyi Chen <[email protected]>
Quoted 9 lines
The kernel copies
bpf_map_infofrom userspace and only fills in the BTF fields when a map has BTF. Reusing the structure for a later map without BTF therefore retains the preceding map's BTF IDs.Clear the structure and restore its length before each query in map listing, named-map listing, BTF detection and dumping. This prevents reporting stale BTF IDs, choosing BTF output for mixed map selections, and formatting a map with another map's key and value types.
Quoted 4 lines
Reported-by Quentin Monnet <[email protected]>Assisted-by LLMSigned-off-by Tianyi Chen <[email protected]>
Quoted 6 lines
Fixes 71bb428fe2c1 ("tools: bpf: add bpftool")Fixes 99f9863a0c45 ("bpftool: Match maps by name")Acked-by Quentin Monnet <[email protected]>Thanks for this.
Hi Tianyi, sorry for the delay, it seems your series somehow fell through the cracks. Daniel sent a fix on kernel side for this [0], but I think it's still worth getting the fix in bpftool as well, to avoid hitting the bug if we run it on older kernels that won't have the fix.
Would you please send a rebased version of your series (and drop the mention of recursing dumps in patch 2's description, keep the Fixes: and
Acked-by: tags)? Similarly, can you repost a rebased version of your patches for the batch lookups for hashmap dumps in bpftool, please?
Thanks,
Quentin
PATCH 2/2 selftests/bpf: Check bpftool map info across mixed BTF maps
In reply to Tianyi ChenCreate a typed map followed by a smaller untyped map with the same name. Check that full and named listings do not report BTF for the untyped map, JSON dumps match independently dumped contents, and plain dumps use raw output when the selected maps do not all have BTF.
These cover the separate info-query loops in map show and map dump without requiring recursive dumping.
--- .../bpf/prog_tests/bpftool_map_info.c | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/bpftool_map_info.c
tools/testing/selftests/bpf/prog_tests/bpftool_map_info.c new file +126 -0
@@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <test_progs.h> +#include <bpftool_helpers.h> +#include <bpf/btf.h> + +#define OUTPUT_SIZE (1024 * 1024) + +static void check_show (char *output,struct bpf_map_info *info) +{ +char id[32 ]; +char *map, *end, *btf; + +snprintf (id,sizeof (id),"{\"id\":%u," , info->id); + map =strstr (output, id); +if (!ASSERT_OK_PTR (map,"map_in_output" )) +return ; + end =strchr (map,'}' ); +if (!ASSERT_OK_PTR (end,"map_object_end" )) +return ; + btf =strstr (map,"\"btf_id\":" ); +if (info->btf_id) { +ASSERT_TRUE (btf && btf < end,"typed_map_btf" ); +if (btf && btf < end) +ASSERT_EQ (strtoul (btf +strlen ("\"btf_id\":" ),NULL ,10 ), + info->btf_id,"btf_id" ); + }else { +ASSERT_TRUE (!btf || btf > end,"raw_map_no_btf" ); + } +} + +static void test_map_info (const char *operation) +{ +LIBBPF_OPTS (bpf_map_create_opts, opts); +struct bpf_map_info info[2 ] = {}; +char command[MAX_BPFTOOL_CMD_LEN ], name[BPF_OBJ_NAME_LEN ]; +char elements[2 ][1024 ] = {}, expected[4096 ]; +struct btf *btf =NULL ; +int fds[] = {-1 ,-1 }; +__u64 wide_key =0 , wide_value =1 ; +__u32 key =0 , value =2 ; +char *output; +int i; + + output =malloc (OUTPUT_SIZE ); +if (!ASSERT_OK_PTR (output,"output" )) +return ; +snprintf (name,sizeof (name),"info_%u" ,getpid ()); + btf =btf__new_empty (); +if (!ASSERT_OK_PTR (btf,"create_btf" ) || + !ASSERT_EQ (btf__add_int (btf,"unsigned long long" ,8 ,0 ),1 ,"btf_int" ) || + !ASSERT_OK (btf__load_into_kernel (btf),"load_btf" )) + goto out; + opts.btf_fd =btf__fd (btf); + opts.btf_key_type_id =1 ; + opts.btf_value_type_id =1 ; +/* Query the typed map before a smaller map without BTF. */ + fds[0 ] =bpf_map_create (BPF_MAP_TYPE_HASH , name,8 ,8 ,1 , &opts); + fds[1 ] =bpf_map_create (BPF_MAP_TYPE_HASH , name,4 ,4 ,1 ,NULL ); +if (!ASSERT_OK_FD (fds[0 ],"create_typed" ) || + !ASSERT_OK_FD (fds[1 ],"create_raw" ) || + !ASSERT_OK (bpf_map_update_elem (fds[0 ], &wide_key, &wide_value,BPF_ANY ), +"populate_typed" ) || + !ASSERT_OK (bpf_map_update_elem (fds[1 ], &key, &value,BPF_ANY ),"populate_raw" )) + goto out; +for (i =0 ; i <ARRAY_SIZE (fds); i++) { +__u32 len =sizeof (info[i]); + +if (!ASSERT_OK (bpf_map_get_info_by_fd (fds[i], &info[i], &len),"map_info" )) + goto out; + } +if (!ASSERT_LT (info[0 ].id, info[1 ].id,"map_order" )) + goto out; + +if (!strcmp (operation,"show_all" )) +snprintf (command,sizeof (command),"-j map show" ); +else if (!strcmp (operation,"show_name" )) +snprintf (command,sizeof (command),"-j map show name %s" , name); +else +snprintf (command,sizeof (command),"%s map dump name %s" , + !strcmp (operation,"dump_json" ) ?"-j" :"" , name); + output[0 ] ='\0' ; +if (!ASSERT_OK (get_bpftool_command_output (command, output,OUTPUT_SIZE ), operation)) + goto out; +if (!strncmp (operation,"show_" ,5 )) { +check_show (output, &info[0 ]); +check_show (output, &info[1 ]); + }else if (!strcmp (operation,"dump_json" )) { +for (i =0 ; i <ARRAY_SIZE (fds); i++) { +snprintf (command,sizeof (command),"-j map dump id %u" , info[i].id); +if (!ASSERT_OK (get_bpftool_command_output (command, elements[i], +sizeof (elements[i])),"single_dump" )) + goto out; + elements[i][strcspn (elements[i],"\n" )] ='\0' ; + } +snprintf (expected,sizeof (expected), +"[{\"id\":%u,\"type\":\"hash\",\"name\":\"%s\",\"flags\":0," +"\"elements\":%s},{\"id\":%u,\"type\":\"hash\",\"name\":\"%s\"," +"\"flags\":0,\"elements\":%s}]\n" , + info[0 ].id, name, elements[0 ], info[1 ].id, name, elements[1 ]); +ASSERT_STREQ (output, expected,"independent_map_formatting" ); + }else { +char *count =strstr (output,"Found 1 element\n" ); + +ASSERT_NULL (strstr (output,"\"value\":" ),"mixed_maps_use_raw_output" ); +if (ASSERT_OK_PTR (count,"first_map_count" )) +ASSERT_HAS_SUBSTR (count +1 ,"Found 1 element\n" ,"second_map_count" ); + } +out: +for (i =0 ; i <ARRAY_SIZE (fds); i++) +if (fds[i] >=0 ) +close (fds[i]); +btf__free (btf); +free (output); +} + +void serial_test_bpftool_map_info (void ) +{ +if (test__start_subtest ("show_all" )) +test_map_info ("show_all" ); +if (test__start_subtest ("show_name" )) +test_map_info ("show_name" ); +if (test__start_subtest ("dump_json" )) +test_map_info ("dump_json" ); +if (test__start_subtest ("dump_plain" )) +test_map_info ("dump_plain" ); +}
Signature · 1 line
2.55.0
Re: [PATCH bpf-next 2/2] selftests/bpf: Check bpftool map info across mixed BTF maps
2026-09-29 02:13 UTC+0900 ~ Tianyi Chen <[email protected]>
Quoted 7 lines
Create a typed map followed by a smaller untyped map with the same name. Check that full and named listings do not report BTF for the untyped map, JSON dumps match independently dumped contents, and plain dumps use raw output when the selected maps do not all have BTF.
These cover the separate info-query loops in map show and map dump without requiring recursive dumping.
Not sure why we'd mention recursive dumping at all in the commit logs for this patch (can we please drop this sentence when applying?).
Looks good otherwise, thanks.
Quoted 3 lines
Assisted-by LLMSigned-off-by Tianyi Chen <[email protected]>