Message ID | 20211209173559.1529291-1-yhs@fb.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | BPF |
Headers | show |
Series | [bpf-next,1/5] compiler_types: define __user as __attribute__((btf_type_tag("user"))) | expand |
On Thu, Dec 09, 2021 at 09:35:59AM -0800, Yonghong Song wrote: > Added a selftest with two __user usages: a __user pointer-type argument > and a __user pointer-type struct member. In both cases, > directly accessing the user memory will result verification failure. ... > diff --git a/tools/testing/selftests/bpf/progs/btf_type_tag_user.c b/tools/testing/selftests/bpf/progs/btf_type_tag_user.c > new file mode 100644 > index 000000000000..e149854f42dd > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/btf_type_tag_user.c > @@ -0,0 +1,29 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* Copyright (c) 2021 Facebook */ > +#include "vmlinux.h" > +#include <bpf/bpf_helpers.h> > +#include <bpf/bpf_tracing.h> > + > +struct bpf_testmod_btf_type_tag_1 { > + int a; > +}; > + > +struct bpf_testmod_btf_type_tag_2 { > + struct bpf_testmod_btf_type_tag_1 *p; > +}; > + > +int g; > + > +SEC("fentry/bpf_testmod_test_btf_type_tag_user_1") > +int BPF_PROG(test_user1, struct bpf_testmod_btf_type_tag_1 *arg) > +{ > + g = arg->a; > + return 0; > +} > + > +SEC("fentry/bpf_testmod_test_btf_type_tag_user_2") > +int BPF_PROG(test_user2, struct bpf_testmod_btf_type_tag_2 *arg) > +{ > + g = arg->p->a; > + return 0; > +} This is a targeted synthetic test. Great, but can you add one that probes real kernel function like: getsockname(int fd, struct sockaddr __user *usockaddr or getpeername(int fd, struct sockaddr __user *usockaddr and the bpf prog tries to deref usockaddr ?
On 12/19/21 5:51 PM, Alexei Starovoitov wrote: > On Thu, Dec 09, 2021 at 09:35:59AM -0800, Yonghong Song wrote: >> Added a selftest with two __user usages: a __user pointer-type argument >> and a __user pointer-type struct member. In both cases, >> directly accessing the user memory will result verification failure. > ... >> diff --git a/tools/testing/selftests/bpf/progs/btf_type_tag_user.c b/tools/testing/selftests/bpf/progs/btf_type_tag_user.c >> new file mode 100644 >> index 000000000000..e149854f42dd >> --- /dev/null >> +++ b/tools/testing/selftests/bpf/progs/btf_type_tag_user.c >> @@ -0,0 +1,29 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* Copyright (c) 2021 Facebook */ >> +#include "vmlinux.h" >> +#include <bpf/bpf_helpers.h> >> +#include <bpf/bpf_tracing.h> >> + >> +struct bpf_testmod_btf_type_tag_1 { >> + int a; >> +}; >> + >> +struct bpf_testmod_btf_type_tag_2 { >> + struct bpf_testmod_btf_type_tag_1 *p; >> +}; >> + >> +int g; >> + >> +SEC("fentry/bpf_testmod_test_btf_type_tag_user_1") >> +int BPF_PROG(test_user1, struct bpf_testmod_btf_type_tag_1 *arg) >> +{ >> + g = arg->a; >> + return 0; >> +} >> + >> +SEC("fentry/bpf_testmod_test_btf_type_tag_user_2") >> +int BPF_PROG(test_user2, struct bpf_testmod_btf_type_tag_2 *arg) >> +{ >> + g = arg->p->a; >> + return 0; >> +} > > This is a targeted synthetic test. Great, but can you add one > that probes real kernel function like: > getsockname(int fd, struct sockaddr __user *usockaddr > or > getpeername(int fd, struct sockaddr __user *usockaddr > and the bpf prog tries to deref usockaddr ? Ack. Will do.
diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c index 5d52ea2768df..119fd5ba7ce4 100644 --- a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +++ b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c @@ -21,6 +21,24 @@ bpf_testmod_test_mod_kfunc(int i) *(int *)this_cpu_ptr(&bpf_testmod_ksym_percpu) = i; } +struct bpf_testmod_btf_type_tag_1 { + int a; +}; + +struct bpf_testmod_btf_type_tag_2 { + struct bpf_testmod_btf_type_tag_1 __user *p; +}; + +noinline int +bpf_testmod_test_btf_type_tag_user_1(struct bpf_testmod_btf_type_tag_1 __user *arg) { + return arg->a; +} + +noinline int +bpf_testmod_test_btf_type_tag_user_2(struct bpf_testmod_btf_type_tag_2 *arg) { + return arg->p->a; +} + noinline int bpf_testmod_loop_test(int n) { int i, sum = 0; diff --git a/tools/testing/selftests/bpf/prog_tests/btf_tag.c b/tools/testing/selftests/bpf/prog_tests/btf_tag.c index c4cf27777ff7..794752265ede 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_tag.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_tag.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2021 Facebook */ #include <test_progs.h> +#include <bpf/btf.h> #include "test_btf_decl_tag.skel.h" /* struct btf_type_tag_test is referenced in btf_type_tag.skel.h */ @@ -8,6 +9,7 @@ struct btf_type_tag_test { int **p; }; #include "btf_type_tag.skel.h" +#include "btf_type_tag_user.skel.h" static void test_btf_decl_tag(void) { @@ -41,10 +43,63 @@ static void test_btf_type_tag(void) btf_type_tag__destroy(skel); } +static void test_btf_type_tag_user(bool load_test_user1) +{ + const char *module_name = "bpf_testmod"; + struct btf *vmlinux_btf, *module_btf; + struct btf_type_tag_user *skel; + __s32 type_id; + int err; + + if (!env.has_testmod) { + test__skip(); + return; + } + + /* skip the test is the module does not have __user tags */ + vmlinux_btf = btf__load_vmlinux_btf(); + if (!ASSERT_OK_PTR(vmlinux_btf, "could not load vmlinux BTF")) + return; + + module_btf = btf__load_module_btf(module_name, vmlinux_btf); + if (!ASSERT_OK_PTR(module_btf, "could not load module BTF")) + goto free_vmlinux_btf; + + type_id = btf__find_by_name_kind(module_btf, "user", BTF_KIND_TYPE_TAG); + if (type_id <= 0) { + printf("%s:SKIP: btf_type_tag attribute not in %s", __func__, module_name); + test__skip(); + goto free_module_btf; + } + + skel = btf_type_tag_user__open(); + if (!ASSERT_OK_PTR(skel, "btf_type_tag_user")) + goto free_module_btf; + + if (load_test_user1) + bpf_program__set_autoload(skel->progs.test_user2, false); + else + bpf_program__set_autoload(skel->progs.test_user1, false); + + err = btf_type_tag_user__load(skel); + ASSERT_ERR(err, "btf_type_tag_user"); + + btf_type_tag_user__destroy(skel); + +free_module_btf: + btf__free(module_btf); +free_vmlinux_btf: + btf__free(vmlinux_btf); +} + void test_btf_tag(void) { if (test__start_subtest("btf_decl_tag")) test_btf_decl_tag(); if (test__start_subtest("btf_type_tag")) test_btf_type_tag(); + if (test__start_subtest("btf_type_tag_user_1")) + test_btf_type_tag_user(true); + if (test__start_subtest("btf_type_tag_user_2")) + test_btf_type_tag_user(false); } diff --git a/tools/testing/selftests/bpf/progs/btf_type_tag_user.c b/tools/testing/selftests/bpf/progs/btf_type_tag_user.c new file mode 100644 index 000000000000..e149854f42dd --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf_type_tag_user.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2021 Facebook */ +#include "vmlinux.h" +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> + +struct bpf_testmod_btf_type_tag_1 { + int a; +}; + +struct bpf_testmod_btf_type_tag_2 { + struct bpf_testmod_btf_type_tag_1 *p; +}; + +int g; + +SEC("fentry/bpf_testmod_test_btf_type_tag_user_1") +int BPF_PROG(test_user1, struct bpf_testmod_btf_type_tag_1 *arg) +{ + g = arg->a; + return 0; +} + +SEC("fentry/bpf_testmod_test_btf_type_tag_user_2") +int BPF_PROG(test_user2, struct bpf_testmod_btf_type_tag_2 *arg) +{ + g = arg->p->a; + return 0; +}
Added a selftest with two __user usages: a __user pointer-type argument and a __user pointer-type struct member. In both cases, directly accessing the user memory will result verification failure. $ ./test_progs -v -n 22/3 ... libbpf: load bpf program failed: Permission denied libbpf: -- BEGIN DUMP LOG --- libbpf: R1 type=ctx expected=fp ; int BPF_PROG(test_user1, struct bpf_testmod_btf_type_tag_1 *arg) 0: (79) r1 = *(u64 *)(r1 +0) func 'bpf_testmod_test_btf_type_tag_user_1' arg0 has btf_id 143948 addr_space 1 type STRUCT 'bpf_testmod_btf_type_tag_1' ; g = arg->a; 1: (61) r1 = *(u32 *)(r1 +0) R1 is ptr_bpf_testmod_btf_type_tag_1 access user memory: off=0 ... #22/3 btf_tag/btf_type_tag_user_1:OK $ ./test_progs -v -n 22/4 ... libbpf: load bpf program failed: Permission denied libbpf: -- BEGIN DUMP LOG --- libbpf: R1 type=ctx expected=fp ; int BPF_PROG(test_user2, struct bpf_testmod_btf_type_tag_2 *arg) 0: (79) r1 = *(u64 *)(r1 +0) func 'bpf_testmod_test_btf_type_tag_user_2' arg0 has btf_id 143950 addr_space 0 type STRUCT 'bpf_testmod_btf_type_tag_2' ; g = arg->p->a; 1: (79) r1 = *(u64 *)(r1 +0) ; g = arg->p->a; 2: (61) r1 = *(u32 *)(r1 +0) R1 is ptr_bpf_testmod_btf_type_tag_1 access user memory: off=0 ... #22/4 btf_tag/btf_type_tag_user_2:OK Signed-off-by: Yonghong Song <yhs@fb.com> --- .../selftests/bpf/bpf_testmod/bpf_testmod.c | 18 ++++++ .../selftests/bpf/prog_tests/btf_tag.c | 55 +++++++++++++++++++ .../selftests/bpf/progs/btf_type_tag_user.c | 29 ++++++++++ 3 files changed, 102 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/btf_type_tag_user.c