Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add unit tests for pointers in global functions

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

authored by

Dmitrii Banshchikov and committed by
Alexei Starovoitov
8b08807d e5069b9c

+449
+60
tools/testing/selftests/bpf/prog_tests/global_func_args.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include "test_progs.h" 3 + #include "network_helpers.h" 4 + 5 + static __u32 duration; 6 + 7 + static void test_global_func_args0(struct bpf_object *obj) 8 + { 9 + int err, i, map_fd, actual_value; 10 + const char *map_name = "values"; 11 + 12 + map_fd = bpf_find_map(__func__, obj, map_name); 13 + if (CHECK(map_fd < 0, "bpf_find_map", "cannot find BPF map %s: %s\n", 14 + map_name, strerror(errno))) 15 + return; 16 + 17 + struct { 18 + const char *descr; 19 + int expected_value; 20 + } tests[] = { 21 + {"passing NULL pointer", 0}, 22 + {"returning value", 1}, 23 + {"reading local variable", 100 }, 24 + {"writing local variable", 101 }, 25 + {"reading global variable", 42 }, 26 + {"writing global variable", 43 }, 27 + {"writing to pointer-to-pointer", 1 }, 28 + }; 29 + 30 + for (i = 0; i < ARRAY_SIZE(tests); ++i) { 31 + const int expected_value = tests[i].expected_value; 32 + 33 + err = bpf_map_lookup_elem(map_fd, &i, &actual_value); 34 + 35 + CHECK(err || actual_value != expected_value, tests[i].descr, 36 + "err %d result %d expected %d\n", err, actual_value, expected_value); 37 + } 38 + } 39 + 40 + void test_global_func_args(void) 41 + { 42 + const char *file = "./test_global_func_args.o"; 43 + __u32 retval; 44 + struct bpf_object *obj; 45 + int err, prog_fd; 46 + 47 + err = bpf_prog_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd); 48 + if (CHECK(err, "load program", "error %d loading %s\n", err, file)) 49 + return; 50 + 51 + err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), 52 + NULL, NULL, &retval, &duration); 53 + CHECK(err || retval, "pass global func args run", 54 + "err %d errno %d retval %d duration %d\n", 55 + err, errno, retval, duration); 56 + 57 + test_global_func_args0(obj); 58 + 59 + bpf_object__close(obj); 60 + }
+8
tools/testing/selftests/bpf/prog_tests/test_global_funcs.c
··· 61 61 { "test_global_func6.o" , "modified ctx ptr R2" }, 62 62 { "test_global_func7.o" , "foo() doesn't return scalar" }, 63 63 { "test_global_func8.o" }, 64 + { "test_global_func9.o" }, 65 + { "test_global_func10.o", "invalid indirect read from stack" }, 66 + { "test_global_func11.o", "Caller passes invalid args into func#1" }, 67 + { "test_global_func12.o", "invalid mem access 'mem_or_null'" }, 68 + { "test_global_func13.o", "Caller passes invalid args into func#1" }, 69 + { "test_global_func14.o", "reference type('FWD S') size cannot be determined" }, 70 + { "test_global_func15.o", "At program exit the register R0 has value" }, 71 + { "test_global_func16.o", "invalid indirect read from stack" }, 64 72 }; 65 73 libbpf_print_fn_t old_print_fn = NULL; 66 74 int err, i, duration = 0;
+29
tools/testing/selftests/bpf/progs/test_global_func10.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + #include <stddef.h> 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + struct Small { 7 + int x; 8 + }; 9 + 10 + struct Big { 11 + int x; 12 + int y; 13 + }; 14 + 15 + __noinline int foo(const struct Big *big) 16 + { 17 + if (big == 0) 18 + return 0; 19 + 20 + return bpf_get_prandom_u32() < big->y; 21 + } 22 + 23 + SEC("cgroup_skb/ingress") 24 + int test_cls(struct __sk_buff *skb) 25 + { 26 + const struct Small small = {.x = skb->len }; 27 + 28 + return foo((struct Big *)&small) ? 1 : 0; 29 + }
+19
tools/testing/selftests/bpf/progs/test_global_func11.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + #include <stddef.h> 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + struct S { 7 + int x; 8 + }; 9 + 10 + __noinline int foo(const struct S *s) 11 + { 12 + return s ? bpf_get_prandom_u32() < s->x : 0; 13 + } 14 + 15 + SEC("cgroup_skb/ingress") 16 + int test_cls(struct __sk_buff *skb) 17 + { 18 + return foo(skb); 19 + }
+21
tools/testing/selftests/bpf/progs/test_global_func12.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + #include <stddef.h> 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + struct S { 7 + int x; 8 + }; 9 + 10 + __noinline int foo(const struct S *s) 11 + { 12 + return bpf_get_prandom_u32() < s->x; 13 + } 14 + 15 + SEC("cgroup_skb/ingress") 16 + int test_cls(struct __sk_buff *skb) 17 + { 18 + const struct S s = {.x = skb->len }; 19 + 20 + return foo(&s); 21 + }
+24
tools/testing/selftests/bpf/progs/test_global_func13.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + #include <stddef.h> 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + struct S { 7 + int x; 8 + }; 9 + 10 + __noinline int foo(const struct S *s) 11 + { 12 + if (s) 13 + return bpf_get_prandom_u32() < s->x; 14 + 15 + return 0; 16 + } 17 + 18 + SEC("cgroup_skb/ingress") 19 + int test_cls(struct __sk_buff *skb) 20 + { 21 + const struct S *s = (const struct S *)(0xbedabeda); 22 + 23 + return foo(s); 24 + }
+21
tools/testing/selftests/bpf/progs/test_global_func14.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + #include <stddef.h> 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + struct S; 7 + 8 + __noinline int foo(const struct S *s) 9 + { 10 + if (s) 11 + return bpf_get_prandom_u32() < *(const int *) s; 12 + 13 + return 0; 14 + } 15 + 16 + SEC("cgroup_skb/ingress") 17 + int test_cls(struct __sk_buff *skb) 18 + { 19 + 20 + return foo(NULL); 21 + }
+22
tools/testing/selftests/bpf/progs/test_global_func15.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + #include <stddef.h> 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + __noinline int foo(unsigned int *v) 7 + { 8 + if (v) 9 + *v = bpf_get_prandom_u32(); 10 + 11 + return 0; 12 + } 13 + 14 + SEC("cgroup_skb/ingress") 15 + int test_cls(struct __sk_buff *skb) 16 + { 17 + unsigned int v = 1; 18 + 19 + foo(&v); 20 + 21 + return v; 22 + }
+22
tools/testing/selftests/bpf/progs/test_global_func16.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + #include <stddef.h> 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + __noinline int foo(int (*arr)[10]) 7 + { 8 + if (arr) 9 + return (*arr)[9]; 10 + 11 + return 0; 12 + } 13 + 14 + SEC("cgroup_skb/ingress") 15 + int test_cls(struct __sk_buff *skb) 16 + { 17 + int array[10]; 18 + 19 + const int rv = foo(&array); 20 + 21 + return rv ? 1 : 0; 22 + }
+132
tools/testing/selftests/bpf/progs/test_global_func9.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + #include <stddef.h> 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + struct S { 7 + int x; 8 + }; 9 + 10 + struct C { 11 + int x; 12 + int y; 13 + }; 14 + 15 + struct { 16 + __uint(type, BPF_MAP_TYPE_ARRAY); 17 + __uint(max_entries, 1); 18 + __type(key, __u32); 19 + __type(value, struct S); 20 + } map SEC(".maps"); 21 + 22 + enum E { 23 + E_ITEM 24 + }; 25 + 26 + static int global_data_x = 100; 27 + static int volatile global_data_y = 500; 28 + 29 + __noinline int foo(const struct S *s) 30 + { 31 + if (s) 32 + return bpf_get_prandom_u32() < s->x; 33 + 34 + return 0; 35 + } 36 + 37 + __noinline int bar(int *x) 38 + { 39 + if (x) 40 + *x &= bpf_get_prandom_u32(); 41 + 42 + return 0; 43 + } 44 + __noinline int baz(volatile int *x) 45 + { 46 + if (x) 47 + *x &= bpf_get_prandom_u32(); 48 + 49 + return 0; 50 + } 51 + 52 + __noinline int qux(enum E *e) 53 + { 54 + if (e) 55 + return *e; 56 + 57 + return 0; 58 + } 59 + 60 + __noinline int quux(int (*arr)[10]) 61 + { 62 + if (arr) 63 + return (*arr)[9]; 64 + 65 + return 0; 66 + } 67 + 68 + __noinline int quuz(int **p) 69 + { 70 + if (p) 71 + *p = NULL; 72 + 73 + return 0; 74 + } 75 + 76 + SEC("cgroup_skb/ingress") 77 + int test_cls(struct __sk_buff *skb) 78 + { 79 + int result = 0; 80 + 81 + { 82 + const struct S s = {.x = skb->len }; 83 + 84 + result |= foo(&s); 85 + } 86 + 87 + { 88 + const __u32 key = 1; 89 + const struct S *s = bpf_map_lookup_elem(&map, &key); 90 + 91 + result |= foo(s); 92 + } 93 + 94 + { 95 + const struct C c = {.x = skb->len, .y = skb->family }; 96 + 97 + result |= foo((const struct S *)&c); 98 + } 99 + 100 + { 101 + result |= foo(NULL); 102 + } 103 + 104 + { 105 + bar(&result); 106 + bar(&global_data_x); 107 + } 108 + 109 + { 110 + result |= baz(&global_data_y); 111 + } 112 + 113 + { 114 + enum E e = E_ITEM; 115 + 116 + result |= qux(&e); 117 + } 118 + 119 + { 120 + int array[10] = {0}; 121 + 122 + result |= quux(&array); 123 + } 124 + 125 + { 126 + int *p; 127 + 128 + result |= quuz(&p); 129 + } 130 + 131 + return result ? 1 : 0; 132 + }
+91
tools/testing/selftests/bpf/progs/test_global_func_args.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/bpf.h> 4 + 5 + #include <bpf/bpf_helpers.h> 6 + 7 + struct S { 8 + int v; 9 + }; 10 + 11 + static volatile struct S global_variable; 12 + 13 + struct { 14 + __uint(type, BPF_MAP_TYPE_ARRAY); 15 + __uint(max_entries, 7); 16 + __type(key, __u32); 17 + __type(value, int); 18 + } values SEC(".maps"); 19 + 20 + static void save_value(__u32 index, int value) 21 + { 22 + bpf_map_update_elem(&values, &index, &value, 0); 23 + } 24 + 25 + __noinline int foo(__u32 index, struct S *s) 26 + { 27 + if (s) { 28 + save_value(index, s->v); 29 + return ++s->v; 30 + } 31 + 32 + save_value(index, 0); 33 + 34 + return 1; 35 + } 36 + 37 + __noinline int bar(__u32 index, volatile struct S *s) 38 + { 39 + if (s) { 40 + save_value(index, s->v); 41 + return ++s->v; 42 + } 43 + 44 + save_value(index, 0); 45 + 46 + return 1; 47 + } 48 + 49 + __noinline int baz(struct S **s) 50 + { 51 + if (s) 52 + *s = 0; 53 + 54 + return 0; 55 + } 56 + 57 + SEC("cgroup_skb/ingress") 58 + int test_cls(struct __sk_buff *skb) 59 + { 60 + __u32 index = 0; 61 + 62 + { 63 + const int v = foo(index++, 0); 64 + 65 + save_value(index++, v); 66 + } 67 + 68 + { 69 + struct S s = { .v = 100 }; 70 + 71 + foo(index++, &s); 72 + save_value(index++, s.v); 73 + } 74 + 75 + { 76 + global_variable.v = 42; 77 + bar(index++, &global_variable); 78 + save_value(index++, global_variable.v); 79 + } 80 + 81 + { 82 + struct S v, *p = &v; 83 + 84 + baz(&p); 85 + save_value(index++, !p); 86 + } 87 + 88 + return 0; 89 + } 90 + 91 + char _license[] SEC("license") = "GPL";