> On Nov 4, 2021, at 10:07 AM, Yonghong Song <yhs@xxxxxx> wrote: > > > > On 11/4/21 12:00 AM, Song Liu 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> [...] >> +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(); >> + } >> + 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; > > Does this really work? Compiler could do > j += 1000000; I think compiler won't do it with volatile 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 */ > > Above comment is not needed. It should be clear from the code. > >> +} >> + >> +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 > > Let us 'long' instead of '__u64' to match uapi bpf.h. > >> +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") > > The test will fail for non x86_64 architecture. > I had some tweaks in test_probe_user.c. Please take a look. > We can refactor to make tweaks in test_probe_user.c reusable > by other files. Good point. I will look into this. > >> +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; > > This is tricky. How do we guarantee task->pid == target_pid hit? > This probably mostly okay in serial running mode. But it may > become more challenging if test_progs is running in parallel mode? This is on a per task perf_event, so it shouldn't hit other tasks. > >> + >> + 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 > > __u64 => long > >> +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 > > __u64 => long > >> +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; >> +}