On 3/18/25 7:33 AM, Anton Protopopov wrote:
Tests are split in two parts. The `bpf_insn_set_ops` test checks that the map is managed properly: * Incorrect instruction indexes are rejected * Non-sorted and non-unique indexes are rejected * Unfrozen maps are not accepted * Two programs can't use the same map * BPF progs can't operate the map The `bpf_insn_set_reloc` part validates, as best as it can do it from user space, that instructions are relocated properly: * no relocations => map is the same * expected relocations when instructions are added * expected relocations when instructions are deleted * expected relocations when multiple functions are present Signed-off-by: Anton Protopopov <aspsk@xxxxxxxxxxxxx> --- .../selftests/bpf/prog_tests/bpf_insn_set.c | 639 ++++++++++++++++++ 1 file changed, 639 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/bpf_insn_set.c diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_insn_set.c b/tools/testing/selftests/bpf/prog_tests/bpf_insn_set.c new file mode 100644 index 000000000000..796980bd4fcb --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/bpf_insn_set.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <bpf/bpf.h> +#include <test_progs.h> + +static inline int map_create(__u32 map_type, __u32 max_entries) +{ + const char *map_name = "insn_set"; + __u32 key_size = 4; + __u32 value_size = 4; + + return bpf_map_create(map_type, map_name, key_size, value_size, max_entries, NULL); +} + +/* + * Load a program, which will not be anyhow mangled by the verifier. Add an + * insn_set map pointing to every instruction. Check that it hasn't changed + * after the program load. + */ +static void check_one_to_one_mapping(void) +{ + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 4), + BPF_MOV64_IMM(BPF_REG_0, 3), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog_fd, map_fd;
prog_fd needs to be initialized to something like -1.
+ union bpf_attr attr = { + .prog_type = BPF_PROG_TYPE_XDP, /* we don't care */ + .insns = ptr_to_u64(insns), + .insn_cnt = ARRAY_SIZE(insns), + .license = ptr_to_u64("GPL"), + .fd_array = ptr_to_u64(&map_fd), + .fd_array_cnt = 1, + }; + int i; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, ARRAY_SIZE(insns)); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + for (i = 0; i < ARRAY_SIZE(insns); i++) + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &i, 0), 0, "bpf_map_update_elem")) + goto cleanup;
Otherwise, goto cleanup here will goto cleanup and close(prog_fd) will close a random prog_fd. Please check the rest of prog_fd usage.
+ + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + return; + + prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(insns); i++) { + __u32 val; + + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) + goto cleanup; + + ASSERT_EQ(val, i, "val should be equal i"); + } + +cleanup: + close(prog_fd); + close(map_fd); +} +
[...]