selftests/bpf: Test TYPE_EXISTS and TYPE_SIZE CO-RE relocations
authorAndrii Nakryiko <andriin@fb.com>
Wed, 19 Aug 2020 19:45:16 +0000 (12:45 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 19 Aug 2020 21:19:39 +0000 (14:19 -0700)
Add selftests for TYPE_EXISTS and TYPE_SIZE relocations, testing correctness
of relocations and handling of type compatiblity/incompatibility.

If __builtin_preserve_type_info() is not supported by compiler, skip tests.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20200819194519.3375898-3-andriin@fb.com
tools/testing/selftests/bpf/prog_tests/core_reloc.c
tools/testing/selftests/bpf/progs/btf__core_reloc_type_based.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___all_missing.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___diff_sz.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___fn_wrong_args.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___incompat.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/core_reloc_types.h
tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c
tools/testing/selftests/bpf/progs/test_core_reloc_type_based.c [new file with mode: 0644]

index 4d650e9..b775ce0 100644 (file)
        .fails = true,                                                  \
 }
 
-#define EXISTENCE_CASE_COMMON(name)                                    \
+#define FIELD_EXISTS_CASE_COMMON(name)                                 \
        .case_name = #name,                                             \
        .bpf_obj_file = "test_core_reloc_existence.o",                  \
-       .btf_src_file = "btf__core_reloc_" #name ".o",                  \
-       .relaxed_core_relocs = true
+       .btf_src_file = "btf__core_reloc_" #name ".o"                   \
 
-#define EXISTENCE_ERR_CASE(name) {                                     \
-       EXISTENCE_CASE_COMMON(name),                                    \
+#define FIELD_EXISTS_ERR_CASE(name) {                                  \
+       FIELD_EXISTS_CASE_COMMON(name),                                 \
        .fails = true,                                                  \
 }
 
        .fails = true,                                                  \
 }
 
