* [PATCH bpf-next v2 0/2] bpftool: Fail map dump for maps that can't be iterated
@ 2026-09-25 16:20 Maxim Skokov
2026-09-25 16:20 ` [PATCH bpf-next v2 1/2] " Maxim Skokov
2026-09-25 16:20 ` [PATCH bpf-next v2 2/2] selftests/bpf: Check that bpftool map dump fails for socket storage Maxim Skokov
0 siblings, 2 replies; 5+ messages in thread
From: Maxim Skokov @ 2026-09-25 16:20 UTC (permalink / raw)
To: bpf
Cc: qmo, ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, kuba, shuah,
linux-kselftest, Maxim Skokov
"bpftool map dump" treats only ENOENT from bpf_map_get_next_key() as the
end of the map. Map types that can't be iterated (local storage, ringbuf,
bloom filter, arena, queue, stack) fail on the first key, and bpftool
prints an empty dump, "[]" or "Found 0 elements", with only a non-zero
exit status.
Patch 1 asks for the first key before printing anything and fails with
the map type and the error. Patch 2 adds a test_progs case on a socket
storage map.
The new test fails without patch 1 and passes with it; the other bpftool_*
selftests pass. Tested on x86_64 under a bpf-next kernel with KASAN and
lockdep, by hand on queue, stack, bloom filter, ringbuf, arena, sk_storage
and empty and filled hash maps, and through BPF CI (x86_64, aarch64,
s390x) with only comment style differing from this posting.
Changes in v2:
- probe the first key before any output instead of printing a hint for
ENOTSUPP only, so every non-iterable type gets an error and stdout gets
no "[]" (Alexei)
- drop the Fixes tag and use a neutral example map (Alexei)
- selftests: check that JSON output is only the error object and plain
stdout has no elements or count; clear the output buffer before each
run (Sashiko)
- multi-line comment style (bpf-ci)
v1: https://lore.kernel.org/bpf/20260924160732.485650-1-skokovmaksimevg@gmail.com/
Maxim Skokov (2):
bpftool: Fail map dump for maps that can't be iterated
selftests/bpf: Check that bpftool map dump fails for socket storage
tools/bpf/bpftool/map.c | 26 +++++-
.../bpf/prog_tests/bpftool_map_dump.c | 88 +++++++++++++++++++
2 files changed, 112 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/bpftool_map_dump.c
base-commit: 4f3a5eae895b9995e93425a75235d8f1f3268caa
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH bpf-next v2 1/2] bpftool: Fail map dump for maps that can't be iterated
2026-09-25 16:20 [PATCH bpf-next v2 0/2] bpftool: Fail map dump for maps that can't be iterated Maxim Skokov
@ 2026-09-25 16:20 ` Maxim Skokov
2026-09-25 19:24 ` Quentin Monnet
2026-09-25 16:20 ` [PATCH bpf-next v2 2/2] selftests/bpf: Check that bpftool map dump fails for socket storage Maxim Skokov
1 sibling, 1 reply; 5+ messages in thread
From: Maxim Skokov @ 2026-09-25 16:20 UTC (permalink / raw)
To: bpf
Cc: qmo, ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, kuba, shuah,
linux-kselftest, Maxim Skokov
"bpftool map dump" walks the map with bpf_map_get_next_key() and treats
ENOENT as the end of the map. Map types that can't be iterated fail that
call on the very first key: local storage and ringbuf with ENOTSUPP,
bloom filter and arena with EOPNOTSUPP, queue and stack with EINVAL.
bpftool then ends the walk without a word and prints what it prints for
an empty map, "[]" with BTF or "Found 0 elements" without, and only the
exit status tells that the map was not read. On a socket storage map
that holds storage for a socket:
# bpftool map dump pinned /sys/fs/bpf/sk_storage
[]
Ask for the first key before printing anything, and when that fails with
anything but ENOENT, fail with the map type and the error instead of a
dump:
# bpftool map dump pinned /sys/fs/bpf/sk_storage
Error: can't dump sk_storage map: Unknown error 524
# bpftool -j map dump pinned /sys/fs/bpf/sk_storage
{"error":"can't dump sk_storage map: Unknown error 524"}
An error later in the walk is now reported too, as "bpftool map getnext"
reports it, and the element count is printed only after a complete walk.
Assisted-by: LLM
Signed-off-by: Maxim Skokov <skokovmaksimevg@gmail.com>
---
tools/bpf/bpftool/map.c | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index 20d59eab09..626882f207 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -856,6 +856,25 @@ map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr,
}
}
+ /*
+ * Map types that can't be iterated fail on the very first key. Say so
+ * before printing anything, so that the output isn't an empty dump.
+ */
+ if (bpf_map_get_next_key(fd, NULL, key) && errno != ENOENT) {
+ const char *map_type_str;
+ int saved_errno = errno;
+
+ map_type_str = libbpf_bpf_map_type_str(info->type);
+ if (map_type_str)
+ p_err("can't dump %s map: %s", map_type_str,
+ strerror(saved_errno));
+ else
+ p_err("can't dump map of type %u: %s", info->type,
+ strerror(saved_errno));
+ err = -1;
+ goto exit_free;
+ }
+
if (wtr) {
err = get_map_kv_btf(info, &btf);
if (err) {
@@ -883,8 +902,11 @@ map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr,
while (true) {
err = bpf_map_get_next_key(fd, prev_key, key);
if (err) {
- if (errno == ENOENT)
+ if (errno == ENOENT) {
err = 0;
+ break;
+ }
+ p_err("can't get next key: %s", strerror(errno));
break;
}
if (!dump_map_elem(fd, key, value, info, btf, wtr,
@@ -897,7 +919,7 @@ map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr,
jsonw_end_array(wtr); /* elements */
if (show_header)
jsonw_end_object(wtr); /* map object */
- } else {
+ } else if (!err) {
printf("Found %u element%s\n", num_elems,
num_elems != 1 ? "s" : "");
}
--
2.47.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH bpf-next v2 2/2] selftests/bpf: Check that bpftool map dump fails for socket storage
2026-09-25 16:20 [PATCH bpf-next v2 0/2] bpftool: Fail map dump for maps that can't be iterated Maxim Skokov
2026-09-25 16:20 ` [PATCH bpf-next v2 1/2] " Maxim Skokov
@ 2026-09-25 16:20 ` Maxim Skokov
1 sibling, 0 replies; 5+ messages in thread
From: Maxim Skokov @ 2026-09-25 16:20 UTC (permalink / raw)
To: bpf
Cc: qmo, ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, kuba, shuah,
linux-kselftest, Maxim Skokov
Socket storage can't be iterated from user space. Create a
BPF_MAP_TYPE_SK_STORAGE map, attach storage to a socket, pin it, and
check that "bpftool map dump" fails with nothing but an error: a JSON
error object with -j, no elements and no count on stdout in plain mode,
and the reason on stderr.
Assisted-by: LLM
Signed-off-by: Maxim Skokov <skokovmaksimevg@gmail.com>
---
.../bpf/prog_tests/bpftool_map_dump.c | 88 +++++++++++++++++++
1 file changed, 88 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/bpftool_map_dump.c
diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_map_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_map_dump.c
new file mode 100644
index 0000000000..0a0fa8926b
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_map_dump.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <sys/socket.h>
+#include <unistd.h>
+#include <bpf/btf.h>
+#include <test_progs.h>
+#include "bpftool_helpers.h"
+
+#define PIN_PATH "/sys/fs/bpf/test_bpftool_map_dump_sk_storage"
+#define JSON_ERROR "{\"error\":\"can't dump sk_storage map"
+
+/* A socket storage map with a single element, keyed on a socket we own. */
+static int create_sk_storage_map(void)
+{
+ LIBBPF_OPTS(bpf_map_create_opts, opts,
+ .map_flags = BPF_F_NO_PREALLOC);
+ struct btf *btf;
+ int int_id, fd = -1;
+
+ btf = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf, "btf__new_empty"))
+ return -1;
+
+ int_id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
+ if (!ASSERT_GT(int_id, 0, "btf__add_int"))
+ goto out;
+ if (!ASSERT_OK(btf__load_into_kernel(btf), "btf__load_into_kernel"))
+ goto out;
+
+ opts.btf_fd = btf__fd(btf);
+ opts.btf_key_type_id = int_id;
+ opts.btf_value_type_id = int_id;
+ fd = bpf_map_create(BPF_MAP_TYPE_SK_STORAGE, "sk_storage", sizeof(int),
+ sizeof(int), 0, &opts);
+ ASSERT_OK_FD(fd, "bpf_map_create");
+out:
+ btf__free(btf);
+ return fd;
+}
+
+void test_bpftool_map_dump(void)
+{
+ int map_fd, sk_fd = -1, value = 42, ret;
+ char output[1024] = {};
+
+ map_fd = create_sk_storage_map();
+ if (map_fd < 0)
+ return;
+
+ sk_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (!ASSERT_OK_FD(sk_fd, "socket"))
+ goto out;
+ if (!ASSERT_OK(bpf_map_update_elem(map_fd, &sk_fd, &value, BPF_NOEXIST),
+ "add socket storage"))
+ goto out;
+ if (!ASSERT_OK(bpf_obj_pin(map_fd, PIN_PATH), "pin map"))
+ goto out;
+
+ /*
+ * Socket storage can't be iterated, so the dump must fail with an
+ * error and nothing else, rather than print an empty map that holds
+ * an element. The buffer is cleared before each run so that a run
+ * that fails to start can't match stale or uninitialised output.
+ */
+ ret = get_bpftool_command_output("-j map dump pinned " PIN_PATH,
+ output, sizeof(output));
+ ASSERT_NEQ(ret, 0, "json dump fails");
+ ASSERT_STRNEQ(output, JSON_ERROR, strlen(JSON_ERROR),
+ "json dump is only the error");
+
+ memset(output, 0, sizeof(output));
+ ret = get_bpftool_command_output("map dump pinned " PIN_PATH,
+ output, sizeof(output));
+ ASSERT_NEQ(ret, 0, "plain dump fails");
+ ASSERT_NULL(strchr(output, '['), "plain dump prints no elements");
+ ASSERT_NULL(strstr(output, "Found"), "plain dump prints no count");
+
+ memset(output, 0, sizeof(output));
+ get_bpftool_command_output("map dump pinned " PIN_PATH " 2>&1 >/dev/null",
+ output, sizeof(output));
+ ASSERT_HAS_SUBSTR(output, "can't dump sk_storage map",
+ "plain dump explains why on stderr");
+
+ unlink(PIN_PATH);
+out:
+ if (sk_fd >= 0)
+ close(sk_fd);
+ close(map_fd);
+}
--
2.47.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH bpf-next v2 1/2] bpftool: Fail map dump for maps that can't be iterated
2026-09-25 16:20 ` [PATCH bpf-next v2 1/2] " Maxim Skokov
@ 2026-09-25 19:24 ` Quentin Monnet
2026-09-26 1:44 ` Tianyi Chen
0 siblings, 1 reply; 5+ messages in thread
From: Quentin Monnet @ 2026-09-25 19:24 UTC (permalink / raw)
To: Maxim Skokov, bpf, Tianyi Chen
Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, kuba, shuah,
linux-kselftest
2026-09-25 19:20 UTC+0300 ~ Maxim Skokov <skokovmaksimevg@gmail.com>
> "bpftool map dump" walks the map with bpf_map_get_next_key() and treats
> ENOENT as the end of the map. Map types that can't be iterated fail that
> call on the very first key: local storage and ringbuf with ENOTSUPP,
> bloom filter and arena with EOPNOTSUPP, queue and stack with EINVAL.
> bpftool then ends the walk without a word and prints what it prints for
> an empty map, "[]" with BTF or "Found 0 elements" without, and only the
> exit status tells that the map was not read. On a socket storage map
> that holds storage for a socket:
>
> # bpftool map dump pinned /sys/fs/bpf/sk_storage
> []
>
> Ask for the first key before printing anything, and when that fails with
> anything but ENOENT, fail with the map type and the error instead of a
> dump:
>
> # bpftool map dump pinned /sys/fs/bpf/sk_storage
> Error: can't dump sk_storage map: Unknown error 524
> # bpftool -j map dump pinned /sys/fs/bpf/sk_storage
> {"error":"can't dump sk_storage map: Unknown error 524"}
>
> An error later in the walk is now reported too, as "bpftool map getnext"
> reports it, and the element count is printed only after a complete walk.
>
> Assisted-by: LLM
> Signed-off-by: Maxim Skokov <skokovmaksimevg@gmail.com>
> ---
> tools/bpf/bpftool/map.c | 26 ++++++++++++++++++++++++--
> 1 file changed, 24 insertions(+), 2 deletions(-)
>
> diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
> index 20d59eab09..626882f207 100644
> --- a/tools/bpf/bpftool/map.c
> +++ b/tools/bpf/bpftool/map.c
> @@ -856,6 +856,25 @@ map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr,
> }
> }
>
> + /*
> + * Map types that can't be iterated fail on the very first key. Say so
> + * before printing anything, so that the output isn't an empty dump.
> + */
Or simply:
/* Fail early to avoid an empty dump if map type cannot be iterated */
But that's a minor nit, the patch looks good to me, thank you!
Acked-by: Quentin Monnet <qmo@kernel.org>
Looks like you need to address Sashiko's findings on patch 2, though.
> + if (bpf_map_get_next_key(fd, NULL, key) && errno != ENOENT) {
> + const char *map_type_str;
> + int saved_errno = errno;
> +
> + map_type_str = libbpf_bpf_map_type_str(info->type);
> + if (map_type_str)
> + p_err("can't dump %s map: %s", map_type_str,
> + strerror(saved_errno));
> + else
> + p_err("can't dump map of type %u: %s", info->type,
> + strerror(saved_errno));
> + err = -1;
> + goto exit_free;
> + }
Tianyi, note that this conflicts with your series ("bpftool: Add
recursive map dumping"), one of you will have to rebase.
> +
> if (wtr) {
> err = get_map_kv_btf(info, &btf);
> if (err) {
> @@ -883,8 +902,11 @@ map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr,
> while (true) {
> err = bpf_map_get_next_key(fd, prev_key, key);
> if (err) {
> - if (errno == ENOENT)
> + if (errno == ENOENT) {
> err = 0;
> + break;
> + }
> + p_err("can't get next key: %s", strerror(errno));
> break;
> }
> if (!dump_map_elem(fd, key, value, info, btf, wtr,
> @@ -897,7 +919,7 @@ map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr,
> jsonw_end_array(wtr); /* elements */
> if (show_header)
> jsonw_end_object(wtr); /* map object */
> - } else {
> + } else if (!err) {
> printf("Found %u element%s\n", num_elems,
> num_elems != 1 ? "s" : "");
> }
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH bpf-next v2 1/2] bpftool: Fail map dump for maps that can't be iterated
2026-09-25 19:24 ` Quentin Monnet
@ 2026-09-26 1:44 ` Tianyi Chen
0 siblings, 0 replies; 5+ messages in thread
From: Tianyi Chen @ 2026-09-26 1:44 UTC (permalink / raw)
To: qmo, skokovmaksimevg
Cc: bpf, ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, kuba, shuah,
linux-kselftest
Hi Quentin, Maxim,
> Tianyi, note that this conflicts with your series ("bpftool: Add
> recursive map dumping"), one of you will have to rebase.
I'll take care of rebasing the recursive dump series. I have posted v9
on current bpf-next plus Maxim's v2 patch 1/2, explicitly listed as a
prerequisite in the cover letter:
https://lore.kernel.org/r/20260926014208.24773-1-hi@tychen.cc
The conflict resolution keeps the early iteration check before recursive
plain/BTF output and retains Maxim's iteration-error reporting. All 15
recursive dump subtests pass on the combined tree. I also checked that
non-iterable ringbuf dumps fail with valid JSON, with and without recursion.
Maxim, please go ahead with your revisions; no need to rebase around my
series. I'll adjust mine if your implementation changes.
Thanks,
Tianyi
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-26 1:44 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-25 16:20 [PATCH bpf-next v2 0/2] bpftool: Fail map dump for maps that can't be iterated Maxim Skokov
2026-09-25 16:20 ` [PATCH bpf-next v2 1/2] " Maxim Skokov
2026-09-25 19:24 ` Quentin Monnet
2026-09-26 1:44 ` Tianyi Chen
2026-09-25 16:20 ` [PATCH bpf-next v2 2/2] selftests/bpf: Check that bpftool map dump fails for socket storage Maxim Skokov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox