> On Nov 4, 2021, at 12:00 AM, Song Liu <songliubraving@xxxxxx> wrote: > > Add tests for bpf_find_vma in perf_event program and kprobe program. The > perf_event program is triggered from NMI context, so the second call of > bpf_find_vma() will return -EBUSY (irq_work busy). The kprobe program, > on the other hand, does not have this constraint. > > Also add test for illegal writes to task or vma from the callback > function. The verifier should reject both cases. > > Signed-off-by: Song Liu <songliubraving@xxxxxx> > --- > .../selftests/bpf/prog_tests/find_vma.c | 115 ++++++++++++++++++ > tools/testing/selftests/bpf/progs/find_vma.c | 70 +++++++++++ > .../selftests/bpf/progs/find_vma_fail1.c | 30 +++++ > .../selftests/bpf/progs/find_vma_fail2.c | 30 +++++ > 4 files changed, 245 insertions(+) > create mode 100644 tools/testing/selftests/bpf/prog_tests/find_vma.c > create mode 100644 tools/testing/selftests/bpf/progs/find_vma.c > create mode 100644 tools/testing/selftests/bpf/progs/find_vma_fail1.c > create mode 100644 tools/testing/selftests/bpf/progs/find_vma_fail2.c > > diff --git a/tools/testing/selftests/bpf/prog_tests/find_vma.c b/tools/testing/selftests/bpf/prog_tests/find_vma.c > new file mode 100644 > index 0000000000000..3955a92d4c152 > --- /dev/null > +++ b/tools/testing/selftests/bpf/prog_tests/find_vma.c > @@ -0,0 +1,115 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* Copyright (c) 2021 Facebook */ > +#include <test_progs.h> > +#include <sys/types.h> > +#include <unistd.h> > +#include "find_vma.skel.h" > +#include "find_vma_fail1.skel.h" > +#include "find_vma_fail2.skel.h" > + > +static void test_and_reset_skel(struct find_vma *skel, int expected_find_zero_ret) > +{ > + ASSERT_EQ(skel->bss->found_vm_exec, 1, "found_vm_exec"); > + ASSERT_EQ(skel->data->find_addr_ret, 0, "find_addr_ret"); > + ASSERT_EQ(skel->data->find_zero_ret, expected_find_zero_ret, "find_zero_ret"); > + ASSERT_OK_PTR(strstr(skel->bss->d_iname, "test_progs"), "find_test_progs"); > + > + skel->bss->found_vm_exec = 0; > + skel->data->find_addr_ret = -1; > + skel->data->find_zero_ret = -1; > + skel->bss->d_iname[0] = 0; > +} > + > +static int open_pe(void) > +{ > + struct perf_event_attr attr = {0}; > + int pfd; > + > + /* create perf event */ > + attr.size = sizeof(attr); > + attr.type = PERF_TYPE_HARDWARE; > + attr.config = PERF_COUNT_HW_CPU_CYCLES; > + attr.freq = 1; > + attr.sample_freq = 4000; > + pfd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, PERF_FLAG_FD_CLOEXEC); > + > + return pfd >= 0 ? pfd : -errno; > +} > + > +static void test_find_vma_pe(struct find_vma *skel) > +{ > + struct bpf_link *link = NULL; > + volatile int j = 0; > + int pfd = -1, i; > + > + pfd = open_pe(); > + if (pfd < 0) { > + if (pfd == -ENOENT || pfd == -EOPNOTSUPP) { > + printf("%s:SKIP:no PERF_COUNT_HW_CPU_CYCLES\n", __func__); > + test__skip(); Ah, I missed a goto cleanup here, so it breaks vmtest. I can fix this in v3, or we can fix it when applying the patch. > + } > + if (!ASSERT_GE(pfd, 0, "perf_event_open")) > + goto cleanup; > + } > + > + link = bpf_program__attach_perf_event(skel->progs.handle_pe, pfd); > + if (!ASSERT_OK_PTR(link, "attach_perf_event")) > + goto cleanup; > + > + for (i = 0; i < 1000000; ++i) > + ++j; > + > + test_and_reset_skel(skel, -EBUSY /* in nmi, irq_work is busy */); > +cleanup: > + bpf_link__destroy(link); > + close(pfd); > + /* caller will clean up skel */ > +} > + > +static void test_find_vma_kprobe(struct find_vma *skel) > +{ > + int err; > + > + err = find_vma__attach(skel); > + if (!ASSERT_OK(err, "get_branch_snapshot__attach")) > + return; /* caller will cleanup skel */ > + > + getpgid(skel->bss->target_pid); > + test_and_reset_skel(skel, -ENOENT /* could not find vma for ptr 0 */); > +} > + > +static void test_illegal_write_vma(void) > +{ > + struct find_vma_fail1 *skel; > + > + skel = find_vma_fail1__open_and_load(); > + ASSERT_ERR_PTR(skel, "find_vma_fail1__open_and_load"); > +} > + > +static void test_illegal_write_task(void) > +{ > + struct find_vma_fail2 *skel; > + > + skel = find_vma_fail2__open_and_load(); > + ASSERT_ERR_PTR(skel, "find_vma_fail2__open_and_load"); > +} > + > +void serial_test_find_vma(void) > +{ > + struct find_vma *skel; > + > + skel = find_vma__open_and_load(); > + if (!ASSERT_OK_PTR(skel, "find_vma__open_and_load")) > + return; > + > + skel->bss->target_pid = getpid(); > + skel->bss->addr = (__u64)test_find_vma_pe; > + > + test_find_vma_pe(skel); > + usleep(100000); /* allow the irq_work to finish */ > + test_find_vma_kprobe(skel); > + > + find_vma__destroy(skel); > + test_illegal_write_vma(); > + test_illegal_write_task(); > +} > diff --git a/tools/testing/selftests/bpf/progs/find_vma.c b/tools/testing/selftests/bpf/progs/find_vma.c > new file mode 100644 > index 0000000000000..2776718a54e29 > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/find_vma.c > @@ -0,0 +1,70 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* Copyright (c) 2021 Facebook */ > +#include "vmlinux.h" > +#include <bpf/bpf_helpers.h> > +#include <bpf/bpf_tracing.h> > + > +char _license[] SEC("license") = "GPL"; > + > +struct callback_ctx { > + int dummy; > +}; > + > +#define VM_EXEC 0x00000004 > +#define DNAME_INLINE_LEN 32 > + > +pid_t target_pid = 0; > +char d_iname[DNAME_INLINE_LEN] = {0}; > +__u32 found_vm_exec = 0; > +__u64 addr = 0; > +int find_zero_ret = -1; > +int find_addr_ret = -1; > + > +static __u64 > +check_vma(struct task_struct *task, struct vm_area_struct *vma, > + struct callback_ctx *data) > +{ > + if (vma->vm_file) > + bpf_probe_read_kernel_str(d_iname, DNAME_INLINE_LEN - 1, > + vma->vm_file->f_path.dentry->d_iname); > + > + /* check for VM_EXEC */ > + if (vma->vm_flags & VM_EXEC) > + found_vm_exec = 1; > + > + return 0; > +} > + > +SEC("kprobe/__x64_sys_getpgid") > +int handle_getpid(void) > +{ > + struct task_struct *task = bpf_get_current_task_btf(); > + struct callback_ctx data = {0}; > + > + if (task->pid != target_pid) > + return 0; > + > + find_addr_ret = bpf_find_vma(task, addr, check_vma, &data, 0); > + > + /* this should return -ENOENT */ > + find_zero_ret = bpf_find_vma(task, 0, check_vma, &data, 0); > + return 0; > +} > + > +SEC("perf_event") > +int handle_pe(void) > +{ > + struct task_struct *task = bpf_get_current_task_btf(); > + struct callback_ctx data = {0}; > + > + if (task->pid != target_pid) > + return 0; > + > + find_addr_ret = bpf_find_vma(task, addr, check_vma, &data, 0); > + > + /* In NMI, this should return -EBUSY, as the previous call is using > + * the irq_work. > + */ > + find_zero_ret = bpf_find_vma(task, 0, check_vma, &data, 0); > + return 0; > +} > diff --git a/tools/testing/selftests/bpf/progs/find_vma_fail1.c b/tools/testing/selftests/bpf/progs/find_vma_fail1.c > new file mode 100644 > index 0000000000000..d17bdcdf76f07 > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/find_vma_fail1.c > @@ -0,0 +1,30 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* Copyright (c) 2021 Facebook */ > +#include "vmlinux.h" > +#include <bpf/bpf_helpers.h> > + > +char _license[] SEC("license") = "GPL"; > + > +struct callback_ctx { > + int dummy; > +}; > + > +static __u64 > +write_vma(struct task_struct *task, struct vm_area_struct *vma, > + struct callback_ctx *data) > +{ > + /* writing to vma, which is illegal */ > + vma->vm_flags |= 0x55; > + > + return 0; > +} > + > +SEC("kprobe/__x64_sys_getpgid") > +int handle_getpid(void) > +{ > + struct task_struct *task = bpf_get_current_task_btf(); > + struct callback_ctx data = {0}; > + > + bpf_find_vma(task, 0, write_vma, &data, 0); > + return 0; > +} > diff --git a/tools/testing/selftests/bpf/progs/find_vma_fail2.c b/tools/testing/selftests/bpf/progs/find_vma_fail2.c > new file mode 100644 > index 0000000000000..079c4594c095d > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/find_vma_fail2.c > @@ -0,0 +1,30 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* Copyright (c) 2021 Facebook */ > +#include "vmlinux.h" > +#include <bpf/bpf_helpers.h> > + > +char _license[] SEC("license") = "GPL"; > + > +struct callback_ctx { > + int dummy; > +}; > + > +static __u64 > +write_task(struct task_struct *task, struct vm_area_struct *vma, > + struct callback_ctx *data) > +{ > + /* writing to task, which is illegal */ > + task->mm = NULL; > + > + return 0; > +} > + > +SEC("kprobe/__x64_sys_getpgid") > +int handle_getpid(void) > +{ > + struct task_struct *task = bpf_get_current_task_btf(); > + struct callback_ctx data = {0}; > + > + bpf_find_vma(task, 0, write_task, &data, 0); > + return 0; > +} > -- > 2.30.2 >