[PATCH bpf-next 2/2] selftests/bpf: Cover symbolic bpftool map creation flags

From: Tianyi Chen

Date: Sun Sep 06 2026 - 13:13:15 EST


Check numeric and symbolic map creation flags, including combined and
repeated names, against map information read independently with libbpf.
Exercise malformed names and lists, update-only flags, empty input and
numeric range errors. Verify rejected input leaves no pinned map.

Assisted-by: Codex:GPT-6
Signed-off-by: Tianyi Chen <hi@xxxxxxxxx>
---
.../bpf/prog_tests/bpftool_map_flags.c | 99 +++++++++++++++++++
1 file changed, 99 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/bpftool_map_flags.c

diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_map_flags.c b/tools/testing/selftests/bpf/prog_tests/bpftool_map_flags.c
new file mode 100644
index 00000000000..140401dd254
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_map_flags.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <errno.h>
+#include <unistd.h>
+#include <bpf/bpf.h>
+#include <bpftool_helpers.h>
+#include <test_progs.h>
+
+static const struct map_flags_test {
+ const char *name;
+ const char *flags;
+ __u32 expected_flags;
+ const char *error;
+} tests[] = {
+ { "zero", "0", 0 },
+ { "decimal", "129", BPF_F_NO_PREALLOC | BPF_F_RDONLY_PROG },
+ { "hexadecimal", "0x81", BPF_F_NO_PREALLOC | BPF_F_RDONLY_PROG },
+ { "octal", "0201", BPF_F_NO_PREALLOC | BPF_F_RDONLY_PROG },
+ { "positive_sign", "+1", BPF_F_NO_PREALLOC },
+ { "single_name", "BPF_F_NO_PREALLOC", BPF_F_NO_PREALLOC },
+ { "combined_names", "BPF_F_NO_PREALLOC,BPF_F_RDONLY_PROG",
+ BPF_F_NO_PREALLOC | BPF_F_RDONLY_PROG },
+ { "repeated_name", "BPF_F_NO_PREALLOC,BPF_F_NO_PREALLOC", BPF_F_NO_PREALLOC },
+ { "unknown_name", "BPF_F_NOT_A_MAP_FLAG", 0, "can't parse" },
+ { "other_command_flag", "BPF_F_PATH_FD", 0, "can't parse" },
+ { "update_flag", "BPF_F_LOCK", 0, "can't parse" },
+ { "abbreviated_name", "BPF_F_NO_PRE", 0, "can't parse" },
+ { "lowercase_name", "bpf_f_no_prealloc", 0, "can't parse" },
+ { "empty", "", 0, "can't parse" },
+ { "whitespace", " ", 0, "can't parse" },
+ { "empty_list", ",", 0, "can't parse" },
+ { "leading_comma", ",BPF_F_NO_PREALLOC", 0, "can't parse" },
+ { "trailing_comma", "BPF_F_NO_PREALLOC,", 0, "can't parse" },
+ { "empty_element", "BPF_F_NO_PREALLOC,,BPF_F_RDONLY_PROG", 0, "can't parse" },
+ { "number_then_name", "1,BPF_F_RDONLY_PROG", 0, "can't parse" },
+ { "name_then_number", "BPF_F_NO_PREALLOC,128", 0, "can't parse" },
+ { "numeric_list", "1,128", 0, "can't parse" },
+ { "whitespace_in_list", "BPF_F_NO_PREALLOC, BPF_F_RDONLY_PROG", 0, "can't parse" },
+ { "overflow_u32", "4294967296", 0, "can't parse" },
+ { "overflow_hex", "0x100000000", 0, "can't parse" },
+ { "overflow_u64", "18446744073709551616", 0, "can't parse" },
+ { "negative", "-1", 0, "can't parse" },
+ /* Numeric bits unknown to bpftool must still reach the kernel. */
+ { "all_bits", "0xffffffff", 0, "map create failed" },
+ { "invalid_combination", "BPF_F_RDONLY,BPF_F_WRONLY", 0, "map create failed" },
+};
+
+static void test_map_flags(const struct map_flags_test *test, const char *path)
+{
+ char cmd[MAX_BPFTOOL_CMD_LEN], output[1024] = {};
+ struct bpf_map_info info = {};
+ __u32 info_len = sizeof(info);
+ int fd, err;
+
+ /* Let the flags parser handle negative numbers instead of getopt(). */
+ err = snprintf(cmd, sizeof(cmd),
+ "-- map create %s type hash key 4 value 4 entries 1 name flags_test flags '%s' 2>&1",
+ path, test->flags);
+ if (!ASSERT_GT(err, 0, "format_command") ||
+ !ASSERT_LT(err, sizeof(cmd), "command_length"))
+ return;
+
+ err = get_bpftool_command_output(cmd, output, sizeof(output));
+ if (test->error) {
+ ASSERT_NEQ(err, 0, "reject_flags");
+ ASSERT_HAS_SUBSTR(output, test->error, "error_message");
+ err = access(path, F_OK);
+ ASSERT_EQ(err, -1, "no_pin");
+ ASSERT_EQ(errno, ENOENT, "pin_absent");
+ goto cleanup;
+ }
+ if (!ASSERT_OK(err, "create_map"))
+ goto cleanup;
+
+ fd = bpf_obj_get(path);
+ if (!ASSERT_OK_FD(fd, "open_map"))
+ goto cleanup;
+ if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &info_len), "map_info"))
+ ASSERT_EQ(info.map_flags, test->expected_flags, "map_flags");
+ close(fd);
+cleanup:
+ unlink(path);
+}
+
+void test_bpftool_map_flags(void)
+{
+ char dir[] = "/sys/fs/bpf/bpftool_flags_XXXXXX";
+ char path[sizeof(dir) + sizeof("/map")];
+ int i;
+
+ if (!ASSERT_OK_PTR(mkdtemp(dir), "create_pin_dir"))
+ return;
+ snprintf(path, sizeof(path), "%s/map", dir);
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ if (test__start_subtest(tests[i].name))
+ test_map_flags(&tests[i], path);
+ }
+ ASSERT_OK(rmdir(dir), "remove_pin_dir");
+}
--
2.55.0