selftests/bpf: Add unit tests for pointers in global functions
authorDmitrii Banshchikov <me@ubique.spb.ru>
Fri, 12 Feb 2021 20:56:42 +0000 (00:56 +0400)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 13 Feb 2021 01:37:23 +0000 (17:37 -0800)
test_global_func9  - check valid pointer's scenarios
test_global_func10 - check that a smaller type cannot be passed as a
                     larger one
test_global_func11 - check that CTX pointer cannot be passed
test_global_func12 - check access to a null pointer
test_global_func13 - check access to an arbitrary pointer value
test_global_func14 - check that an opaque pointer cannot be passed
test_global_func15 - check that a variable has an unknown value after
     it was passed to a global function by pointer
test_global_func16 - check access to uninitialized stack memory

test_global_func_args - check read and write operations through a pointer

Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20210212205642.620788-5-me@ubique.spb.ru
tools/testing/selftests/bpf/prog_tests/global_func_args.c [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/test_global_funcs.c
tools/testing/selftests/bpf/progs/test_global_func10.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_global_func11.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_global_func12.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_global_func13.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_global_func14.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_global_func15.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_global_func16.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_global_func9.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_global_func_args.c [new file with mode: 0644]

diff --git a/tools/testing/selftests/bpf/prog_tests/global_func_args.c b/tools/testing/selftests/bpf/prog_tests/global_func_args.c
new file mode 100644 (file)
index 0000000..8bcc286
--- /dev/null
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "test_progs.h"
+#include "network_helpers.h"
+
+static __u32 duration;
+
+static void test_global_func_args0(struct bpf_object *obj)
+{
+       int err, i, map_fd, actual_value;
+       const char *map_name = "values";
+
+       map_fd = bpf_find_map(__func__, obj, map_name);
+       if (CHECK(map_fd < 0, "bpf_find_map", "cannot find BPF map %s: %s\n",
+               map_name, strerror(errno)))
+               return;
+
+       struct {
+               const char *descr;
+               int expected_value;
+       } tests[] = {
+               {"passing NULL pointer", 0},
+               {"returning value", 1},
+               {"reading local variable", 100 },
+               {"writing local variable", 101 },
+               {"reading global variable", 42 },
+               {"writing global variable", 43 },
+               {"writing to pointer-to-pointer", 1 },
+       };
+
+       for (i = 0; i < ARRAY_SIZE(tests); ++i) {
+               const int expected_value = tests[i].expected_value;
+
+               err = bpf_map_lookup_elem(map_fd, &i, &actual_value);
+
+               CHECK(err || actual_value != expected_value, tests[i].descr,
+                        "err %d result %d expected %d\n", err, actual_value, expected_value);
+       }
+}
+
+void test_global_func_args(void)
+{
+       const char *file = "./test_global_func_args.o";
+       __u32 retval;
+       struct bpf_object *obj;
+       int err, prog_fd;
+
+       err = bpf_prog_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd);
+       if (CHECK(err, "load program", "error %d loading %s\n", err, file))
+               return;
+
+       err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
+                               NULL, NULL, &retval, &duration);
+       CHECK(err || retval, "pass global func args run",
+             "err %d errno %d retval %d duration %d\n",
+             err, errno, retval, duration);
+
+       test_global_func_args0(obj);
+
+       bpf_object__close(obj);
+}
index 32e4348..7e13129 100644 (file)
@@ -61,6 +61,14 @@ void test_test_global_funcs(void)
                { "test_global_func6.o" , "modified ctx ptr R2" },
                { "test_global_func7.o" , "foo() doesn't return scalar" },
                { "test_global_func8.o" },
+               { "test_global_func9.o" },
+               { "test_global_func10.o", "invalid indirect read from stack" },
+               { "test_global_func11.o", "Caller passes invalid args into func#1" },
+               { "test_global_func12.o", "invalid mem access 'mem_or_null'" },
+               { "test_global_func13.o", "Caller passes invalid args into func#1" },
+               { "test_global_func14.o", "reference type('FWD S') size cannot be determined" },
+               { "test_global_func15.o", "At program exit the register R0 has value" },
+               { "test_global_func16.o", "invalid indirect read from stack" },
        };
        libbpf_print_fn_t old_print_fn = NULL;
        int err, i, duration = 0;
