BPF List
 help / color / mirror / Atom feed
* [PATCH bpf-next 1/2] bpftool: Reset map info before each query in show and dump loops
  2026-09-28 17:18 [PATCH bpf-next 0/2] bpftool: Reset map info between queries Tianyi Chen
@ 2026-09-28 17:13 ` Tianyi Chen
  2026-09-29 10:28   ` Quentin Monnet
  2026-09-28 17:13 ` [PATCH bpf-next 2/2] selftests/bpf: Check bpftool map info across mixed BTF maps Tianyi Chen
  1 sibling, 1 reply; 5+ messages in thread
From: Tianyi Chen @ 2026-09-28 17:13 UTC (permalink / raw)
  To: bpf; +Cc: qmo, andrii, eddyz87, ihor.solodrai, linux-kselftest,
	skokovmaksimevg

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.

Reported-by: Quentin Monnet <qmo@kernel.org>
Link: https://lore.kernel.org/r/3acbe829-06f0-4df4-b57c-a8c7b802ec96@kernel.org
Assisted-by: LLM
Signed-off-by: Tianyi Chen <hi@tychen.cc>
---
 tools/bpf/bpftool/map.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index 20d59eab09a1..f84311fa640d 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -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;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH bpf-next 2/2] selftests/bpf: Check bpftool map info across mixed BTF maps
  2026-09-28 17:18 [PATCH bpf-next 0/2] bpftool: Reset map info between queries Tianyi Chen
  2026-09-28 17:13 ` [PATCH bpf-next 1/2] bpftool: Reset map info before each query in show and dump loops Tianyi Chen
@ 2026-09-28 17:13 ` Tianyi Chen
  2026-09-29 10:28   ` Quentin Monnet
  1 sibling, 1 reply; 5+ messages in thread
From: Tianyi Chen @ 2026-09-28 17:13 UTC (permalink / raw)
  To: bpf; +Cc: qmo, andrii, eddyz87, ihor.solodrai, linux-kselftest,
	skokovmaksimevg

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.

Assisted-by: LLM
Signed-off-by: Tianyi Chen <hi@tychen.cc>
---
 .../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

diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_map_info.c b/tools/testing/selftests/bpf/prog_tests/bpftool_map_info.c
new file mode 100644
index 000000000000..5bcfef952619
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_map_info.c
@@ -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");
+}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH bpf-next 0/2] bpftool: Reset map info between queries
@ 2026-09-28 17:18 Tianyi Chen
  2026-09-28 17:13 ` [PATCH bpf-next 1/2] bpftool: Reset map info before each query in show and dump loops Tianyi Chen
  2026-09-28 17:13 ` [PATCH bpf-next 2/2] selftests/bpf: Check bpftool map info across mixed BTF maps Tianyi Chen
  0 siblings, 2 replies; 5+ messages in thread
From: Tianyi Chen @ 2026-09-28 17:18 UTC (permalink / raw)
  To: bpf; +Cc: qmo, andrii, eddyz87, ihor.solodrai, linux-kselftest,
	skokovmaksimevg

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
-- 
2.55.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next 1/2] bpftool: Reset map info before each query in show and dump loops
  2026-09-28 17:13 ` [PATCH bpf-next 1/2] bpftool: Reset map info before each query in show and dump loops Tianyi Chen
@ 2026-09-29 10:28   ` Quentin Monnet
  0 siblings, 0 replies; 5+ messages in thread
From: Quentin Monnet @ 2026-09-29 10:28 UTC (permalink / raw)
  To: Tianyi Chen, bpf
  Cc: andrii, eddyz87, ihor.solodrai, linux-kselftest, skokovmaksimevg

2026-09-29 02:13 UTC+0900 ~ Tianyi Chen <hi@tychen.cc>
> 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.
> 
> Reported-by: Quentin Monnet <qmo@kernel.org>
> Link: https://lore.kernel.org/r/3acbe829-06f0-4df4-b57c-a8c7b802ec96@kernel.org
> Assisted-by: LLM
> Signed-off-by: Tianyi Chen <hi@tychen.cc>


Fixes: 71bb428fe2c1 ("tools: bpf: add bpftool")
Fixes: 99f9863a0c45 ("bpftool: Match maps by name")

Acked-by: Quentin Monnet <qmo@kernel.org>

Thanks for this.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next 2/2] selftests/bpf: Check bpftool map info across mixed BTF maps
  2026-09-28 17:13 ` [PATCH bpf-next 2/2] selftests/bpf: Check bpftool map info across mixed BTF maps Tianyi Chen
@ 2026-09-29 10:28   ` Quentin Monnet
  0 siblings, 0 replies; 5+ messages in thread
From: Quentin Monnet @ 2026-09-29 10:28 UTC (permalink / raw)
  To: Tianyi Chen, bpf
  Cc: andrii, eddyz87, ihor.solodrai, linux-kselftest, skokovmaksimevg

2026-09-29 02:13 UTC+0900 ~ Tianyi Chen <hi@tychen.cc>
> 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.

Acked-by: Quentin Monnet <qmo@kernel.org>


> 
> Assisted-by: LLM
> Signed-off-by: Tianyi Chen <hi@tychen.cc>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-29 10:29 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-28 17:18 [PATCH bpf-next 0/2] bpftool: Reset map info between queries Tianyi Chen
2026-09-28 17:13 ` [PATCH bpf-next 1/2] bpftool: Reset map info before each query in show and dump loops Tianyi Chen
2026-09-29 10:28   ` Quentin Monnet
2026-09-28 17:13 ` [PATCH bpf-next 2/2] selftests/bpf: Check bpftool map info across mixed BTF maps Tianyi Chen
2026-09-29 10:28   ` Quentin Monnet

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox