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
This commit is contained in:
parent
e5069b9c23
commit
8b08807d03
60
tools/testing/selftests/bpf/prog_tests/global_func_args.c
Normal file
60
tools/testing/selftests/bpf/prog_tests/global_func_args.c
Normal file
@ -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);
|
||||||
|
}
|
@ -61,6 +61,14 @@ void test_test_global_funcs(void)
|
|||||||
{ "test_global_func6.o" , "modified ctx ptr R2" },
|
{ "test_global_func6.o" , "modified ctx ptr R2" },
|
||||||
{ "test_global_func7.o" , "foo() doesn't return scalar" },
|
{ "test_global_func7.o" , "foo() doesn't return scalar" },
|
||||||
{ "test_global_func8.o" },
|
{ "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;
|
libbpf_print_fn_t old_print_fn = NULL;
|
||||||
int err, i, duration = 0;
|
int err, i, duration = 0;
|
||||||
|
29
tools/testing/selftests/bpf/progs/test_global_func10.c
Normal file
29
tools/testing/selftests/bpf/progs/test_global_func10.c
Normal file
@ -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;
|
||||||
|
}
|
19
tools/testing/selftests/bpf/progs/test_global_func11.c
Normal file
19
tools/testing/selftests/bpf/progs/test_global_func11.c
Normal file
@ -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);
|
||||||
|
}
|
21
tools/testing/selftests/bpf/progs/test_global_func12.c
Normal file
21
tools/testing/selftests/bpf/progs/test_global_func12.c
Normal file
@ -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);
|
||||||
|
}
|
24
tools/testing/selftests/bpf/progs/test_global_func13.c
Normal file
24
tools/testing/selftests/bpf/progs/test_global_func13.c
Normal file
@ -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);
|
||||||
|
}
|
21
tools/testing/selftests/bpf/progs/test_global_func14.c
Normal file
21
tools/testing/selftests/bpf/progs/test_global_func14.c
Normal file
@ -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);
|
||||||
|
}
|
22
tools/testing/selftests/bpf/progs/test_global_func15.c
Normal file
22
tools/testing/selftests/bpf/progs/test_global_func15.c
Normal file
@ -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;
|
||||||
|
}
|
22
tools/testing/selftests/bpf/progs/test_global_func16.c
Normal file
22
tools/testing/selftests/bpf/progs/test_global_func16.c
Normal file
@ -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;
|
||||||
|
}
|
132
tools/testing/selftests/bpf/progs/test_global_func9.c
Normal file
132
tools/testing/selftests/bpf/progs/test_global_func9.c
Normal file
@ -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;
|
||||||
|
}
|
91
tools/testing/selftests/bpf/progs/test_global_func_args.c
Normal file
91
tools/testing/selftests/bpf/progs/test_global_func_args.c
Normal file
@ -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";
|
Loading…
x
Reference in New Issue
Block a user