selftest/bpf: Add relocatable bitfield reading tests
authorAndrii Nakryiko <andriin@fb.com>
Fri, 1 Nov 2019 22:28:09 +0000 (15:28 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Mon, 4 Nov 2019 15:06:56 +0000 (16:06 +0100)
Add a bunch of selftests verifying correctness of relocatable bitfield reading
support in libbpf. Both bpf_probe_read()-based and direct read-based bitfield
macros are tested. core_reloc.c "test_harness" is extended to support raw
tracepoint and new typed raw tracepoints as test BPF program types.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20191101222810.1246166-5-andriin@fb.com
tools/testing/selftests/bpf/prog_tests/core_reloc.c
tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___bit_sz_change.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___bitfield_vs_int.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___err_too_big_bitfield.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___just_big_enough.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/core_reloc_types.h
tools/testing/selftests/bpf/progs/test_core_reloc_bitfields_direct.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_core_reloc_bitfields_probed.c [new file with mode: 0644]

index 09dfa75..340aa12 100644 (file)
        .fails = true,                                                  \
 }
 
+#define BITFIELDS_CASE_COMMON(objfile, test_name_prefix,  name)                \
+       .case_name = test_name_prefix#name,                             \
+       .bpf_obj_file = objfile,                                        \
+       .btf_src_file = "btf__core_reloc_" #name ".o"
+
+#define BITFIELDS_CASE(name, ...) {                                    \
+       BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o",     \
+                             "direct:", name),                         \
+       .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__,     \
+       .input_len = sizeof(struct core_reloc_##name),                  \
+       .output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output)       \
+               __VA_ARGS__,                                            \
+       .output_len = sizeof(struct core_reloc_bitfields_output),       \
+}, {                                                                   \
+       BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o",     \
+                             "probed:", name),                         \
+       .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__,     \
+       .input_len = sizeof(struct core_reloc_##name),                  \
+       .output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output)       \
+               __VA_ARGS__,                                            \
+       .output_len = sizeof(struct core_reloc_bitfields_output),       \
+       .direct_raw_tp = true,                                          \
+}
+
+
+#define BITFIELDS_ERR_CASE(name) {                                     \
+       BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o",     \
+                             "probed:", name),                         \
+       .fails = true,                                                  \
+}, {                                                                   \
+       BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o",     \
+                             "direct:", name),                         \
+       .direct_raw_tp = true,                                          \
+       .fails = true,                                                  \
+}
+
 struct core_reloc_test_case {
        const char *case_name;
        const char *bpf_obj_file;
@@ -199,6 +235,7 @@ struct core_reloc_test_case {
        int output_len;
        bool fails;
        bool relaxed_core_relocs;
+       bool direct_raw_tp;
 };
 
 static struct core_reloc_test_case test_cases[] = {
@@ -352,6 +389,40 @@ static struct core_reloc_test_case test_cases[] = {
        EXISTENCE_ERR_CASE(existence__err_arr_kind),
        EXISTENCE_ERR_CASE(existence__err_arr_value_type),
        EXISTENCE_ERR_CASE(existence__err_struct_type),
+
+       /* bitfield relocation checks */
+       BITFIELDS_CASE(bitfields, {
+               .ub1 = 1,
+               .ub2 = 2,
+               .ub7 = 96,
+               .sb4 = -7,
+               .sb20 = -0x76543,
+               .u32 = 0x80000000,
+               .s32 = -0x76543210,
+       }),
+       BITFIELDS_CASE(bitfields___bit_sz_change, {
+               .ub1 = 6,
+               .ub2 = 0xABCDE,
+               .ub7 = 1,
+               .sb4 = -1,
+               .sb20 = -0x17654321,
+               .u32 = 0xBEEF,
+               .s32 = -0x3FEDCBA987654321,
+       }),
+       BITFIELDS_CASE(bitfields___bitfield_vs_int, {
+               .ub1 = 0xFEDCBA9876543210,
+               .ub2 = 0xA6,
+               .ub7 = -0x7EDCBA987654321,
+               .sb4 = -0x6123456789ABCDE,
+               .sb20 = 0xD00D,
+               .u32 = -0x76543,
+               .s32 = 0x0ADEADBEEFBADB0B,
+       }),
+       BITFIELDS_CASE(bitfields___just_big_enough, {
+               .ub1 = 0xF,
+               .ub2 = 0x0812345678FEDCBA,
+       }),
+       BITFIELDS_ERR_CASE(bitfields___err_too_big_bitfield),
 };
 
 struct data {
@@ -361,9 +432,9 @@ struct data {
 
 void test_core_reloc(void)
 {
-       const char *probe_name = "raw_tracepoint/sys_enter";
        struct bpf_object_load_attr load_attr = {};
        struct core_reloc_test_case *test_case;
+       const char *tp_name, *probe_name;
        int err, duration = 0, i, equal;
        struct bpf_link *link = NULL;
        struct bpf_map *data_map;
@@ -387,6 +458,15 @@ void test_core_reloc(void)
                          test_case->bpf_obj_file, PTR_ERR(obj)))
                        continue;
 
+               /* for typed raw tracepoints, NULL should be specified */
+               if (test_case->direct_raw_tp) {
+                       probe_name = "tp_btf/sys_enter";
+                       tp_name = NULL;
+               } else {
+                       probe_name = "raw_tracepoint/sys_enter";
+                       tp_name = "sys_enter";
+               }
+
                prog = bpf_object__find_program_by_title(obj, probe_name);
                if (CHECK(!prog, "find_probe",
                          "prog '%s' not found\n", probe_name))
@@ -407,7 +487,7 @@ void test_core_reloc(void)
                                goto cleanup;
                }
 
-               link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
+               link = bpf_program__attach_raw_tracepoint(prog, tp_name);
                if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n",
                          PTR_ERR(link)))
                        goto cleanup;
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields.c
new file mode 100644 (file)
index 0000000..cff6f18
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_bitfields x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___bit_sz_change.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___bit_sz_change.c
new file mode 100644 (file)
index 0000000..a1cd157
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_bitfields___bit_sz_change x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___bitfield_vs_int.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___bitfield_vs_int.c
new file mode 100644 (file)
index 0000000..3f2c7b0
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_bitfields___bitfield_vs_int x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___err_too_big_bitfield.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___err_too_big_bitfield.c
new file mode 100644 (file)
index 0000000..f9746d6
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_bitfields___err_too_big_bitfield x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___just_big_enough.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_bitfields___just_big_enough.c
new file mode 100644 (file)
index 0000000..e7c75a6
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_bitfields___just_big_enough x) {}
index 3fe54f6..7eb08d9 100644 (file)
@@ -662,3 +662,75 @@ struct core_reloc_existence___err_wrong_arr_value_type {
 struct core_reloc_existence___err_wrong_struct_type {
        int s;
 };
+
+/*
+ * BITFIELDS
+ */
+/* bitfield read results, all as plain integers */
+struct core_reloc_bitfields_output {
+       int64_t         ub1;
+       int64_t         ub2;
+       int64_t         ub7;
+       int64_t         sb4;
+       int64_t         sb20;
+       int64_t         u32;
+       int64_t         s32;
+};
+
+struct core_reloc_bitfields {
+       /* unsigned bitfields */
+       uint8_t         ub1: 1;
+       uint8_t         ub2: 2;
+       uint32_t        ub7: 7;
+       /* signed bitfields */
+       int8_t          sb4: 4;
+       int32_t         sb20: 20;
+       /* non-bitfields */
+       uint32_t        u32;
+       int32_t         s32;
+};
+
+/* different bit sizes (both up and down) */
+struct core_reloc_bitfields___bit_sz_change {
+       /* unsigned bitfields */
+       uint16_t        ub1: 3;         /*  1 ->  3 */
+       uint32_t        ub2: 20;        /*  2 -> 20 */
+       uint8_t         ub7: 1;         /*  7 ->  1 */
+       /* signed bitfields */
+       int8_t          sb4: 1;         /*  4 ->  1 */
+       int32_t         sb20: 30;       /* 20 -> 30 */
+       /* non-bitfields */
+       uint16_t        u32;            /* 32 -> 16 */
+       int64_t         s32;            /* 32 -> 64 */
+};
+
+/* turn bitfield into non-bitfield and vice versa */
+struct core_reloc_bitfields___bitfield_vs_int {
+       uint64_t        ub1;            /*  3 -> 64 non-bitfield */
+       uint8_t         ub2;            /* 20 ->  8 non-bitfield */
+       int64_t         ub7;            /*  7 -> 64 non-bitfield signed */
+       int64_t         sb4;            /*  4 -> 64 non-bitfield signed */
+       uint64_t        sb20;           /* 20 -> 16 non-bitfield unsigned */
+       int32_t         u32: 20;        /* 32 non-bitfield -> 20 bitfield */
+       uint64_t        s32: 60;        /* 32 non-bitfield -> 60 bitfield */
+};
+
+struct core_reloc_bitfields___just_big_enough {
+       uint64_t        ub1: 4;
+       uint64_t        ub2: 60; /* packed tightly */
+       uint32_t        ub7;
+       uint32_t        sb4;
+       uint32_t        sb20;
+       uint32_t        u32;
+       uint32_t        s32;
+} __attribute__((packed)) ;
+
+struct core_reloc_bitfields___err_too_big_bitfield {
+       uint64_t        ub1: 4;
+       uint64_t        ub2: 61; /* packed tightly */
+       uint32_t        ub7;
+       uint32_t        sb4;
+       uint32_t        sb20;
+       uint32_t        u32;
+       uint32_t        s32;
+} __attribute__((packed)) ;
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_bitfields_direct.c b/tools/testing/selftests/bpf/progs/test_core_reloc_bitfields_direct.c
new file mode 100644 (file)
index 0000000..738b34b
--- /dev/null
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+#include "bpf_core_read.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+       char in[256];
+       char out[256];
+} data;
+
+struct core_reloc_bitfields {
+       /* unsigned bitfields */
+       uint8_t         ub1: 1;
+       uint8_t         ub2: 2;
+       uint32_t        ub7: 7;
+       /* signed bitfields */
+       int8_t          sb4: 4;
+       int32_t         sb20: 20;
+       /* non-bitfields */
+       uint32_t        u32;
+       int32_t         s32;
+};
+
+/* bitfield read results, all as plain integers */
+struct core_reloc_bitfields_output {
+       int64_t         ub1;
+       int64_t         ub2;
+       int64_t         ub7;
+       int64_t         sb4;
+       int64_t         sb20;
+       int64_t         u32;
+       int64_t         s32;
+};
+
+struct pt_regs;
+
+struct trace_sys_enter {
+       struct pt_regs *regs;
+       long id;
+};
+
+SEC("tp_btf/sys_enter")
+int test_core_bitfields_direct(void *ctx)
+{
+       struct core_reloc_bitfields *in = (void *)&data.in;
+       struct core_reloc_bitfields_output *out = (void *)&data.out;
+
+       out->ub1 = BPF_CORE_READ_BITFIELD(in, ub1);
+       out->ub2 = BPF_CORE_READ_BITFIELD(in, ub2);
+       out->ub7 = BPF_CORE_READ_BITFIELD(in, ub7);
+       out->sb4 = BPF_CORE_READ_BITFIELD(in, sb4);
+       out->sb20 = BPF_CORE_READ_BITFIELD(in, sb20);
+       out->u32 = BPF_CORE_READ_BITFIELD(in, u32);
+       out->s32 = BPF_CORE_READ_BITFIELD(in, s32);
+
+       return 0;
+}
+
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_bitfields_probed.c b/tools/testing/selftests/bpf/progs/test_core_reloc_bitfields_probed.c
new file mode 100644 (file)
index 0000000..a381f8a
--- /dev/null
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+#include "bpf_core_read.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+       char in[256];
+       char out[256];
+} data;
+
+struct core_reloc_bitfields {
+       /* unsigned bitfields */
+       uint8_t         ub1: 1;
+       uint8_t         ub2: 2;
+       uint32_t        ub7: 7;
+       /* signed bitfields */
+       int8_t          sb4: 4;
+       int32_t         sb20: 20;
+       /* non-bitfields */
+       uint32_t        u32;
+       int32_t         s32;
+};
+
+/* bitfield read results, all as plain integers */
+struct core_reloc_bitfields_output {
+       int64_t         ub1;
+       int64_t         ub2;
+       int64_t         ub7;
+       int64_t         sb4;
+       int64_t         sb20;
+       int64_t         u32;
+       int64_t         s32;
+};
+
+#define TRANSFER_BITFIELD(in, out, field)                              \
+       if (BPF_CORE_READ_BITFIELD_PROBED(in, field, &res))             \
+               return 1;                                               \
+       out->field = res
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_bitfields(void *ctx)
+{
+       struct core_reloc_bitfields *in = (void *)&data.in;
+       struct core_reloc_bitfields_output *out = (void *)&data.out;
+       uint64_t res;
+
+       TRANSFER_BITFIELD(in, out, ub1);
+       TRANSFER_BITFIELD(in, out, ub2);
+       TRANSFER_BITFIELD(in, out, ub7);
+       TRANSFER_BITFIELD(in, out, sb4);
+       TRANSFER_BITFIELD(in, out, sb20);
+       TRANSFER_BITFIELD(in, out, u32);
+       TRANSFER_BITFIELD(in, out, s32);
+
+       return 0;
+}
+