diff --git a/tools/testing/selftests/bpf/progs/test_global_func10.c b/tools/testing/selftests/bpf/progs/test_global_func10.c
new file mode 100644 (file)
index 0000000..61c2ae9
--- /dev/null
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct Small {
+       int x;
+};
+
+struct Big {
+       int x;
+       int y;
+};
+
+__noinline int foo(const struct Big *big)
+{
+       if (big == 0)
+               return 0;
+
+       return bpf_get_prandom_u32() < big->y;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+       const struct Small small = {.x = skb->len };
+
+       return foo((struct Big *)&small) ? 1 : 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func11.c b/tools/testing/selftests/bpf/progs/test_global_func11.c
new file mode 100644 (file)
index 0000000..2848804
--- /dev/null
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct S {
+       int x;
+};
+
+__noinline int foo(const struct S *s)
+{
+       return s ? bpf_get_prandom_u32() < s->x : 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+       return foo(skb);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func12.c b/tools/testing/selftests/bpf/progs/test_global_func12.c
new file mode 100644 (file)
index 0000000..6234352
--- /dev/null
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct S {
+       int x;
+};
+
+__noinline int foo(const struct S *s)
+{
+       return bpf_get_prandom_u32() < s->x;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+       const struct S s = {.x = skb->len };
+
+       return foo(&s);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func13.c b/tools/testing/selftests/bpf/progs/test_global_func13.c
new file mode 100644 (file)
index 0000000..ff8897c
--- /dev/null
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct S {
+       int x;
+};
+
+__noinline int foo(const struct S *s)
+{
+       if (s)
+               return bpf_get_prandom_u32() < s->x;
+
+       return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+       const struct S *s = (const struct S *)(0xbedabeda);
+
+       return foo(s);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func14.c b/tools/testing/selftests/bpf/progs/test_global_func14.c
new file mode 100644 (file)
index 0000000..698c771
--- /dev/null
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct S;
+
+__noinline int foo(const struct S *s)
+{
+       if (s)
+               return bpf_get_prandom_u32() < *(const int *) s;
+
+       return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+
+       return foo(NULL);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func15.c b/tools/testing/selftests/bpf/progs/test_global_func15.c
new file mode 100644 (file)
index 0000000..c19c435
--- /dev/null
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+__noinline int foo(unsigned int *v)
+{
+       if (v)
+               *v = bpf_get_prandom_u32();
+
+       return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+       unsigned int v = 1;
+
+       foo(&v);
+
+       return v;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func16.c b/tools/testing/selftests/bpf/progs/test_global_func16.c
new file mode 100644 (file)
index 0000000..0312d1e
--- /dev/null
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+__noinline int foo(int (*arr)[10])
+{
+       if (arr)
+               return (*arr)[9];
+
+       return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+       int array[10];
+
+       const int rv = foo(&array);
+
+       return rv ? 1 : 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func9.c b/tools/testing/selftests/bpf/progs/test_global_func9.c
new file mode 100644 (file)
index 0000000..bd233dd
--- /dev/null
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct S {
+       int x;
+};
+
+struct C {
+       int x;
+       int y;
+};
+
+struct {
+       __uint(type, BPF_MAP_TYPE_ARRAY);
+       __uint(max_entries, 1);
+       __type(key, __u32);
+       __type(value, struct S);
+} map SEC(".maps");
+
+enum E {
+       E_ITEM
+};
+
+static int global_data_x = 100;
+static int volatile global_data_y = 500;
+
+__noinline int foo(const struct S *s)
+{
+       if (s)
+               return bpf_get_prandom_u32() < s->x;
+
+       return 0;
+}
+
+__noinline int bar(int *x)
+{
+       if (x)
+               *x &= bpf_get_prandom_u32();
+
+       return 0;
+}
+__noinline int baz(volatile int *x)
+{
+       if (x)
+               *x &= bpf_get_prandom_u32();
+
+       return 0;
+}
+
+__noinline int qux(enum E *e)
+{
+       if (e)
+               return *e;
+
+       return 0;
+}
+
+__noinline int quux(int (*arr)[10])
+{
+       if (arr)
+               return (*arr)[9];
+
+       return 0;
+}
+
+__noinline int quuz(int **p)
+{
+       if (p)
+               *p = NULL;
+
+       return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+       int result = 0;
+
+       {
+               const struct S s = {.x = skb->len };
+
+               result |= foo(&s);
+       }
+
+       {
+               const __u32 key = 1;
+               const struct S *s = bpf_map_lookup_elem(&map, &key);
+
+               result |= foo(s);
+       }
+
+       {
+               const struct C c = {.x = skb->len, .y = skb->family };
+
+               result |= foo((const struct S *)&c);
+       }
+
+       {
+               result |= foo(NULL);
+       }
+
+       {
+               bar(&result);
+               bar(&global_data_x);
+       }
+
+       {
+               result |= baz(&global_data_y);
+       }
+
+       {
+               enum E e = E_ITEM;
+
+               result |= qux(&e);
+       }
+
+       {
+               int array[10] = {0};
+
+               result |= quux(&array);
+       }
+
+       {
+               int *p;
+
+               result |= quuz(&p);
+       }
+
+       return result ? 1 : 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func_args.c b/tools/testing/selftests/bpf/progs/test_global_func_args.c
new file mode 100644 (file)
index 0000000..cae3095
--- /dev/null
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+
+#include <bpf/bpf_helpers.h>
+
+struct S {
+       int v;
+};
+
+static volatile struct S global_variable;
+
+struct {
+       __uint(type, BPF_MAP_TYPE_ARRAY);
+       __uint(max_entries, 7);
+       __type(key, __u32);
+       __type(value, int);
+} values SEC(".maps");
+
+static void save_value(__u32 index, int value)
+{
+       bpf_map_update_elem(&values, &index, &value, 0);
+}
+
+__noinline int foo(__u32 index, struct S *s)
+{
+       if (s) {
+               save_value(index, s->v);
+               return ++s->v;
+       }
+
+       save_value(index, 0);
+
+       return 1;
+}
+
+__noinline int bar(__u32 index, volatile struct S *s)
+{
+       if (s) {
+               save_value(index, s->v);
+               return ++s->v;
+       }
+
+       save_value(index, 0);
+
+       return 1;
+}
+
+__noinline int baz(struct S **s)
+{
+       if (s)
+               *s = 0;
+
+       return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+       __u32 index = 0;
+
+       {
+               const int v = foo(index++, 0);
+
+               save_value(index++, v);
+       }
+
+       {
+               struct S s = { .v = 100 };
+
+               foo(index++, &s);
+               save_value(index++, s.v);
+       }
+
+       {
+               global_variable.v = 42;
+               bar(index++, &global_variable);
+               save_value(index++, global_variable.v);
+       }
+
+       {
+               struct S v, *p = &v;
+
+               baz(&p);
+               save_value(index++, !p);
+       }
+
+       return 0;
+}
+
+char _license[] SEC("license") = "GPL";