summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/bpf/prog_tests')
-rw-r--r--tools/testing/selftests/bpf/prog_tests/cb_refs.c2
-rw-r--r--tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c192
-rw-r--r--tools/testing/selftests/bpf/prog_tests/dynptr.c10
-rw-r--r--tools/testing/selftests/bpf/prog_tests/exceptions.c1
-rw-r--r--tools/testing/selftests/bpf/prog_tests/linked_list.c4
-rw-r--r--tools/testing/selftests/bpf/prog_tests/local_kptr_ownership.c296
-rw-r--r--tools/testing/selftests/bpf/prog_tests/percpu_alloc.c105
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sock_destroy.c121
-rw-r--r--tools/testing/selftests/bpf/prog_tests/spin_lock.c2
-rw-r--r--tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c125
-rw-r--r--tools/testing/selftests/bpf/prog_tests/verifier.c4
11 files changed, 859 insertions, 3 deletions
diff --git a/tools/testing/selftests/bpf/prog_tests/cb_refs.c b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
index 78566b817fd7..490e15e7126d 100644
--- a/tools/testing/selftests/bpf/prog_tests/cb_refs.c
+++ b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
@@ -13,7 +13,7 @@ struct {
} cb_refs_tests[] = {
{ "underflow_prog", "release kfunc bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
{ "leak_prog", "Possibly NULL pointer passed to helper R2" },
- { "nested_cb", "Unreleased reference id=4 alloc_insn=2" }, /* alloc_insn=2{4,5} */
+ { "nested_cb", "Unreleased reference id=5 alloc_insn=2" }, /* alloc_insn=2{4,5} */
{ "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" }, /* alloc_insn=1{1,2} */
};
diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c b/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
index a18d3680fb16..51f42b02a267 100644
--- a/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
+++ b/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
@@ -14,6 +14,197 @@
static char log[16 * 1024];
+static int load_core_relo_insns(int btf_fd, struct bpf_insn *insns, int insn_cnt,
+ struct bpf_func_info *funcs, int func_cnt,
+ int enum_id, int access_str_off, int insn_idx,
+ bool relocate)
+{
+ struct bpf_core_relo relo = {
+ .insn_off = insn_idx * sizeof(struct bpf_insn),
+ .type_id = enum_id,
+ .access_str_off = access_str_off,
+ .kind = BPF_CORE_ENUMVAL_VALUE,
+ };
+ union bpf_attr attr = {
+ .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+ .insn_cnt = insn_cnt,
+ .insns = (__u64)insns,
+ .license = (__u64)"GPL",
+ .log_buf = (__u64)log,
+ .log_size = sizeof(log),
+ .log_level = 2,
+ .prog_btf_fd = btf_fd,
+ .func_info_rec_size = sizeof(struct bpf_func_info),
+ .func_info = (__u64)funcs,
+ .func_info_cnt = func_cnt,
+ };
+
+ if (relocate) {
+ attr.core_relo_cnt = 1;
+ attr.core_relos = (__u64)&relo;
+ attr.core_relo_rec_size = sizeof(relo);
+ }
+ memset(log, 0, sizeof(log));
+ return sys_bpf_prog_load(&attr, sizeof(attr), 1);
+}
+
+static void test_early_core_relo(void)
+{
+ static const char unrecognized[] = "trying to relocate unrecognized insn #2";
+ static const struct {
+ const char *name;
+ struct bpf_insn insns[2];
+ const char *err_msg;
+ } tests[] = {
+ { "poison_exit", { BPF_EXIT_INSN() }, unrecognized },
+ { "poison_ja", { BPF_JMP_A(1) }, unrecognized },
+ { "poison_jmp", { BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1) }, unrecognized },
+ { "poison_jmp32", { BPF_JMP32_IMM(BPF_JEQ, BPF_REG_0, 0, 1) }, unrecognized },
+ { "poison_call", { BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32) }, unrecognized },
+ { "poison_alu_reg", { BPF_MOV32_REG(BPF_REG_0, BPF_REG_1) }, unrecognized },
+ { "poison_alu64_reg", { BPF_MOV64_REG(BPF_REG_0, BPF_REG_1) }, unrecognized },
+ { "poison_ld_abs", { BPF_LD_ABS(BPF_W, 0) },
+ "insn #2 (LDIMM64) has unexpected form" },
+ { "poison_alu_imm", { BPF_MOV32_IMM(BPF_REG_0, 0) } },
+ { "poison_alu64_imm", { BPF_MOV64_IMM(BPF_REG_0, 0) } },
+ { "poison_ldx", { BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0) } },
+ { "poison_st", { BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0) } },
+ { "poison_stx", { BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4) } },
+ { "poison_ldimm64", { BPF_LD_IMM64(BPF_REG_0, 0) } },
+ };
+ struct test_btf {
+ struct btf_header hdr;
+ __u32 types[18];
+ char strings[64];
+ } raw_btf = {
+ .hdr = {
+ .magic = BTF_MAGIC,
+ .version = BTF_VERSION,
+ .hdr_len = sizeof(struct btf_header),
+ .type_off = 0,
+ .type_len = sizeof(raw_btf.types),
+ .str_off = offsetof(struct test_btf, strings) -
+ offsetof(struct test_btf, types),
+ .str_len = sizeof(raw_btf.strings),
+ },
+ .types = {
+ BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4), /* [1] int */
+ BTF_FUNC_PROTO_ENC(1, 0), /* [2] int (*)(void) */
+ BTF_FUNC_ENC(5, 2), /* [3] main_fn */
+ BTF_FUNC_ENC(13, 2), /* [4] sub_fn */
+ BTF_TYPE_ENC(20, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), /* [5] enum */
+ BTF_ENUM_ENC(45, 0), /* value = 0 */
+ },
+ .strings = "\0int\0main_fn\0sub_fn\0core_relo_poison_missing\0value\0" "0",
+ };
+ struct bpf_func_info funcs[] = {
+ { .insn_off = 0, .type_id = 3 },
+ { .insn_off = 3, .type_id = 4 },
+ };
+ struct bpf_insn core_only[] = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ struct bpf_insn subprog[] = {
+ BPF_CALL_REL(2),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ struct bpf_insn truncated_ldimm64[] = {
+ BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0),
+ };
+ int access_str_off = 51; /* offset of "0" */
+ int enum_id = 5;
+ int btf_fd, prog_fd = -1, i;
+
+ btf_fd = bpf_btf_load(&raw_btf, sizeof(raw_btf), NULL);
+ if (!ASSERT_GE(btf_fd, 0, "btf_load"))
+ goto cleanup;
+
+ if (test__start_subtest("without_func_info")) {
+ prog_fd = load_core_relo_insns(btf_fd, core_only, ARRAY_SIZE(core_only), NULL, 0,
+ enum_id, access_str_off, 2, false);
+ if (!ASSERT_GE(prog_fd, 0, "control_load"))
+ goto cleanup;
+ close(prog_fd);
+ prog_fd = load_core_relo_insns(btf_fd, core_only, ARRAY_SIZE(core_only), NULL, 0,
+ enum_id, access_str_off, 2, true);
+ if (!ASSERT_GE(prog_fd, 0, "poisoned_load"))
+ goto cleanup;
+ ASSERT_HAS_SUBSTR(log, "substituting insn #2", "poison_log");
+ close(prog_fd);
+ prog_fd = -1;
+ }
+
+ if (test__start_subtest("before_subprog_validation")) {
+ prog_fd = load_core_relo_insns(btf_fd, subprog, ARRAY_SIZE(subprog), funcs, 2,
+ enum_id, access_str_off, 2, true);
+ if (!ASSERT_LT(prog_fd, 0, "poisoned_load"))
+ goto cleanup;
+ ASSERT_HAS_SUBSTR(log, "substituting insn #2", "poison_log");
+ ASSERT_HAS_SUBSTR(log, "last insn is not an exit or jmp", "poisoned_load_log");
+ }
+
+ if (test__start_subtest("truncated_ldimm64")) {
+ prog_fd = load_core_relo_insns(btf_fd, truncated_ldimm64,
+ ARRAY_SIZE(truncated_ldimm64), NULL, 0,
+ enum_id, access_str_off, 0, true);
+ if (!ASSERT_LT(prog_fd, 0, "truncated_load"))
+ goto cleanup;
+ ASSERT_HAS_SUBSTR(log, "invalid bpf_ld_imm64 insn", "truncated_load_log");
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ struct bpf_insn insns[] = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
+ tests[i].insns[0],
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ bool is_ldimm64 = insns[2].code == (BPF_LD | BPF_DW | BPF_IMM);
+
+ if (!test__start_subtest(tests[i].name))
+ continue;
+ if (is_ldimm64) {
+ insns[1].off = 2;
+ insns[3] = tests[i].insns[1];
+ }
+ prog_fd = load_core_relo_insns(btf_fd, insns, ARRAY_SIZE(insns), funcs, 1,
+ enum_id, access_str_off, 2, false);
+ if (!ASSERT_GE(prog_fd, 0, "control_load"))
+ goto cleanup;
+ close(prog_fd);
+ prog_fd = load_core_relo_insns(btf_fd, insns, ARRAY_SIZE(insns), funcs, 1,
+ enum_id, access_str_off, 2, true);
+ if (!tests[i].err_msg) {
+ ASSERT_GE(prog_fd, 0, "dead_poison_load");
+ ASSERT_HAS_SUBSTR(log, "substituting insn #2", "poison_log");
+ if (is_ldimm64)
+ ASSERT_HAS_SUBSTR(log, "substituting insn #3", "poison_ldimm64_log");
+ } else {
+ ASSERT_LT(prog_fd, 0, "invalid_poison_load");
+ ASSERT_HAS_SUBSTR(log, tests[i].err_msg, "invalid_poison_log");
+ ASSERT_NULL(strstr(log, "substituting insn"), "invalid_poison_substitution");
+ }
+ close(prog_fd);
+ prog_fd = -1;
+ }
+
+cleanup:
+ if (env.verbosity > VERBOSE_NORMAL && log[0]) {
+ printf("-------- program load log start --------\n");
+ printf("%s", log);
+ printf("-------- program load log end ----------\n");
+ }
+ close(prog_fd);
+ close(btf_fd);
+}
+
/* Check that verifier rejects BPF program containing relocation
* pointing to non-existent BTF type.
*/
@@ -120,6 +311,7 @@ out:
void test_core_reloc_raw(void)
{
+ test_early_core_relo();
if (test__start_subtest("bad_local_id"))
test_bad_local_id();
}
diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c
index 5fda11590708..4396560365e8 100644
--- a/tools/testing/selftests/bpf/prog_tests/dynptr.c
+++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c
@@ -9,6 +9,7 @@
enum test_setup_type {
SETUP_SYSCALL_SLEEP,
SETUP_SKB_PROG,
+ SETUP_SKB_PROG_NONLINEAR,
SETUP_SKB_PROG_TP,
SETUP_XDP_PROG,
};
@@ -32,6 +33,7 @@ static struct {
{"test_ringbuf", SETUP_SYSCALL_SLEEP},
{"test_skb_readonly", SETUP_SKB_PROG},
{"test_dynptr_skb_data", SETUP_SKB_PROG},
+ {"test_dynptr_skb_slice_non_linear", SETUP_SKB_PROG_NONLINEAR},
{"test_dynptr_skb_meta_data", SETUP_SKB_PROG},
{"test_dynptr_skb_meta_flags", SETUP_SKB_PROG},
{"test_adjust", SETUP_SYSCALL_SLEEP},
@@ -94,7 +96,9 @@ static void verify_success(const char *prog_name, enum test_setup_type setup_typ
bpf_link__destroy(link);
break;
case SETUP_SKB_PROG:
+ case SETUP_SKB_PROG_NONLINEAR:
{
+ struct __sk_buff ctx = {};
int prog_fd;
char buf[64];
@@ -106,6 +110,12 @@ static void verify_success(const char *prog_name, enum test_setup_type setup_typ
.repeat = 1,
);
+ if (setup_type == SETUP_SKB_PROG_NONLINEAR) {
+ ctx.data_end = ETH_HLEN + sizeof(struct iphdr);
+ topts.ctx_in = &ctx;
+ topts.ctx_size_in = sizeof(ctx);
+ }
+
prog_fd = bpf_program__fd(prog);
if (!ASSERT_GE(prog_fd, 0, "prog_fd"))
goto cleanup;
diff --git a/tools/testing/selftests/bpf/prog_tests/exceptions.c b/tools/testing/selftests/bpf/prog_tests/exceptions.c
index 3588d6f97fd4..639866ce09a9 100644
--- a/tools/testing/selftests/bpf/prog_tests/exceptions.c
+++ b/tools/testing/selftests/bpf/prog_tests/exceptions.c
@@ -55,6 +55,7 @@ static void test_exceptions_success(void)
RUN_SUCCESS(exception_ext, 0);
RUN_SUCCESS(exception_ext_mod_cb_runtime, 35);
RUN_SUCCESS(exception_throw_subprog, 1);
+ RUN_SUCCESS(exception_throw_subprog_stack_cb, 0x1234);
RUN_SUCCESS(exception_assert_nz_gfunc, 1);
RUN_SUCCESS(exception_assert_zero_gfunc, 1);
RUN_SUCCESS(exception_assert_neg_gfunc, 1);
diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c
index c3d133c6a00d..52fabbee3dd5 100644
--- a/tools/testing/selftests/bpf/prog_tests/linked_list.c
+++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c
@@ -714,7 +714,7 @@ static void test_btf(void)
break;
err = btf__load_into_kernel(btf);
- ASSERT_EQ(err, -ELOOP, "check btf");
+ ASSERT_EQ(err, 0, "check btf");
btf__free(btf);
break;
}
@@ -773,7 +773,7 @@ static void test_btf(void)
break;
err = btf__load_into_kernel(btf);
- ASSERT_EQ(err, -ELOOP, "check btf");
+ ASSERT_EQ(err, 0, "check btf");
btf__free(btf);
break;
}
diff --git a/tools/testing/selftests/bpf/prog_tests/local_kptr_ownership.c b/tools/testing/selftests/bpf/prog_tests/local_kptr_ownership.c
new file mode 100644
index 000000000000..a487aa68f2ee
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/local_kptr_ownership.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <bpf/btf.h>
+#include <linux/btf.h>
+#include <test_progs.h>
+
+#define SPIN_LOCK 2
+#define LIST_HEAD 3
+#define LIST_NODE 4
+/* Keep in sync with BTF_MAX_OWNERSHIP_DEPTH. */
+#define MAX_OWNERSHIP_DEPTH 8
+
+static struct btf *init_btf(void)
+{
+ struct btf *btf;
+ int id;
+
+ btf = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf, "btf__new_empty"))
+ return NULL;
+ id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
+ if (!ASSERT_EQ(id, 1, "btf__add_int"))
+ goto err_out;
+ id = btf__add_struct(btf, "bpf_spin_lock", 4);
+ if (!ASSERT_EQ(id, SPIN_LOCK, "btf__add_struct bpf_spin_lock"))
+ goto err_out;
+ id = btf__add_struct(btf, "bpf_list_head", 16);
+ if (!ASSERT_EQ(id, LIST_HEAD, "btf__add_struct bpf_list_head"))
+ goto err_out;
+ id = btf__add_struct(btf, "bpf_list_node", 24);
+ if (!ASSERT_EQ(id, LIST_NODE, "btf__add_struct bpf_list_node"))
+ goto err_out;
+ return btf;
+
+err_out:
+ btf__free(btf);
+ return NULL;
+}
+
+static int add_local_kptr(struct btf *btf, int pointee_id, const char *tag)
+{
+ int id;
+
+ id = btf__add_type_tag(btf, tag, pointee_id);
+ if (!ASSERT_GT(id, 0, "btf__add_type_tag"))
+ return id;
+ id = btf__add_ptr(btf, id);
+ ASSERT_GT(id, 0, "btf__add_ptr");
+ return id;
+}
+
+static void test_self_cycle(const char *tag, int expected_err)
+{
+ struct btf *btf;
+ int id, err;
+
+ btf = init_btf();
+ if (!ASSERT_OK_PTR(btf, "init_btf"))
+ return;
+ id = add_local_kptr(btf, 7, tag);
+ if (id <= 0)
+ goto out;
+ id = btf__add_struct(btf, "self_cycle", 8);
+ if (!ASSERT_EQ(id, 7, "btf__add_struct self_cycle"))
+ goto out;
+ err = btf__add_field(btf, "next", 6, 0, 0);
+ if (!ASSERT_OK(err, "btf__add_field self_cycle::next"))
+ goto out;
+
+ err = btf__load_into_kernel(btf);
+ ASSERT_EQ(err, expected_err, "check btf");
+out:
+ btf__free(btf);
+}
+
+static void test_aba_cycle(void)
+{
+ struct btf *btf;
+ int id, err;
+
+ btf = init_btf();
+ if (!ASSERT_OK_PTR(btf, "init_btf"))
+ return;
+ id = add_local_kptr(btf, 10, "kptr");
+ if (id <= 0)
+ goto out;
+ id = add_local_kptr(btf, 9, "kptr");
+ if (id <= 0)
+ goto out;
+ id = btf__add_struct(btf, "cycle_a", 8);
+ if (!ASSERT_EQ(id, 9, "btf__add_struct cycle_a"))
+ goto out;
+ err = btf__add_field(btf, "b", 6, 0, 0);
+ if (!ASSERT_OK(err, "btf__add_field cycle_a::b"))
+ goto out;
+ id = btf__add_struct(btf, "cycle_b", 8);
+ if (!ASSERT_EQ(id, 10, "btf__add_struct cycle_b"))
+ goto out;
+ err = btf__add_field(btf, "a", 8, 0, 0);
+ if (!ASSERT_OK(err, "btf__add_field cycle_b::a"))
+ goto out;
+
+ err = btf__load_into_kernel(btf);
+ ASSERT_EQ(err, -ELOOP, "check btf");
+out:
+ btf__free(btf);
+}
+
+static void test_mixed_cycle(void)
+{
+ struct btf *btf;
+ int id, err;
+
+ btf = init_btf();
+ if (!ASSERT_OK_PTR(btf, "init_btf"))
+ return;
+ id = add_local_kptr(btf, 7, "kptr");
+ if (id <= 0)
+ goto out;
+ id = btf__add_struct(btf, "mixed_owner", 20);
+ if (!ASSERT_EQ(id, 7, "btf__add_struct mixed_owner"))
+ goto out;
+ err = btf__add_field(btf, "root", LIST_HEAD, 0, 0);
+ if (!ASSERT_OK(err, "btf__add_field mixed_owner::root"))
+ goto out;
+ err = btf__add_field(btf, "lock", SPIN_LOCK, 128, 0);
+ if (!ASSERT_OK(err, "btf__add_field mixed_owner::lock"))
+ goto out;
+ id = btf__add_decl_tag(btf, "contains:mixed_node:node", 7, 0);
+ if (!ASSERT_EQ(id, 8, "btf__add_decl_tag mixed_owner"))
+ goto out;
+ id = btf__add_struct(btf, "mixed_node", 32);
+ if (!ASSERT_EQ(id, 9, "btf__add_struct mixed_node"))
+ goto out;
+ err = btf__add_field(btf, "node", LIST_NODE, 0, 0);
+ if (!ASSERT_OK(err, "btf__add_field mixed_node::node"))
+ goto out;
+ err = btf__add_field(btf, "owner", 6, 192, 0);
+ if (!ASSERT_OK(err, "btf__add_field mixed_node::owner"))
+ goto out;
+
+ err = btf__load_into_kernel(btf);
+ ASSERT_EQ(err, -ELOOP, "check btf");
+out:
+ btf__free(btf);
+}
+
+static void test_acyclic_depth(int depth, bool child_first, bool shared_suffix, int expected_err)
+{
+ int ptr_id[MAX_OWNERSHIP_DEPTH + 1];
+ int first_struct_id;
+ struct btf *btf;
+ int id, err, i, n, pointee_id;
+
+ btf = init_btf();
+ if (!ASSERT_OK_PTR(btf, "init_btf"))
+ return;
+ first_struct_id = 5 + 2 * depth;
+ for (i = 0; i < depth; i++) {
+ if (i == depth - 1)
+ pointee_id = first_struct_id + depth;
+ else
+ pointee_id = first_struct_id + (child_first ? depth - 2 - i : i + 1);
+ ptr_id[i] = add_local_kptr(btf, pointee_id, "kptr");
+ if (ptr_id[i] <= 0)
+ goto out;
+ }
+ for (n = 0; n < depth; n++) {
+ char name[32];
+ int offset = 0;
+
+ i = child_first ? depth - 1 - n : n;
+ snprintf(name, sizeof(name), "owner_%d", i);
+ id = btf__add_struct(btf, name, shared_suffix && !i ? 16 : 8);
+ if (!ASSERT_EQ(id, first_struct_id + n, "btf__add_struct owner"))
+ goto out;
+ if (shared_suffix && !i) {
+ /*
+ * Visit the shared suffix through the shorter path before
+ * reaching it again with less remaining depth.
+ */
+ err = btf__add_field(btf, "suffix", ptr_id[1], 0, 0);
+ if (!ASSERT_OK(err, "btf__add_field owner::suffix"))
+ goto out;
+ offset = 64;
+ }
+ err = btf__add_field(btf, "next", ptr_id[i], offset, 0);
+ if (!ASSERT_OK(err, "btf__add_field owner::next"))
+ goto out;
+ }
+ id = btf__add_struct(btf, "plain_leaf", 4);
+ if (!ASSERT_EQ(id, first_struct_id + depth, "btf__add_struct plain_leaf"))
+ goto out;
+
+ err = btf__load_into_kernel(btf);
+ ASSERT_EQ(err, expected_err, "check btf");
+out:
+ btf__free(btf);
+}
+
+static void test_graph_depth(bool rbtree, int depth, int expected_err)
+{
+ int root_type = LIST_HEAD, node_type = LIST_NODE, node_size = 24;
+ int id, err, i, lock_off, root_off, size;
+ struct btf *btf;
+
+ btf = init_btf();
+ if (!ASSERT_OK_PTR(btf, "init_btf"))
+ return;
+ if (rbtree) {
+ root_type = btf__add_struct(btf, "bpf_rb_root", 16);
+ if (!ASSERT_GT(root_type, 0, "btf__add_struct bpf_rb_root"))
+ goto out;
+ node_type = btf__add_struct(btf, "bpf_rb_node", 32);
+ if (!ASSERT_GT(node_type, 0, "btf__add_struct bpf_rb_node"))
+ goto out;
+ node_size = 32;
+ }
+
+ for (i = 0; i < depth; i++) {
+ char name[32], tag[64];
+
+ lock_off = i ? node_size : 0;
+ root_off = lock_off + 8;
+ size = i == depth - 1 ? node_size : root_off + 16;
+ snprintf(name, sizeof(name), "graph_owner_%d", i);
+ id = btf__add_struct(btf, name, size);
+ if (!ASSERT_GT(id, 0, "btf__add_struct graph_owner"))
+ goto out;
+ if (i) {
+ err = btf__add_field(btf, "node", node_type, 0, 0);
+ if (!ASSERT_OK(err, "btf__add_field graph_owner::node"))
+ goto out;
+ }
+ if (i == depth - 1)
+ continue;
+ err = btf__add_field(btf, "lock", SPIN_LOCK, lock_off * 8, 0);
+ if (!ASSERT_OK(err, "btf__add_field graph_owner::lock"))
+ goto out;
+ err = btf__add_field(btf, "root", root_type, root_off * 8, 0);
+ if (!ASSERT_OK(err, "btf__add_field graph_owner::root"))
+ goto out;
+ snprintf(tag, sizeof(tag), "contains:graph_owner_%d:node", i + 1);
+ err = btf__add_decl_tag(btf, tag, id, i ? 2 : 1);
+ if (!ASSERT_GT(err, 0, "btf__add_decl_tag graph_owner"))
+ goto out;
+ }
+
+ err = btf__load_into_kernel(btf);
+ ASSERT_EQ(err, expected_err, "check btf");
+out:
+ btf__free(btf);
+}
+
+void test_local_kptr_ownership(void)
+{
+ if (test__start_subtest("self_cycle"))
+ test_self_cycle("kptr", -ELOOP);
+ if (test__start_subtest("untrusted_self_cycle"))
+ test_self_cycle("kptr_untrusted", 0);
+ if (test__start_subtest("percpu_self_cycle"))
+ test_self_cycle("percpu_kptr", -ELOOP);
+ if (test__start_subtest("ABA_cycle"))
+ test_aba_cycle();
+ if (test__start_subtest("mixed_graph_root_cycle"))
+ test_mixed_cycle();
+ if (test__start_subtest("max_acyclic"))
+ test_acyclic_depth(MAX_OWNERSHIP_DEPTH, false, false, 0);
+ if (test__start_subtest("too_deep_acyclic"))
+ test_acyclic_depth(MAX_OWNERSHIP_DEPTH + 1, false, false, -ELOOP);
+ if (test__start_subtest("max_acyclic_child_first"))
+ test_acyclic_depth(MAX_OWNERSHIP_DEPTH, true, false, 0);
+ if (test__start_subtest("too_deep_acyclic_child_first"))
+ test_acyclic_depth(MAX_OWNERSHIP_DEPTH + 1, true, false, -ELOOP);
+ if (test__start_subtest("max_acyclic_shared_suffix"))
+ test_acyclic_depth(MAX_OWNERSHIP_DEPTH, false, true, 0);
+ if (test__start_subtest("too_deep_acyclic_shared_suffix"))
+ test_acyclic_depth(MAX_OWNERSHIP_DEPTH + 1, false, true, -ELOOP);
+ if (test__start_subtest("list_three_types"))
+ test_graph_depth(false, 3, 0);
+ if (test__start_subtest("list_four_types"))
+ test_graph_depth(false, 4, 0);
+ if (test__start_subtest("list_max_depth"))
+ test_graph_depth(false, MAX_OWNERSHIP_DEPTH, 0);
+ if (test__start_subtest("list_too_deep"))
+ test_graph_depth(false, MAX_OWNERSHIP_DEPTH + 1, -ELOOP);
+ if (test__start_subtest("rbtree_three_types"))
+ test_graph_depth(true, 3, 0);
+ if (test__start_subtest("rbtree_four_types"))
+ test_graph_depth(true, 4, 0);
+ if (test__start_subtest("rbtree_max_depth"))
+ test_graph_depth(true, MAX_OWNERSHIP_DEPTH, 0);
+ if (test__start_subtest("rbtree_too_deep"))
+ test_graph_depth(true, MAX_OWNERSHIP_DEPTH + 1, -ELOOP);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
index a72ae0b29f6e..7b4a1e24363b 100644
--- a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
+++ b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
@@ -1,4 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <sched.h>
#include <test_progs.h>
#include "cgroup_helpers.h"
#include "percpu_alloc_array.skel.h"
@@ -350,6 +352,103 @@ static void test_lru_percpu_hash_cpu_flag(void)
test_percpu_map_cpu_flag(BPF_MAP_TYPE_LRU_PERCPU_HASH);
}
+/*
+ * A BPF_F_CPU update that creates an element must zero the value on the other
+ * cpus, rather than leave them holding whatever the recycled element last
+ * contained. max_entries is 1 so the second key can only reuse the element
+ * the first one released.
+ */
+static void test_percpu_map_cpu_flag_create(enum bpf_map_type map_type, __u32 map_flags)
+{
+ LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = map_flags);
+ const u32 stale = 0xDEADC0DE, fresh = 0xC0FFEE;
+ int nr_cpus, cpu, map_fd, err, key;
+ int pinned_cpu, value_cpu;
+ cpu_set_t old_mask, new_mask;
+ bool restore_mask = false;
+ u32 value;
+ u64 flags;
+
+ nr_cpus = libbpf_num_possible_cpus();
+ if (!ASSERT_GT(nr_cpus, 0, "libbpf_num_possible_cpus"))
+ return;
+
+ if (nr_cpus < 2) {
+ test__skip();
+ return;
+ }
+
+ map_fd = bpf_map_create(map_type, "cpu_flag_create", sizeof(key), sizeof(value), 1, &opts);
+ if (!ASSERT_GE(map_fd, 0, "bpf_map_create"))
+ return;
+
+ /* NO_PREALLOC recycles per cpu, so keep the delete and the create on one cpu. */
+ err = sched_getaffinity(0, sizeof(old_mask), &old_mask);
+ if (!ASSERT_OK(err, "sched_getaffinity"))
+ goto out;
+
+ pinned_cpu = sched_getcpu();
+ if (!ASSERT_GE(pinned_cpu, 0, "sched_getcpu"))
+ goto out;
+
+ CPU_ZERO(&new_mask);
+ CPU_SET(pinned_cpu, &new_mask);
+ err = sched_setaffinity(0, sizeof(new_mask), &new_mask);
+ if (!ASSERT_OK(err, "sched_setaffinity"))
+ goto out;
+ restore_mask = true;
+
+ value_cpu = pinned_cpu ? 0 : 1;
+
+ key = 1;
+ value = stale;
+ err = bpf_map_update_elem(map_fd, &key, &value, BPF_F_ALL_CPUS);
+ if (!ASSERT_OK(err, "bpf_map_update_elem all_cpus"))
+ goto out;
+
+ err = bpf_map_delete_elem(map_fd, &key);
+ if (!ASSERT_OK(err, "bpf_map_delete_elem"))
+ goto out;
+
+ key = 2;
+ value = fresh;
+ flags = (u64)value_cpu << 32 | BPF_F_CPU;
+ err = bpf_map_update_elem(map_fd, &key, &value, flags);
+ if (!ASSERT_OK(err, "bpf_map_update_elem specified cpu"))
+ goto out;
+
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
+ value = 0;
+ flags = (u64)cpu << 32 | BPF_F_CPU;
+ err = bpf_map_lookup_elem_flags(map_fd, &key, &value, flags);
+ if (!ASSERT_OK(err, "bpf_map_lookup_elem_flags specified cpu"))
+ goto out;
+ if (!ASSERT_EQ(value, cpu == value_cpu ? fresh : 0, "value on specified cpu"))
+ goto out;
+ }
+
+out:
+ if (restore_mask)
+ sched_setaffinity(0, sizeof(old_mask), &old_mask);
+ close(map_fd);
+}
+
+static void test_percpu_hash_cpu_flag_create(void)
+{
+ test_percpu_map_cpu_flag_create(BPF_MAP_TYPE_PERCPU_HASH, 0);
+}
+
+static void test_percpu_hash_cpu_flag_create_malloc(void)
+{
+ test_percpu_map_cpu_flag_create(BPF_MAP_TYPE_PERCPU_HASH, BPF_F_NO_PREALLOC);
+}
+
+static void test_lru_percpu_hash_cpu_flag_create(void)
+{
+ /* lru without prealloc is -ENOTSUPP, so there is no malloc variant */
+ test_percpu_map_cpu_flag_create(BPF_MAP_TYPE_LRU_PERCPU_HASH, 0);
+}
+
static void test_percpu_cgroup_storage_cpu_flag(void)
{
struct percpu_alloc_array *skel = NULL;
@@ -454,6 +553,12 @@ void test_percpu_alloc(void)
test_percpu_hash_cpu_flag();
if (test__start_subtest("cpu_flag_lru_percpu_hash"))
test_lru_percpu_hash_cpu_flag();
+ if (test__start_subtest("cpu_flag_create_percpu_hash"))
+ test_percpu_hash_cpu_flag_create();
+ if (test__start_subtest("cpu_flag_create_percpu_hash_malloc"))
+ test_percpu_hash_cpu_flag_create_malloc();
+ if (test__start_subtest("cpu_flag_create_lru_percpu_hash"))
+ test_lru_percpu_hash_cpu_flag_create();
if (test__start_subtest("cpu_flag_percpu_cgroup_storage"))
test_percpu_cgroup_storage_cpu_flag();
if (test__start_subtest("cpu_flag_array"))
diff --git a/tools/testing/selftests/bpf/prog_tests/sock_destroy.c b/tools/testing/selftests/bpf/prog_tests/sock_destroy.c
index 9c11938fe597..78d642a02bdb 100644
--- a/tools/testing/selftests/bpf/prog_tests/sock_destroy.c
+++ b/tools/testing/selftests/bpf/prog_tests/sock_destroy.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+#include <poll.h>
#include <test_progs.h>
#include <bpf/bpf_endian.h>
@@ -110,6 +111,122 @@ cleanup:
close(serv);
}
+static void test_tcp_listen_pending(struct sock_destroy_prog *skel)
+{
+ int serv = -1, clien = -1, accept_serv = -1, n, serv_port;
+ struct pollfd pfd = { .events = POLLIN };
+ char buf[1];
+
+ serv = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
+ if (!ASSERT_GE(serv, 0, "start_server"))
+ goto cleanup;
+ serv_port = get_socket_local_port(serv);
+ if (!ASSERT_GE(serv_port, 0, "get_sock_local_port"))
+ goto cleanup;
+ skel->bss->serv_port = (__be16)serv_port;
+
+ /*
+ * Connect but never accept, so the child sits in the accept queue
+ * of the listener. Wait until it's actually there.
+ */
+ clien = connect_to_fd(serv, 0);
+ if (!ASSERT_GE(clien, 0, "connect_to_fd"))
+ goto cleanup;
+ pfd.fd = serv;
+ if (!ASSERT_EQ(poll(&pfd, 1, -1), 1, "poll listener"))
+ goto cleanup;
+
+ /* Run iterator program that destroys server sockets. */
+ start_iter_sockets(skel->progs.iter_tcp6_server);
+
+ accept_serv = accept(serv, NULL, NULL);
+ if (!ASSERT_LT(accept_serv, 0, "accept on destroyed listener"))
+ goto cleanup;
+ ASSERT_EQ(errno, EINVAL, "error code on destroyed listener");
+
+ /* The unaccepted child was reset along with the listener. */
+ n = recv(clien, buf, sizeof(buf), 0);
+ if (!ASSERT_LT(n, 0, "client recv on reset child"))
+ goto cleanup;
+ ASSERT_EQ(errno, ECONNRESET, "error code on reset child");
+
+cleanup:
+ if (clien != -1)
+ close(clien);
+ if (accept_serv != -1)
+ close(accept_serv);
+ if (serv != -1)
+ close(serv);
+}
+
+static void test_tcp_timewait(struct sock_destroy_prog *skel)
+{
+ int serv = -1, clien = -1, accept_serv = -1, n;
+ struct timeval tv = {};
+ char buf[1];
+
+ serv = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
+ if (!ASSERT_GE(serv, 0, "start_server"))
+ goto cleanup;
+
+ clien = connect_to_fd(serv, 0);
+ if (!ASSERT_GE(clien, 0, "connect_to_fd"))
+ goto cleanup;
+
+ accept_serv = accept(serv, NULL, NULL);
+ if (!ASSERT_GE(accept_serv, 0, "serv accept"))
+ goto cleanup;
+
+ /*
+ * Active close from the client, then close the server side. Once
+ * recv() sees EOF the server FIN has been processed and the client
+ * sock is in TIME_WAIT. Block without timeout so a loaded CI box
+ * can't race us.
+ */
+ if (!ASSERT_OK(setsockopt(clien, SOL_SOCKET, SO_RCVTIMEO, &tv,
+ sizeof(tv)), "clear rcvtimeo"))
+ goto cleanup;
+ if (!ASSERT_OK(shutdown(clien, SHUT_WR), "client shutdown"))
+ goto cleanup;
+
+ /*
+ * Make sure the server has seen the client FIN before it closes,
+ * so the two FINs never cross.
+ */
+ n = recv(accept_serv, buf, sizeof(buf), 0);
+ if (!ASSERT_EQ(n, 0, "server recv EOF"))
+ goto cleanup;
+
+ close(accept_serv);
+ accept_serv = -1;
+
+ /* block until return EOF */
+ n = recv(clien, buf, sizeof(buf), 0);
+ if (!ASSERT_EQ(n, 0, "client recv EOF"))
+ goto cleanup;
+
+ /* Run iterator program that destroys the timewait client sock. */
+ skel->bss->tw_found = 0;
+ start_iter_sockets(skel->progs.iter_tcp6_timewait);
+ if (!ASSERT_EQ(skel->bss->tw_found, 1, "timewait sock found"))
+ goto cleanup;
+
+ ASSERT_OK(skel->bss->tw_destroy_err, "destroy timewait sock");
+
+ /* The destroyed timewait sock must be gone. */
+ skel->bss->tw_found = 0;
+ start_iter_sockets(skel->progs.iter_tcp6_timewait);
+ ASSERT_EQ(skel->bss->tw_found, 0, "timewait sock destroyed");
+
+cleanup:
+ if (clien != -1)
+ close(clien);
+ if (accept_serv != -1)
+ close(accept_serv);
+ if (serv != -1)
+ close(serv);
+}
+
static void test_udp_client(struct sock_destroy_prog *skel)
{
int serv = -1, clien = -1, n = 0;
@@ -204,6 +321,10 @@ void test_sock_destroy(void)
test_tcp_client(skel);
if (test__start_subtest("tcp_server"))
test_tcp_server(skel);
+ if (test__start_subtest("tcp_listen_pending"))
+ test_tcp_listen_pending(skel);
+ if (test__start_subtest("tcp_timewait"))
+ test_tcp_timewait(skel);
if (test__start_subtest("udp_client"))
test_udp_client(skel);
if (test__start_subtest("udp_server"))
diff --git a/tools/testing/selftests/bpf/prog_tests/spin_lock.c b/tools/testing/selftests/bpf/prog_tests/spin_lock.c
index 5c3579438427..e368370262c8 100644
--- a/tools/testing/selftests/bpf/prog_tests/spin_lock.c
+++ b/tools/testing/selftests/bpf/prog_tests/spin_lock.c
@@ -54,6 +54,8 @@ static struct {
{ "lock_global_sleepable_helper_subprog", "global function calls are not allowed while holding a lock" },
{ "lock_global_sleepable_kfunc_subprog", "global function calls are not allowed while holding a lock" },
{ "lock_global_sleepable_subprog_indirect", "global function calls are not allowed while holding a lock" },
+ { "callback_value_lock_identity", "bpf_spin_unlock of different lock" },
+ { "callback_inner_map_value_lock_identity", "bpf_spin_unlock of different lock" },
};
static int match_regex(const char *pattern, const char *string)
diff --git a/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c b/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c
new file mode 100644
index 000000000000..7acdbd5757a9
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <netinet/tcp.h>
+
+#include "test_progs.h"
+#include "network_helpers.h"
+#include "test_tc_change_tail_pmtu.skel.h"
+
+#define CLIENT_NS "tc-change-tail-cli-ns"
+#define SERVER_NS "tc-change-tail-srv-ns"
+#define CLIENT_IP "192.168.1.1"
+#define SERVER_IP "192.168.1.2"
+
+#define TEST_PMTU 1000
+#define TEST_MSS_MAX (TEST_PMTU - 20 - 20)
+#define TIMEOUT_MS 3000
+#define XFER_BYTES 8192
+
+void test_tc_change_tail_pmtu(void)
+{
+ LIBBPF_OPTS(bpf_tcx_opts, tcx_opts);
+ int mss_before = 0, mss_after = 0, ifindex, port;
+ int srv_fd = -1, srv_conn_fd = -1, cli_fd = -1;
+ struct test_tc_change_tail_pmtu *skel = NULL;
+ struct nstoken *nstoken = NULL;
+ static char buf[XFER_BYTES];
+ socklen_t optlen;
+ ssize_t bytes;
+ size_t total;
+
+ if (!ASSERT_OK(make_netns(CLIENT_NS), "make client ns"))
+ return;
+ if (!ASSERT_OK(make_netns(SERVER_NS), "make server ns"))
+ goto out_client_ns;
+
+ nstoken = open_netns(CLIENT_NS);
+ if (!ASSERT_OK_PTR(nstoken, "open client ns"))
+ goto out;
+ SYS(out, "ip link add veth1 type veth peer name veth2 netns " SERVER_NS);
+ SYS(out, "ip -4 addr add " CLIENT_IP "/24 dev veth1");
+ SYS(out, "ip link set veth1 up");
+ ifindex = if_nametoindex("veth1");
+ if (!ASSERT_NEQ(ifindex, 0, "if_nametoindex"))
+ goto out;
+ close_netns(nstoken);
+ nstoken = NULL;
+
+ nstoken = open_netns(SERVER_NS);
+ if (!ASSERT_OK_PTR(nstoken, "open server ns"))
+ goto out;
+ SYS(out, "ip -4 addr add " SERVER_IP "/24 dev veth2");
+ SYS(out, "ip link set veth2 up");
+ srv_fd = start_server(AF_INET, SOCK_STREAM, SERVER_IP, 0, TIMEOUT_MS);
+ if (!ASSERT_OK_FD(srv_fd, "start server"))
+ goto out;
+ close_netns(nstoken);
+ nstoken = NULL;
+
+ skel = test_tc_change_tail_pmtu__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open and load skeleton"))
+ goto out;
+
+ port = get_socket_local_port(srv_fd);
+ if (!ASSERT_GE(port, 0, "get server port"))
+ goto out;
+
+ skel->bss->server_port = port;
+ skel->bss->pmtu = TEST_PMTU;
+
+ nstoken = open_netns(CLIENT_NS);
+ if (!ASSERT_OK_PTR(nstoken, "open client ns"))
+ goto out;
+
+ skel->links.change_tail_icmp =
+ bpf_program__attach_tcx(skel->progs.change_tail_icmp, ifindex,
+ &tcx_opts);
+ if (!ASSERT_OK_PTR(skel->links.change_tail_icmp, "attach tcx"))
+ goto out;
+
+ cli_fd = connect_to_fd(srv_fd, TIMEOUT_MS);
+ if (!ASSERT_OK_FD(cli_fd, "connect to server"))
+ goto out;
+ srv_conn_fd = accept(srv_fd, NULL, NULL);
+ if (!ASSERT_OK_FD(srv_conn_fd, "accept connection"))
+ goto out;
+ if (!ASSERT_OK(settimeo(srv_conn_fd, TIMEOUT_MS), "set server timeout"))
+ goto out;
+
+ optlen = sizeof(mss_before);
+ if (!ASSERT_OK(getsockopt(cli_fd, IPPROTO_TCP, TCP_MAXSEG, &mss_before,
+ &optlen), "get mss before"))
+ goto out;
+
+ bytes = send(cli_fd, buf, sizeof(buf), 0);
+ if (!ASSERT_EQ(bytes, (ssize_t)sizeof(buf), "send data"))
+ goto out;
+
+ for (total = 0; total < sizeof(buf); total += bytes) {
+ bytes = recv(srv_conn_fd, buf, sizeof(buf), 0);
+ if (bytes <= 0)
+ break;
+ }
+
+ ASSERT_EQ(total, sizeof(buf), "receive data");
+ ASSERT_OK(skel->data->change_tail_ret, "change tail");
+ ASSERT_OK(skel->bss->adjust_room_ret, "adjust room");
+ ASSERT_TRUE(skel->bss->icmp_sent, "icmp sent");
+
+ optlen = sizeof(mss_after);
+ if (!ASSERT_OK(getsockopt(cli_fd, IPPROTO_TCP, TCP_MAXSEG, &mss_after,
+ &optlen), "get mss after"))
+ goto out;
+
+ ASSERT_LT(mss_after, mss_before, "mss reduced");
+ ASSERT_LE(mss_after, TEST_MSS_MAX, "mss below pmtu");
+out:
+ close(srv_conn_fd);
+ close(cli_fd);
+ close(srv_fd);
+ test_tc_change_tail_pmtu__destroy(skel);
+ close_netns(nstoken);
+ remove_netns(SERVER_NS);
+out_client_ns:
+ remove_netns(CLIENT_NS);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 64ac49ad67e6..8b439e194bcc 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -23,6 +23,7 @@
#include "verifier_bpf_trap.skel.h"
#include "verifier_bswap.skel.h"
#include "verifier_btf_ctx_access.skel.h"
+#include "verifier_btf_flex_array.skel.h"
#include "verifier_btf_unreliable_prog.skel.h"
#include "verifier_call_large_imm.skel.h"
#include "verifier_cfg.skel.h"
@@ -53,6 +54,7 @@
#include "verifier_iterating_callbacks.skel.h"
#include "verifier_jeq_infer_not_null.skel.h"
#include "verifier_jit_convergence.skel.h"
+#include "verifier_kfunc_perfmon.skel.h"
#include "verifier_ld_ind.skel.h"
#include "verifier_ldsx.skel.h"
#include "verifier_leak_ptr.skel.h"
@@ -186,6 +188,7 @@ void test_verifier_bpf_get_stack(void) { RUN(verifier_bpf_get_stack); }
void test_verifier_bpf_trap(void) { RUN(verifier_bpf_trap); }
void test_verifier_bswap(void) { RUN(verifier_bswap); }
void test_verifier_btf_ctx_access(void) { RUN(verifier_btf_ctx_access); }
+void test_verifier_btf_flex_array(void) { RUN(verifier_btf_flex_array); }
void test_verifier_btf_unreliable_prog(void) { RUN(verifier_btf_unreliable_prog); }
void test_verifier_call_large_imm(void) { RUN(verifier_call_large_imm); }
void test_verifier_cfg(void) { RUN(verifier_cfg); }
@@ -216,6 +219,7 @@ void test_verifier_int_ptr(void) { RUN(verifier_int_ptr); }
void test_verifier_iterating_callbacks(void) { RUN(verifier_iterating_callbacks); }
void test_verifier_jeq_infer_not_null(void) { RUN(verifier_jeq_infer_not_null); }
void test_verifier_jit_convergence(void) { RUN(verifier_jit_convergence); }
+void test_verifier_kfunc_perfmon(void) { RUN(verifier_kfunc_perfmon); }
void test_verifier_load_acquire(void) { RUN(verifier_load_acquire); }
void test_verifier_ld_ind(void) { RUN(verifier_ld_ind); }
void test_verifier_ldsx(void) { RUN(verifier_ldsx); }