selftests/bpf: Add tests for iter next method returning valid pointer
authorJuntong Deng <juntong.deng@outlook.com>
Thu, 29 Aug 2024 20:13:15 +0000 (21:13 +0100)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 30 Aug 2024 01:52:16 +0000 (18:52 -0700)
This patch adds test cases for iter next method returning valid
pointer, which can also used as usage examples.

Currently iter next method should return valid pointer.

iter_next_trusted is the correct usage and test if iter next method
return valid pointer. bpf_iter_task_vma_next has KF_RET_NULL flag,
so the returned pointer may be NULL. We need to check if the pointer
is NULL before using it.

iter_next_trusted_or_null is the incorrect usage. There is no checking
before using the pointer, so it will be rejected by the verifier.

iter_next_rcu and iter_next_rcu_or_null are similar test cases for
KF_RCU_PROTECTED iterators.

iter_next_rcu_not_trusted is used to test that the pointer returned by
iter next method of KF_RCU_PROTECTED iterator cannot be passed in
KF_TRUSTED_ARGS kfuncs.

iter_next_ptr_mem_not_trusted is used to test that base type
PTR_TO_MEM should not be combined with type flag PTR_TRUSTED.

Signed-off-by: Juntong Deng <juntong.deng@outlook.com>
Link: https://lore.kernel.org/r/AM6PR03MB5848709758F6922F02AF9F1F99962@AM6PR03MB5848.eurprd03.prod.outlook.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod_kfunc.h
tools/testing/selftests/bpf/prog_tests/iters.c
tools/testing/selftests/bpf/progs/iters_testmod.c [new file with mode: 0644]

index 42c38db..c73d04b 100644 (file)
@@ -198,6 +198,22 @@ __bpf_kfunc void bpf_kfunc_nested_release_test(struct sk_buff *ptr)
 {
 }
 