+#define TYPE_BASED_CASE_COMMON(name)                                   \
+       .case_name = #name,                                             \
+       .bpf_obj_file = "test_core_reloc_type_based.o",         \
+       .btf_src_file = "btf__core_reloc_" #name ".o"                   \
+
+#define TYPE_BASED_CASE(name, ...) {                                   \
+       TYPE_BASED_CASE_COMMON(name),                                   \
+       .output = STRUCT_TO_CHAR_PTR(core_reloc_type_based_output)      \
+                       __VA_ARGS__,                                    \
+       .output_len = sizeof(struct core_reloc_type_based_output),      \
+}
+
+#define TYPE_BASED_ERR_CASE(name) {                                    \
+       TYPE_BASED_CASE_COMMON(name),                                   \
+       .fails = true,                                                  \
+}
+
 struct core_reloc_test_case {
        const char *case_name;
        const char *bpf_obj_file;
@@ -364,7 +380,7 @@ static struct core_reloc_test_case test_cases[] = {
 
        /* validate field existence checks */
        {
-               EXISTENCE_CASE_COMMON(existence),
+               FIELD_EXISTS_CASE_COMMON(existence),
                .input = STRUCT_TO_CHAR_PTR(core_reloc_existence) {
                        .a = 1,
                        .b = 2,
@@ -388,7 +404,7 @@ static struct core_reloc_test_case test_cases[] = {
                .output_len = sizeof(struct core_reloc_existence_output),
        },
        {
-               EXISTENCE_CASE_COMMON(existence___minimal),
+               FIELD_EXISTS_CASE_COMMON(existence___minimal),
                .input = STRUCT_TO_CHAR_PTR(core_reloc_existence___minimal) {
                        .a = 42,
                },
@@ -408,12 +424,12 @@ static struct core_reloc_test_case test_cases[] = {
                .output_len = sizeof(struct core_reloc_existence_output),
        },
 
-       EXISTENCE_ERR_CASE(existence__err_int_sz),
-       EXISTENCE_ERR_CASE(existence__err_int_type),
-       EXISTENCE_ERR_CASE(existence__err_int_kind),
-       EXISTENCE_ERR_CASE(existence__err_arr_kind),
-       EXISTENCE_ERR_CASE(existence__err_arr_value_type),
-       EXISTENCE_ERR_CASE(existence__err_struct_type),
+       FIELD_EXISTS_ERR_CASE(existence__err_int_sz),
+       FIELD_EXISTS_ERR_CASE(existence__err_int_type),
+       FIELD_EXISTS_ERR_CASE(existence__err_int_kind),
+       FIELD_EXISTS_ERR_CASE(existence__err_arr_kind),
+       FIELD_EXISTS_ERR_CASE(existence__err_arr_value_type),
+       FIELD_EXISTS_ERR_CASE(existence__err_struct_type),
 
        /* bitfield relocation checks */
        BITFIELDS_CASE(bitfields, {
@@ -453,11 +469,73 @@ static struct core_reloc_test_case test_cases[] = {
        SIZE_CASE(size),
        SIZE_CASE(size___diff_sz),
        SIZE_ERR_CASE(size___err_ambiguous),
+
+       /* validate type existence and size relocations */
+       TYPE_BASED_CASE(type_based, {
+               .struct_exists = 1,
+               .union_exists = 1,
+               .enum_exists = 1,
+               .typedef_named_struct_exists = 1,
+               .typedef_anon_struct_exists = 1,
+               .typedef_struct_ptr_exists = 1,
+               .typedef_int_exists = 1,
+               .typedef_enum_exists = 1,
+               .typedef_void_ptr_exists = 1,
+               .typedef_func_proto_exists = 1,
+               .typedef_arr_exists = 1,
+               .struct_sz = sizeof(struct a_struct),
+               .union_sz = sizeof(union a_union),
+               .enum_sz = sizeof(enum an_enum),
+               .typedef_named_struct_sz = sizeof(named_struct_typedef),
+               .typedef_anon_struct_sz = sizeof(anon_struct_typedef),
+               .typedef_struct_ptr_sz = sizeof(struct_ptr_typedef),
+               .typedef_int_sz = sizeof(int_typedef),
+               .typedef_enum_sz = sizeof(enum_typedef),
+               .typedef_void_ptr_sz = sizeof(void_ptr_typedef),
+               .typedef_func_proto_sz = sizeof(func_proto_typedef),
+               .typedef_arr_sz = sizeof(arr_typedef),
+       }),
+       TYPE_BASED_CASE(type_based___all_missing, {
+               /* all zeros */
+       }),
+       TYPE_BASED_CASE(type_based___diff_sz, {
+               .struct_exists = 1,
+               .union_exists = 1,
+               .enum_exists = 1,
+               .typedef_named_struct_exists = 1,
+               .typedef_anon_struct_exists = 1,
+               .typedef_struct_ptr_exists = 1,
+               .typedef_int_exists = 1,
+               .typedef_enum_exists = 1,
+               .typedef_void_ptr_exists = 1,
+               .typedef_func_proto_exists = 1,
+               .typedef_arr_exists = 1,
+               .struct_sz = sizeof(struct a_struct___diff_sz),
+               .union_sz = sizeof(union a_union___diff_sz),
+               .enum_sz = sizeof(enum an_enum___diff_sz),
+               .typedef_named_struct_sz = sizeof(named_struct_typedef___diff_sz),
+               .typedef_anon_struct_sz = sizeof(anon_struct_typedef___diff_sz),
+               .typedef_struct_ptr_sz = sizeof(struct_ptr_typedef___diff_sz),
+               .typedef_int_sz = sizeof(int_typedef___diff_sz),
+               .typedef_enum_sz = sizeof(enum_typedef___diff_sz),
+               .typedef_void_ptr_sz = sizeof(void_ptr_typedef___diff_sz),
+               .typedef_func_proto_sz = sizeof(func_proto_typedef___diff_sz),
+               .typedef_arr_sz = sizeof(arr_typedef___diff_sz),
+       }),
+       TYPE_BASED_CASE(type_based___incompat, {
+               .enum_exists = 1,
+               .enum_sz = sizeof(enum an_enum),
+       }),
+       TYPE_BASED_CASE(type_based___fn_wrong_args, {
+               .struct_exists = 1,
+               .struct_sz = sizeof(struct a_struct),
+       }),
 };
 
 struct data {
        char in[256];
        char out[256];
+       bool skip;
        uint64_t my_pid_tgid;
 };
 
@@ -516,15 +594,10 @@ void test_core_reloc(void)
                load_attr.log_level = 0;
                load_attr.target_btf_path = test_case->btf_src_file;
                err = bpf_object__load_xattr(&load_attr);
-               if (test_case->fails) {
-                       CHECK(!err, "obj_load_fail",
-                             "should fail to load prog '%s'\n", probe_name);
+               if (err) {
+                       if (!test_case->fails)
+                               CHECK(false, "obj_load", "failed to load prog '%s': %d\n", probe_name, err);
                        goto cleanup;
-               } else {
-                       if (CHECK(err, "obj_load",
-                                 "failed to load prog '%s': %d\n",
-                                 probe_name, err))
-                               goto cleanup;
                }
 
                data_map = bpf_object__find_map_by_name(obj, "test_cor.bss");
@@ -552,6 +625,16 @@ void test_core_reloc(void)
                /* trigger test run */
                usleep(1);
 
+               if (data->skip) {
+                       test__skip();
+                       goto cleanup;
+               }
+
+               if (test_case->fails) {
+                       CHECK(false, "obj_load_fail", "should fail to load prog '%s'\n", probe_name);
+                       goto cleanup;
+               }
+
                equal = memcmp(data->out, test_case->output,
                               test_case->output_len) == 0;
                if (CHECK(!equal, "check_result",
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_type_based.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_type_based.c
new file mode 100644 (file)
index 0000000..fc3f69e
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_type_based x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___all_missing.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___all_missing.c
new file mode 100644 (file)
index 0000000..5151164
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_type_based___all_missing x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___diff_sz.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___diff_sz.c
new file mode 100644 (file)
index 0000000..67db3dc
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_type_based___diff_sz x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___fn_wrong_args.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___fn_wrong_args.c
new file mode 100644 (file)
index 0000000..b357fc6
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_type_based___fn_wrong_args x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___incompat.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_type_based___incompat.c
new file mode 100644 (file)
index 0000000..8ddf20d
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_type_based___incompat x) {}
index 3b1126c..d998537 100644 (file)
@@ -652,7 +652,7 @@ struct core_reloc_misc_extensible {
 };
 
 /*
- * EXISTENCE
+ * FIELD EXISTENCE
  */
 struct core_reloc_existence_output {
        int a_exists;
@@ -834,3 +834,203 @@ struct core_reloc_size___err_ambiguous2 {
        void *ptr_field;
        enum { VALUE___2 = 123 } enum_field;
 };
+
+/*
+ * TYPE EXISTENCE & SIZE
+ */
+struct core_reloc_type_based_output {
+       bool struct_exists;
+       bool union_exists;
+       bool enum_exists;
+       bool typedef_named_struct_exists;
+       bool typedef_anon_struct_exists;
+       bool typedef_struct_ptr_exists;
+       bool typedef_int_exists;
+       bool typedef_enum_exists;
+       bool typedef_void_ptr_exists;
+       bool typedef_func_proto_exists;
+       bool typedef_arr_exists;
+
+       int struct_sz;
+       int union_sz;
+       int enum_sz;
+       int typedef_named_struct_sz;
+       int typedef_anon_struct_sz;
+       int typedef_struct_ptr_sz;
+       int typedef_int_sz;
+       int typedef_enum_sz;
+       int typedef_void_ptr_sz;
+       int typedef_func_proto_sz;
+       int typedef_arr_sz;
+};
+
+struct a_struct {
+       int x;
+};
+
+union a_union {
+       int y;
+       int z;
+};
+
+typedef struct a_struct named_struct_typedef;
+
+typedef struct { int x, y, z; } anon_struct_typedef;
+
+typedef struct {
+       int a, b, c;
+} *struct_ptr_typedef;
+
+enum an_enum {
+       AN_ENUM_VAL1 = 1,
+       AN_ENUM_VAL2 = 2,
+       AN_ENUM_VAL3 = 3,
+};
+
+typedef int int_typedef;
+
+typedef enum { TYPEDEF_ENUM_VAL1, TYPEDEF_ENUM_VAL2 } enum_typedef;
+
+typedef void *void_ptr_typedef;
+
+typedef int (*func_proto_typedef)(long);
+
+typedef char arr_typedef[20];
+
+struct core_reloc_type_based {
+       struct a_struct f1;
+       union a_union f2;
+       enum an_enum f3;
+       named_struct_typedef f4;
+       anon_struct_typedef f5;
+       struct_ptr_typedef f6;
+       int_typedef f7;
+       enum_typedef f8;
+       void_ptr_typedef f9;
+       func_proto_typedef f10;
+       arr_typedef f11;
+};
+
+/* no types in target */
+struct core_reloc_type_based___all_missing {
+};
+
+/* different type sizes, extra modifiers, anon vs named enums, etc */
+struct a_struct___diff_sz {
+       long x;
+       int y;
+       char z;
+};
+
+union a_union___diff_sz {
+       char yy;
+       char zz;
+};
+
+typedef struct a_struct___diff_sz named_struct_typedef___diff_sz;
+
+typedef struct { long xx, yy, zzz; } anon_struct_typedef___diff_sz;
+
+typedef struct {
+       char aa[1], bb[2], cc[3];
+} *struct_ptr_typedef___diff_sz;
+
+enum an_enum___diff_sz {
+       AN_ENUM_VAL1___diff_sz = 0x123412341234,
+       AN_ENUM_VAL2___diff_sz = 2,
+};
+
+typedef unsigned long int_typedef___diff_sz;
+
+typedef enum an_enum___diff_sz enum_typedef___diff_sz;
+
+typedef const void * const void_ptr_typedef___diff_sz;
+
+typedef int_typedef___diff_sz (*func_proto_typedef___diff_sz)(char);
+
+typedef int arr_typedef___diff_sz[2];
+
+struct core_reloc_type_based___diff_sz {
+       struct a_struct___diff_sz f1;
+       union a_union___diff_sz f2;
+       enum an_enum___diff_sz f3;
+       named_struct_typedef___diff_sz f4;
+       anon_struct_typedef___diff_sz f5;
+       struct_ptr_typedef___diff_sz f6;
+       int_typedef___diff_sz f7;
+       enum_typedef___diff_sz f8;
+       void_ptr_typedef___diff_sz f9;
+       func_proto_typedef___diff_sz f10;
+       arr_typedef___diff_sz f11;
+};
+
+/* incompatibilities between target and local types */
+union a_struct___incompat { /* union instead of struct */
+       int x;
+};
+
+struct a_union___incompat { /* struct instead of union */
+       int y;
+       int z;
+};
+
+/* typedef to union, not to struct */
+typedef union a_struct___incompat named_struct_typedef___incompat;
+
+/* typedef to void pointer, instead of struct */
+typedef void *anon_struct_typedef___incompat;
+
+/* extra pointer indirection */
+typedef struct {
+       int a, b, c;
+} **struct_ptr_typedef___incompat;
+
+/* typedef of a struct with int, instead of int */
+typedef struct { int x; } int_typedef___incompat;
+
+/* typedef to func_proto, instead of enum */
+typedef int (*enum_typedef___incompat)(void);
+
+/* pointer to char instead of void */
+typedef char *void_ptr_typedef___incompat;
+
+/* void return type instead of int */
+typedef void (*func_proto_typedef___incompat)(long);
+
+/* multi-dimensional array instead of a single-dimensional */
+typedef int arr_typedef___incompat[20][2];
+
+struct core_reloc_type_based___incompat {
+       union a_struct___incompat f1;
+       struct a_union___incompat f2;
+       /* the only valid one is enum, to check that something still succeeds */
+       enum an_enum f3;
+       named_struct_typedef___incompat f4;
+       anon_struct_typedef___incompat f5;
+       struct_ptr_typedef___incompat f6;
+       int_typedef___incompat f7;
+       enum_typedef___incompat f8;
+       void_ptr_typedef___incompat f9;
+       func_proto_typedef___incompat f10;
+       arr_typedef___incompat f11;
+};
+
+/* func_proto with incompatible signature */
+typedef void (*func_proto_typedef___fn_wrong_ret1)(long);
+typedef int * (*func_proto_typedef___fn_wrong_ret2)(long);
+typedef struct { int x; } int_struct_typedef;
+typedef int_struct_typedef (*func_proto_typedef___fn_wrong_ret3)(long);
+typedef int (*func_proto_typedef___fn_wrong_arg)(void *);
+typedef int (*func_proto_typedef___fn_wrong_arg_cnt1)(long, long);
+typedef int (*func_proto_typedef___fn_wrong_arg_cnt2)(void);
+
+struct core_reloc_type_based___fn_wrong_args {
+       /* one valid type to make sure relos still work */
+       struct a_struct f1;
+       func_proto_typedef___fn_wrong_ret1 f2;
+       func_proto_typedef___fn_wrong_ret2 f3;
+       func_proto_typedef___fn_wrong_ret3 f4;
+       func_proto_typedef___fn_wrong_arg f5;
+       func_proto_typedef___fn_wrong_arg_cnt1 f6;
+       func_proto_typedef___fn_wrong_arg_cnt2 f7;
+};
index aba928f..145028b 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/bpf.h>
 #include <stdint.h>
+#include <stdbool.h>
 #include <bpf/bpf_helpers.h>
 #include <bpf/bpf_core_read.h>
 
@@ -11,6 +12,7 @@ char _license[] SEC("license") = "GPL";
 struct {
        char in[256];
        char out[256];
+       bool skip;
        uint64_t my_pid_tgid;
 } data = {};
 
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_type_based.c b/tools/testing/selftests/bpf/progs/test_core_reloc_type_based.c
new file mode 100644 (file)
index 0000000..6ab259d
--- /dev/null
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+
+char _license[] SEC("license") = "GPL";
+
+struct {
+       char in[256];
+       char out[256];
+       bool skip;
+} data = {};
+
+struct a_struct {
+       int x;
+};
+
+union a_union {
+       int y;
+       int z;
+};
+
+typedef struct a_struct named_struct_typedef;
+
+typedef struct { int x, y, z; } anon_struct_typedef;
+
+typedef struct {
+       int a, b, c;
+} *struct_ptr_typedef;
+
+enum an_enum {
+       AN_ENUM_VAL1 = 1,
+       AN_ENUM_VAL2 = 2,
+       AN_ENUM_VAL3 = 3,
+};
+
+typedef int int_typedef;
+
+typedef enum { TYPEDEF_ENUM_VAL1, TYPEDEF_ENUM_VAL2 } enum_typedef;
+
+typedef void *void_ptr_typedef;
+
+typedef int (*func_proto_typedef)(long);
+
+typedef char arr_typedef[20];
+
+struct core_reloc_type_based {
+       struct a_struct f1;
+       union a_union f2;
+       enum an_enum f3;
+       named_struct_typedef f4;
+       anon_struct_typedef f5;
+       struct_ptr_typedef f6;
+       int_typedef f7;
+       enum_typedef f8;
+       void_ptr_typedef f9;
+       func_proto_typedef f10;
+       arr_typedef f11;
+};
+
+struct core_reloc_type_based_output {
+       bool struct_exists;
+       bool union_exists;
+       bool enum_exists;
+       bool typedef_named_struct_exists;
+       bool typedef_anon_struct_exists;
+       bool typedef_struct_ptr_exists;
+       bool typedef_int_exists;
+       bool typedef_enum_exists;
+       bool typedef_void_ptr_exists;
+       bool typedef_func_proto_exists;
+       bool typedef_arr_exists;
+
+       int struct_sz;
+       int union_sz;
+       int enum_sz;
+       int typedef_named_struct_sz;
+       int typedef_anon_struct_sz;
+       int typedef_struct_ptr_sz;
+       int typedef_int_sz;
+       int typedef_enum_sz;
+       int typedef_void_ptr_sz;
+       int typedef_func_proto_sz;
+       int typedef_arr_sz;
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_type_based(void *ctx)
+{
+#if __has_builtin(__builtin_preserve_type_info)
+       struct core_reloc_type_based_output *out = (void *)&data.out;
+
+       out->struct_exists = bpf_core_type_exists(struct a_struct);
+       out->union_exists = bpf_core_type_exists(union a_union);
+       out->enum_exists = bpf_core_type_exists(enum an_enum);
+       out->typedef_named_struct_exists = bpf_core_type_exists(named_struct_typedef);
+       out->typedef_anon_struct_exists = bpf_core_type_exists(anon_struct_typedef);
+       out->typedef_struct_ptr_exists = bpf_core_type_exists(struct_ptr_typedef);
+       out->typedef_int_exists = bpf_core_type_exists(int_typedef);
+       out->typedef_enum_exists = bpf_core_type_exists(enum_typedef);
+       out->typedef_void_ptr_exists = bpf_core_type_exists(void_ptr_typedef);
+       out->typedef_func_proto_exists = bpf_core_type_exists(func_proto_typedef);
+       out->typedef_arr_exists = bpf_core_type_exists(arr_typedef);
+
+       out->struct_sz = bpf_core_type_size(struct a_struct);
+       out->union_sz = bpf_core_type_size(union a_union);
+       out->enum_sz = bpf_core_type_size(enum an_enum);
+       out->typedef_named_struct_sz = bpf_core_type_size(named_struct_typedef);
+       out->typedef_anon_struct_sz = bpf_core_type_size(anon_struct_typedef);
+       out->typedef_struct_ptr_sz = bpf_core_type_size(struct_ptr_typedef);
+       out->typedef_int_sz = bpf_core_type_size(int_typedef);
+       out->typedef_enum_sz = bpf_core_type_size(enum_typedef);
+       out->typedef_void_ptr_sz = bpf_core_type_size(void_ptr_typedef);
+       out->typedef_func_proto_sz = bpf_core_type_size(func_proto_typedef);
+       out->typedef_arr_sz = bpf_core_type_size(arr_typedef);
+#else
+       data.skip = true;
+#endif
+       return 0;
+}