selftests/bpf: add CO-RE relocs nesting tests
authorAndrii Nakryiko <andriin@fb.com>
Wed, 7 Aug 2019 21:39:55 +0000 (14:39 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 7 Aug 2019 21:43:49 +0000 (14:43 -0700)
Add a bunch of test validating correct handling of nested
structs/unions.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Acked-by: Song Liu <songliubraving@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
16 files changed:
tools/testing/selftests/bpf/prog_tests/core_reloc.c
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/core_reloc_types.h
tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c [new file with mode: 0644]

index 59fd9cb207ab38cf6f7fedfb5bbb403b7e966fc2..3e9344e41556566ca2f0233355befc67ef546d47 100644 (file)
        .fails = true,                                                  \
 }
 
+#define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) {    \
+       .a = { .a = { .a = 42 } },                                      \
+       .b = { .b = { .b = 0xc001 } },                                  \
+}
+
+#define NESTING_CASE_COMMON(name)                                      \
+       .case_name = #name,                                             \
+       .bpf_obj_file = "test_core_reloc_nesting.o",                    \
+       .btf_src_file = "btf__core_reloc_" #name ".o"
+
+#define NESTING_CASE(name) {                                           \
+       NESTING_CASE_COMMON(name),                                      \
+       .input = NESTING_DATA(core_reloc_##name),                       \
+       .input_len = sizeof(struct core_reloc_##name),                  \
+       .output = NESTING_DATA(core_reloc_nesting),                     \
+       .output_len = sizeof(struct core_reloc_nesting)                 \
+}
+
+#define NESTING_ERR_CASE(name) {                                       \
+       NESTING_CASE_COMMON(name),                                      \
+       .fails = true,                                                  \
+}
+
 struct core_reloc_test_case {
        const char *case_name;
        const char *bpf_obj_file;
@@ -57,6 +80,22 @@ static struct core_reloc_test_case test_cases[] = {
        FLAVORS_CASE(flavors),
 
        FLAVORS_ERR_CASE(flavors__err_wrong_name),
+
+       /* various struct/enum nesting and resolution scenarios */
+       NESTING_CASE(nesting),
+       NESTING_CASE(nesting___anon_embed),
+       NESTING_CASE(nesting___struct_union_mixup),
+       NESTING_CASE(nesting___extra_nesting),
+       NESTING_CASE(nesting___dup_compat_types),
+
+       NESTING_ERR_CASE(nesting___err_missing_field),
+       NESTING_ERR_CASE(nesting___err_array_field),
+       NESTING_ERR_CASE(nesting___err_missing_container),
+       NESTING_ERR_CASE(nesting___err_nonstruct_container),
+       NESTING_ERR_CASE(nesting___err_array_container),
+       NESTING_ERR_CASE(nesting___err_dup_incompat_types),
+       NESTING_ERR_CASE(nesting___err_partial_match_dups),
+       NESTING_ERR_CASE(nesting___err_too_deep),
 };
 
 struct data {
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c
new file mode 100644 (file)
index 0000000..4480fcc
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c
new file mode 100644 (file)
index 0000000..13e108f
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___anon_embed x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c
new file mode 100644 (file)
index 0000000..76b54fd
--- /dev/null
@@ -0,0 +1,5 @@
+#include "core_reloc_types.h"
+
+void f1(struct core_reloc_nesting___dup_compat_types x) {}
+void f2(struct core_reloc_nesting___dup_compat_types__2 x) {}
+void f3(struct core_reloc_nesting___dup_compat_types__3 x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c
new file mode 100644 (file)
index 0000000..975fb95
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_array_container x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c
new file mode 100644 (file)
index 0000000..ad66c67
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_array_field x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c
new file mode 100644 (file)
index 0000000..35c5f8d
--- /dev/null
@@ -0,0 +1,4 @@
+#include "core_reloc_types.h"
+
+void f1(struct core_reloc_nesting___err_dup_incompat_types__1 x) {}
+void f2(struct core_reloc_nesting___err_dup_incompat_types__2 x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c
new file mode 100644 (file)
index 0000000..142e332
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_missing_container x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c
new file mode 100644 (file)
index 0000000..efcae16
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_missing_field x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c
new file mode 100644 (file)
index 0000000..97aaaed
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_nonstruct_container x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c
new file mode 100644 (file)
index 0000000..ffde350
--- /dev/null
@@ -0,0 +1,4 @@
+#include "core_reloc_types.h"
+
+void f1(struct core_reloc_nesting___err_partial_match_dups__a x) {}
+void f2(struct core_reloc_nesting___err_partial_match_dups__b x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c
new file mode 100644 (file)
index 0000000..39a2fad
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_too_deep x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c
new file mode 100644 (file)
index 0000000..a09d9df
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___extra_nesting x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c
new file mode 100644 (file)
index 0000000..3d8a1a7
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___struct_union_mixup x) {}
index 33b0c6a619126140701588590263752008e64602..340ee2bcd463a5be995e5de5bc20a2a3d2a9bb4a 100644 (file)
@@ -13,3 +13,296 @@ struct core_reloc_flavors__err_wrong_name {
        int b;
        int c;
 };
+
+/*
+ * NESTING
+ */
+/* original set up, used to record relocations in BPF program */
+struct core_reloc_nesting_substruct {
+       int a;
+};
+
+union core_reloc_nesting_subunion {
+       int b;
+};
+
+struct core_reloc_nesting {
+       union {
+               struct core_reloc_nesting_substruct a;
+       } a;
+       struct {
+               union core_reloc_nesting_subunion b;
+       } b;
+};
+
+/* inlined anonymous struct/union instead of named structs in original */
+struct core_reloc_nesting___anon_embed {
+       int __just_for_padding;
+       union {
+               struct {
+                       int a;
+               } a;
+       } a;
+       struct {
+               union {
+                       int b;
+               } b;
+       } b;
+};
+
+/* different mix of nested structs/unions than in original */
+struct core_reloc_nesting___struct_union_mixup {
+       int __a;
+       struct {
+               int __a;
+               union {
+                       char __a;
+                       int a;
+               } a;
+       } a;
+       int __b;
+       union {
+               int __b;
+               union {
+                       char __b;
+                       int b;
+               } b;
+       } b;
+};
+
+/* extra anon structs/unions, but still valid a.a.a and b.b.b accessors */
+struct core_reloc_nesting___extra_nesting {
+       int __padding;
+       struct {
+               struct {
+                       struct {
+                               struct {
+                                       union {
+                                               int a;
+                                       } a;
+                               };
+                       };
+               } a;
+               int __some_more;
+               struct {
+                       union {
+                               union {
+                                       union {
+                                               struct {
+                                                       int b;
+                                               };
+                                       } b;
+                               };
+                       } b;
+               };
+       };
+};
+
+/* three flavors of same struct with different structure but same layout for
+ * a.a.a and b.b.b, thus successfully resolved and relocatable */
+struct core_reloc_nesting___dup_compat_types {
+       char __just_for_padding;
+       /* 3 more bytes of padding */
+       struct {
+               struct {
+                       int a; /* offset 4 */
+               } a;
+       } a;
+       long long __more_padding;
+       struct {
+               struct {
+                       int b; /* offset 16 */
+               } b;
+       } b;
+};
+
+struct core_reloc_nesting___dup_compat_types__2 {
+       int __aligned_padding;
+       struct {
+               int __trickier_noop[0];
+               struct {
+                       char __some_more_noops[0];
+                       int a; /* offset 4 */
+               } a;
+       } a;
+       int __more_padding;
+       struct {
+               struct {
+                       struct {
+                               int __critical_padding;
+                               int b; /* offset 16 */
+                       } b;
+                       int __does_not_matter;
+               };
+       } b;
+       int __more_irrelevant_stuff;
+};
+
+struct core_reloc_nesting___dup_compat_types__3 {
+       char __correct_padding[4];
+       struct {
+               struct {
+                       int a; /* offset 4 */
+               } a;
+       } a;
+       /* 8 byte padding due to next struct's alignment */
+       struct {
+               struct {
+                       int b;
+               } b;
+       } b __attribute__((aligned(16)));
+};
+
+/* b.b.b field is missing */
+struct core_reloc_nesting___err_missing_field {
+       struct {
+               struct {
+                       int a;
+               } a;
+       } a;
+       struct {
+               struct {
+                       int x;
+               } b;
+       } b;
+};
+
+/* b.b.b field is an array of integers instead of plain int */
+struct core_reloc_nesting___err_array_field {
+       struct {
+               struct {
+                       int a;
+               } a;
+       } a;
+       struct {
+               struct {
+                       int b[1];
+               } b;
+       } b;
+};
+
+/* middle b container is missing */
+struct core_reloc_nesting___err_missing_container {
+       struct {
+               struct {
+                       int a;
+               } a;
+       } a;
+       struct {
+               int x;
+       } b;
+};
+
+/* middle b container is referenced through pointer instead of being embedded */
+struct core_reloc_nesting___err_nonstruct_container {
+       struct {
+               struct {
+                       int a;
+               } a;
+       } a;
+       struct {
+               struct {
+                       int b;
+               } *b;
+       } b;
+};
+
+/* middle b container is an array of structs instead of plain struct */
+struct core_reloc_nesting___err_array_container {
+       struct {
+               struct {
+                       int a;
+               } a;
+       } a;
+       struct {
+               struct {
+                       int b;
+               } b[1];
+       } b;
+};
+
+/* two flavors of same struct with incompatible layout for b.b.b */
+struct core_reloc_nesting___err_dup_incompat_types__1 {
+       struct {
+               struct {
+                       int a; /* offset 0 */
+               } a;
+       } a;
+       struct {
+               struct {
+                       int b; /* offset 4 */
+               } b;
+       } b;
+};
+
+struct core_reloc_nesting___err_dup_incompat_types__2 {
+       struct {
+               struct {
+                       int a; /* offset 0 */
+               } a;
+       } a;
+       int __extra_padding;
+       struct {
+               struct {
+                       int b; /* offset 8 (!) */
+               } b;
+       } b;
+};
+
+/* two flavors of same struct having one of a.a.a and b.b.b, but not both */
+struct core_reloc_nesting___err_partial_match_dups__a {
+       struct {
+               struct {
+                       int a;
+               } a;
+       } a;
+};
+
+struct core_reloc_nesting___err_partial_match_dups__b {
+       struct {
+               struct {
+                       int b;
+               } b;
+       } b;
+};
+
+struct core_reloc_nesting___err_too_deep {
+       struct {
+               struct {
+                       int a;
+               } a;
+       } a;
+       /* 65 levels of nestedness for b.b.b */
+       struct {
+               struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                       struct { struct { struct { struct { struct {
+                               /* this one is one too much */
+                               struct {
+                                       int b;
+                               };
+                       }; }; }; }; };
+                       }; }; }; }; };
+                       }; }; }; }; };
+                       }; }; }; }; };
+                       }; }; }; }; };
+                       }; }; }; }; };
+                       }; }; }; }; };
+                       }; }; }; }; };
+                       }; }; }; }; };
+                       }; }; }; }; };
+                       }; }; }; }; };
+                       }; }; }; }; };
+               } b;
+       } b;
+};
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c b/tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c
new file mode 100644 (file)
index 0000000..3ca30ce
--- /dev/null
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+       char in[256];
+       char out[256];
+} data;
+
+struct core_reloc_nesting_substruct {
+       int a;
+};
+
+union core_reloc_nesting_subunion {
+       int b;
+};
+
+/* int a.a.a and b.b.b accesses */
+struct core_reloc_nesting {
+       union {
+               struct core_reloc_nesting_substruct a;
+       } a;
+       struct {
+               union core_reloc_nesting_subunion b;
+       } b;
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_nesting(void *ctx)
+{
+       struct core_reloc_nesting *in = (void *)&data.in;
+       struct core_reloc_nesting *out = (void *)&data.out;
+
+       if (BPF_CORE_READ(&out->a.a.a, &in->a.a.a))
+               return 1;
+       if (BPF_CORE_READ(&out->b.b.b, &in->b.b.b))
+               return 1;
+
+       return 0;
+}
+