+__bpf_kfunc void bpf_kfunc_trusted_vma_test(struct vm_area_struct *ptr)
+{
+}
+
+__bpf_kfunc void bpf_kfunc_trusted_task_test(struct task_struct *ptr)
+{
+}
+
+__bpf_kfunc void bpf_kfunc_trusted_num_test(int *ptr)
+{
+}
+
+__bpf_kfunc void bpf_kfunc_rcu_task_test(struct task_struct *ptr)
+{
+}
+
 __bpf_kfunc struct bpf_testmod_ctx *
 bpf_testmod_ctx_create(int *err)
 {
@@ -559,6 +575,10 @@ BTF_ID_FLAGS(func, bpf_kfunc_dynptr_test)
 BTF_ID_FLAGS(func, bpf_kfunc_nested_acquire_nonzero_offset_test, KF_ACQUIRE)
 BTF_ID_FLAGS(func, bpf_kfunc_nested_acquire_zero_offset_test, KF_ACQUIRE)
 BTF_ID_FLAGS(func, bpf_kfunc_nested_release_test, KF_RELEASE)
+BTF_ID_FLAGS(func, bpf_kfunc_trusted_vma_test, KF_TRUSTED_ARGS)
+BTF_ID_FLAGS(func, bpf_kfunc_trusted_task_test, KF_TRUSTED_ARGS)
+BTF_ID_FLAGS(func, bpf_kfunc_trusted_num_test, KF_TRUSTED_ARGS)
+BTF_ID_FLAGS(func, bpf_kfunc_rcu_task_test, KF_RCU)
 BTF_ID_FLAGS(func, bpf_testmod_ctx_create, KF_ACQUIRE | KF_RET_NULL)
 BTF_ID_FLAGS(func, bpf_testmod_ctx_release, KF_RELEASE)
 BTF_KFUNCS_END(bpf_testmod_common_kfunc_ids)
index 7e76532..b588179 100644 (file)
@@ -154,4 +154,9 @@ int bpf_kfunc_st_ops_test_epilogue(struct st_ops_args *args) __ksym;
 int bpf_kfunc_st_ops_test_pro_epilogue(struct st_ops_args *args) __ksym;
 int bpf_kfunc_st_ops_inc10(struct st_ops_args *args) __ksym;
 
+void bpf_kfunc_trusted_vma_test(struct vm_area_struct *ptr) __ksym;
+void bpf_kfunc_trusted_task_test(struct task_struct *ptr) __ksym;
+void bpf_kfunc_trusted_num_test(int *ptr) __ksym;
+void bpf_kfunc_rcu_task_test(struct task_struct *ptr) __ksym;
+
 #endif /* _BPF_TESTMOD_KFUNC_H */
index 3c44037..89ff23c 100644 (file)
@@ -14,6 +14,7 @@
 #include "iters_state_safety.skel.h"
 #include "iters_looping.skel.h"
 #include "iters_num.skel.h"
+#include "iters_testmod.skel.h"
 #include "iters_testmod_seq.skel.h"
 #include "iters_task_vma.skel.h"
 #include "iters_task.skel.h"
@@ -297,8 +298,10 @@ void test_iters(void)
        RUN_TESTS(iters);
        RUN_TESTS(iters_css_task);
 
-       if (env.has_testmod)
+       if (env.has_testmod) {
+               RUN_TESTS(iters_testmod);
                RUN_TESTS(iters_testmod_seq);
+       }
 
        if (test__start_subtest("num"))
                subtest_num_iters();
diff --git a/tools/testing/selftests/bpf/progs/iters_testmod.c b/tools/testing/selftests/bpf/progs/iters_testmod.c
new file mode 100644 (file)
index 0000000..df1d3db
--- /dev/null
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include "bpf_experimental.h"
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+#include "../bpf_testmod/bpf_testmod_kfunc.h"
+
+char _license[] SEC("license") = "GPL";
+
+SEC("raw_tp/sys_enter")
+__success
+int iter_next_trusted(const void *ctx)
+{
+       struct task_struct *cur_task = bpf_get_current_task_btf();
+       struct bpf_iter_task_vma vma_it;
+       struct vm_area_struct *vma_ptr;
+
+       bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+       vma_ptr = bpf_iter_task_vma_next(&vma_it);
+       if (vma_ptr == NULL)
+               goto out;
+
+       bpf_kfunc_trusted_vma_test(vma_ptr);
+out:
+       bpf_iter_task_vma_destroy(&vma_it);
+       return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__failure __msg("Possibly NULL pointer passed to trusted arg0")
+int iter_next_trusted_or_null(const void *ctx)
+{
+       struct task_struct *cur_task = bpf_get_current_task_btf();
+       struct bpf_iter_task_vma vma_it;
+       struct vm_area_struct *vma_ptr;
+
+       bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+       vma_ptr = bpf_iter_task_vma_next(&vma_it);
+
+       bpf_kfunc_trusted_vma_test(vma_ptr);
+
+       bpf_iter_task_vma_destroy(&vma_it);
+       return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__success
+int iter_next_rcu(const void *ctx)
+{
+       struct task_struct *cur_task = bpf_get_current_task_btf();
+       struct bpf_iter_task task_it;
+       struct task_struct *task_ptr;
+
+       bpf_iter_task_new(&task_it, cur_task, 0);
+
+       task_ptr = bpf_iter_task_next(&task_it);
+       if (task_ptr == NULL)
+               goto out;
+
+       bpf_kfunc_rcu_task_test(task_ptr);
+out:
+       bpf_iter_task_destroy(&task_it);
+       return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__failure __msg("Possibly NULL pointer passed to trusted arg0")
+int iter_next_rcu_or_null(const void *ctx)
+{
+       struct task_struct *cur_task = bpf_get_current_task_btf();
+       struct bpf_iter_task task_it;
+       struct task_struct *task_ptr;
+
+       bpf_iter_task_new(&task_it, cur_task, 0);
+
+       task_ptr = bpf_iter_task_next(&task_it);
+
+       bpf_kfunc_rcu_task_test(task_ptr);
+
+       bpf_iter_task_destroy(&task_it);
+       return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__failure __msg("R1 must be referenced or trusted")
+int iter_next_rcu_not_trusted(const void *ctx)
+{
+       struct task_struct *cur_task = bpf_get_current_task_btf();
+       struct bpf_iter_task task_it;
+       struct task_struct *task_ptr;
+
+       bpf_iter_task_new(&task_it, cur_task, 0);
+
+       task_ptr = bpf_iter_task_next(&task_it);
+       if (task_ptr == NULL)
+               goto out;
+
+       bpf_kfunc_trusted_task_test(task_ptr);
+out:
+       bpf_iter_task_destroy(&task_it);
+       return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__failure __msg("R1 cannot write into rdonly_mem")
+/* Message should not be 'R1 cannot write into rdonly_trusted_mem' */
+int iter_next_ptr_mem_not_trusted(const void *ctx)
+{
+       struct bpf_iter_num num_it;
+       int *num_ptr;
+
+       bpf_iter_num_new(&num_it, 0, 10);
+
+       num_ptr = bpf_iter_num_next(&num_it);
+       if (num_ptr == NULL)
+               goto out;
+
+       bpf_kfunc_trusted_num_test(num_ptr);
+out:
+       bpf_iter_num_destroy(&num_it);
+       return 0;
+}