Add verifier coverage for struct pointers returned by open-coded iterator next kfuncs. Verify that the pointer is trusted for kfunc arguments while the iterator element is current, then rejected after either next or destroy. Cover both non-NULL and NULL next results. Exercise a trusted vm_file field as well as the direct VMA pointer. Verify that a FILE dynptr derived from vm_file must be discarded before iterator next or destroy, and that advancing after an explicit discard remains accepted. Keep a MEM_RCU field reachable through the snapshot's vm_mm field valid across iterator transitions because its lifetime is independent of the iterator. Also cover the existing RCU iterator behavior to make sure it retains its current lifetime and trust semantics. Assisted-by: LLM Signed-off-by: Xu Yunxiang --- .../selftests/bpf/progs/iters_testmod.c | 332 ++++++++++++++++++ 1 file changed, 332 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/iters_testmod.c b/tools/testing/selftests/bpf/progs/iters_testmod.c index 76012dbbdb413..a55e5cf29474a 100644 --- a/tools/testing/selftests/bpf/progs/iters_testmod.c +++ b/tools/testing/selftests/bpf/progs/iters_testmod.c @@ -28,6 +28,298 @@ int iter_next_trusted(const void *ctx) return 0; } +SEC("raw_tp/sys_enter") +__failure __msg("invalid mem access 'scalar'") +int iter_next_trusted_after_destroy(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) + goto out; + + bpf_iter_task_vma_destroy(&vma_it); + return vma_ptr->vm_start; +out: + bpf_iter_task_vma_destroy(&vma_it); + return 0; +} + +SEC("raw_tp/sys_enter") +__failure __msg("invalid mem access 'scalar'") +int iter_next_trusted_after_next(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, *next_vma_ptr; + u64 vm_start; + + bpf_iter_task_vma_new(&vma_it, cur_task, 0); + + vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (!vma_ptr) + goto out; + + next_vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (!next_vma_ptr) + goto out; + + vm_start = vma_ptr->vm_start; + bpf_iter_task_vma_destroy(&vma_it); + return vm_start; +out: + bpf_iter_task_vma_destroy(&vma_it); + return 0; +} + +SEC("raw_tp/sys_enter") +__failure __msg("invalid mem access 'scalar'") +int iter_next_trusted_after_next_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, *next_vma_ptr; + u64 vm_start; + + bpf_iter_task_vma_new(&vma_it, cur_task, 0); + + vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (!vma_ptr) + goto out; + + next_vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (next_vma_ptr) + goto out; + + vm_start = vma_ptr->vm_start; + bpf_iter_task_vma_destroy(&vma_it); + return vm_start; +out: + bpf_iter_task_vma_destroy(&vma_it); + return 0; +} + +SEC("raw_tp/sys_enter") +__failure __msg("invalid mem access 'scalar'") +int iter_next_trusted_field_after_destroy(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; + struct file *file_ptr; + + bpf_iter_task_vma_new(&vma_it, cur_task, 0); + + vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (!vma_ptr) + goto out; + + file_ptr = vma_ptr->vm_file; + bpf_iter_task_vma_destroy(&vma_it); + if (file_ptr) + return file_ptr->f_mode; + return 0; +out: + bpf_iter_task_vma_destroy(&vma_it); + return 0; +} + +SEC("raw_tp/sys_enter") +__failure __msg("invalid mem access 'scalar'") +int iter_next_trusted_field_after_next(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, *next_vma_ptr; + struct file *file_ptr; + u32 mode; + + bpf_iter_task_vma_new(&vma_it, cur_task, 0); + + vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (!vma_ptr) + goto out; + + file_ptr = vma_ptr->vm_file; + if (!file_ptr) + goto out; + + next_vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (!next_vma_ptr) + goto out; + + mode = file_ptr->f_mode; + bpf_iter_task_vma_destroy(&vma_it); + return mode; +out: + bpf_iter_task_vma_destroy(&vma_it); + return 0; +} + +SEC("raw_tp/sys_enter") +__failure __msg("Leaking reference id=") +int iter_next_file_dynptr_after_next(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; + struct bpf_dynptr dynptr; + struct file *file_ptr; + + bpf_iter_task_vma_new(&vma_it, cur_task, 0); + + vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (!vma_ptr) + goto out; + + file_ptr = vma_ptr->vm_file; + if (!file_ptr) + goto out; + + bpf_dynptr_from_file(file_ptr, 0, &dynptr); + bpf_iter_task_vma_next(&vma_it); + bpf_dynptr_file_discard(&dynptr); +out: + bpf_iter_task_vma_destroy(&vma_it); + return 0; +} + +SEC("raw_tp/sys_enter") +__failure __msg("Leaking reference id=") +int iter_next_file_dynptr_after_destroy(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; + struct bpf_dynptr dynptr; + struct file *file_ptr; + + bpf_iter_task_vma_new(&vma_it, cur_task, 0); + + vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (!vma_ptr) + goto out; + + file_ptr = vma_ptr->vm_file; + if (!file_ptr) + goto out; + + bpf_dynptr_from_file(file_ptr, 0, &dynptr); + bpf_iter_task_vma_destroy(&vma_it); + bpf_dynptr_file_discard(&dynptr); + return 0; +out: + bpf_iter_task_vma_destroy(&vma_it); + return 0; +} + +SEC("raw_tp/sys_enter") +__success +int iter_next_file_dynptr_discard_before_advance(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; + struct bpf_dynptr dynptr; + struct file *file_ptr; + + bpf_iter_task_vma_new(&vma_it, cur_task, 0); + + vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (!vma_ptr) + goto out; + + file_ptr = vma_ptr->vm_file; + if (!file_ptr) + goto out; + + bpf_dynptr_from_file(file_ptr, 0, &dynptr); + bpf_dynptr_file_discard(&dynptr); + bpf_iter_task_vma_next(&vma_it); +out: + bpf_iter_task_vma_destroy(&vma_it); + return 0; +} + +SEC("raw_tp/sys_enter") +__success +int iter_next_trusted_current_after_next(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) + goto out; + + vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (vma_ptr) + bpf_kfunc_trusted_vma_test(vma_ptr); +out: + bpf_iter_task_vma_destroy(&vma_it); + return 0; +} + +SEC("raw_tp/sys_enter") +__success +int iter_next_trusted_rcu_field_after_destroy(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; + struct mm_struct *mm_ptr; + struct file *file_ptr = NULL; + + bpf_iter_task_vma_new(&vma_it, cur_task, 0); + + vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (vma_ptr) { + mm_ptr = vma_ptr->vm_mm; + /* exe_file has RCU protection independent of the iterator. */ + if (mm_ptr) + file_ptr = mm_ptr->exe_file; + } + + bpf_iter_task_vma_destroy(&vma_it); + if (file_ptr) + return file_ptr->f_mode; + return 0; +} + +SEC("raw_tp/sys_enter") +__success +int iter_next_trusted_rcu_field_after_next(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; + struct mm_struct *mm_ptr; + struct file *file_ptr = NULL; + u32 mode = 0; + + bpf_iter_task_vma_new(&vma_it, cur_task, 0); + + vma_ptr = bpf_iter_task_vma_next(&vma_it); + if (vma_ptr) { + mm_ptr = vma_ptr->vm_mm; + if (mm_ptr) + file_ptr = mm_ptr->exe_file; + } + + bpf_iter_task_vma_next(&vma_it); + if (file_ptr) + mode = file_ptr->f_mode; + bpf_iter_task_vma_destroy(&vma_it); + return mode; +} + SEC("raw_tp/sys_enter") __failure __msg("Possibly NULL pointer passed to trusted R1") int iter_next_trusted_or_null(const void *ctx) @@ -66,6 +358,46 @@ int iter_next_rcu(const void *ctx) return 0; } +SEC("raw_tp/sys_enter") +__success +int iter_next_rcu_after_destroy(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_iter_task_destroy(&task_it); + if (!task_ptr) + return 0; + + bpf_kfunc_rcu_task_test(task_ptr); + return 0; +} + +SEC("raw_tp/sys_enter") +__success +int iter_next_rcu_after_next(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) + goto out; + + bpf_iter_task_next(&task_it); + 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 R1") int iter_next_rcu_or_null(const void *ctx) -- 2.43.0