From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 023EA3382FB for ; Mon, 23 Feb 2026 17:48:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771868938; cv=none; b=lpuUqbdaaAmIQ2/tTMVDYl8akDprdEUS2hRxrg/++Tjz9LEaLBN3oITnRTOiHlCBSNkC4HU+Ny/xVTETG+fFdz/G7ebX0p7yFGgRExlOOAtRcVcbBdoDgjfNazyK41005hgqbZr3iGt/DzebM7eLjLDKYKfoygv4zgoe+rvlgPA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771868938; c=relaxed/simple; bh=zBLSE6ZLneBI1AEN69pXwKxHBhMe6dPBn0zuMHQavik=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RJVg/40fVSuCgJpHVRWS5QFT+9Ug+pTlTJA/53vMRWPG+RLWvZ9JHXpULO+FwlgyV/jD1jBBsFJRiEjw2ZAMr927Urnze9nTEkYE0d3L9kjOb7fLRD8fO0fhYSUHwKfJdNNMjJ9+YaycIhsHNTFhW9UN9PSYUvjWH6kJW38Mrx8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Fb0CP6qQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Fb0CP6qQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 63680C116D0; Mon, 23 Feb 2026 17:48:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771868937; bh=zBLSE6ZLneBI1AEN69pXwKxHBhMe6dPBn0zuMHQavik=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Fb0CP6qQb1vAKxxtdEeyu9ZwxxFRaNJQ7AklB3aH5zOTNKP2zsN2CwdweWyPkdQkr 2mqPhBYRJGgjVs15SBhAZYIV2HwiE5vY0FzgCzmDFk7UdiXzivzZ+BdvXXycaGaHWc kZPIu4obN4UZHPkwXJLNdUpANmGtUbK3Zm1UzLfO5Z3iueiNuz7J/jt1GwqSpDAeoE MLzIGU6ew/dpaVef3q3oCAG+EK0A1pwG51FzvWnjSpe+WPnVNiJzkRgmonv+gDKEr0 DMLpckVbtzrbymKfFgZ0ZcacdsaJPNjfHzl3TGcX+Oel/B1va4R2l7wDMC/ZrcH1TO tacAbA3vrf8/A== From: Puranjay Mohan To: bpf@vger.kernel.org Cc: Puranjay Mohan , Puranjay Mohan , Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , Martin KaFai Lau , Eduard Zingerman , Kumar Kartikeya Dwivedi , Mykyta Yatsenko , kernel-team@meta.com Subject: [PATCH bpf-next v3 6/6] selftests/bpf: Add tests for split task_vma iterator Date: Mon, 23 Feb 2026 09:46:56 -0800 Message-ID: <20260223174659.2749964-7-puranjay@kernel.org> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260223174659.2749964-1-puranjay@kernel.org> References: <20260223174659.2749964-1-puranjay@kernel.org> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add runtime and verifier tests for the split task_vma iteration model. The runtime test iter_task_vma_release_and_copy iterates VMAs, saves vm_start, releases mmap_lock via bpf_iter_task_vma_release(), then calls bpf_copy_from_user() on the saved address. The verifier tests cover: - nosleep_iter_release_then_sleep: release then sleep is accepted - nosleep_iter_sleep_without_release: sleep without release is rejected with "in nosleep region" - nosleep_iter_vma_access_after_release: VMA access after release is rejected with "invalid mem access 'scalar'" Signed-off-by: Puranjay Mohan --- .../testing/selftests/bpf/bpf_experimental.h | 1 + .../testing/selftests/bpf/prog_tests/iters.c | 13 ++ .../selftests/bpf/progs/iters_task_vma.c | 39 ++++++ .../bpf/progs/iters_task_vma_nosleep.c | 125 ++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/iters_task_vma_nosleep.c diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index 9df77e59d4f5..531d6c6aab45 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -165,6 +165,7 @@ extern int bpf_iter_task_vma_new(struct bpf_iter_task_vma *it, struct task_struct *task, __u64 addr) __ksym; extern struct vm_area_struct *bpf_iter_task_vma_next(struct bpf_iter_task_vma *it) __ksym; +extern void bpf_iter_task_vma_release(struct bpf_iter_task_vma *it) __ksym; extern void bpf_iter_task_vma_destroy(struct bpf_iter_task_vma *it) __ksym; /* Convenience macro to wrap over bpf_obj_drop_impl */ diff --git a/tools/testing/selftests/bpf/prog_tests/iters.c b/tools/testing/selftests/bpf/prog_tests/iters.c index a539980a2fbe..977114f0e88f 100644 --- a/tools/testing/selftests/bpf/prog_tests/iters.c +++ b/tools/testing/selftests/bpf/prog_tests/iters.c @@ -21,6 +21,7 @@ #include "iters_css_task.skel.h" #include "iters_css.skel.h" #include "iters_task_failure.skel.h" +#include "iters_task_vma_nosleep.skel.h" static void subtest_num_iters(void) { @@ -152,6 +153,17 @@ static void subtest_task_vma_iters(void) if (!ASSERT_EQ(skel->bss->vmas_seen, seen, "vmas_seen_eq")) goto cleanup; + /* Test release+sleepable: trigger the release_and_copy program */ + skel->bss->release_vmas_seen = 0; + err = iters_task_vma__attach(skel); + if (!ASSERT_OK(err, "skel_reattach")) + goto cleanup; + + getpgid(skel->bss->target_pid); + iters_task_vma__detach(skel); + + ASSERT_GT(skel->bss->release_vmas_seen, 0, "release_vmas_seen_gt_zero"); + cleanup: if (f) fclose(f); @@ -322,4 +334,5 @@ void test_iters(void) if (test__start_subtest("css")) subtest_css_iters(); RUN_TESTS(iters_task_failure); + RUN_TESTS(iters_task_vma_nosleep); } diff --git a/tools/testing/selftests/bpf/progs/iters_task_vma.c b/tools/testing/selftests/bpf/progs/iters_task_vma.c index dc0c3691dcc2..59065f9da4f8 100644 --- a/tools/testing/selftests/bpf/progs/iters_task_vma.c +++ b/tools/testing/selftests/bpf/progs/iters_task_vma.c @@ -40,4 +40,43 @@ int iter_task_vma_for_each(const void *ctx) return 0; } +unsigned int release_vmas_seen = 0; + +SEC("fentry.s/" SYS_PREFIX "sys_getpgid") +int iter_task_vma_release_and_copy(const void *ctx) +{ + struct task_struct *task = bpf_get_current_task_btf(); + struct vm_area_struct *vma; + unsigned int seen = 0; + + if (task->pid != target_pid) + return 0; + + if (release_vmas_seen) + return 0; + + bpf_for_each(task_vma, vma, task, 0) { + __u64 start; + char buf[8]; + + if (bpf_cmp_unlikely(seen, >=, 1000)) + break; + + /* Phase 1: mmap_lock held, read VMA data */ + start = vma->vm_start; + + /* Transition: release mmap_lock */ + bpf_iter_task_vma_release(&___it); + /* VMA pointer is now invalid; sleepable helpers allowed */ + + /* Phase 2: mmap_lock released, sleepable call */ + bpf_copy_from_user(&buf, sizeof(buf), (void *)start); + + seen++; + } + + release_vmas_seen = seen; + return 0; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/iters_task_vma_nosleep.c b/tools/testing/selftests/bpf/progs/iters_task_vma_nosleep.c new file mode 100644 index 000000000000..ab607e29b36a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/iters_task_vma_nosleep.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include "bpf_experimental.h" +#include +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +/* Negative test: sleepable call without release should be rejected */ +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +__failure __msg("sleepable helper bpf_copy_from_user#148 in nosleep region") +int nosleep_iter_sleep_without_release(const void *ctx) +{ + struct task_struct *task = bpf_get_current_task_btf(); + struct vm_area_struct *vma; + + bpf_for_each(task_vma, vma, task, 0) { + char buf[8]; + + /* Attempt to call sleepable helper without releasing mmap_lock. + * Verifier should reject this. + */ + bpf_copy_from_user(&buf, sizeof(buf), (void *)vma->vm_start); + break; + } + return 0; +} + +/* Negative test: VMA access after release should be rejected */ +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +__failure __msg("invalid mem access 'scalar'") +int nosleep_iter_vma_access_after_release(const void *ctx) +{ + struct task_struct *task = bpf_get_current_task_btf(); + struct vm_area_struct *vma; + __u64 val = 0; + + bpf_for_each(task_vma, vma, task, 0) { + bpf_iter_task_vma_release(&___it); + /* VMA pointer is now invalid. Accessing it should be rejected. */ + val = vma->vm_start; + break; + } + __sink(val); + return 0; +} + +/* Positive test: release then sleepable call should succeed */ +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +__success +int nosleep_iter_release_then_sleep(const void *ctx) +{ + struct task_struct *task = bpf_get_current_task_btf(); + struct vm_area_struct *vma; + + bpf_for_each(task_vma, vma, task, 0) { + __u64 start = vma->vm_start; + char buf[8]; + + bpf_iter_task_vma_release(&___it); + bpf_copy_from_user(&buf, sizeof(buf), (void *)start); + break; + } + return 0; +} + +/* Negative test: double release should be rejected */ +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +__failure __msg("no acquired reference to release") +int nosleep_iter_double_release(const void *ctx) +{ + struct task_struct *task = bpf_get_current_task_btf(); + struct vm_area_struct *vma; + + bpf_for_each(task_vma, vma, task, 0) { + bpf_iter_task_vma_release(&___it); + bpf_iter_task_vma_release(&___it); + break; + } + return 0; +} + +/* Negative test: release without any prior _next acquire */ +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +__failure __msg("no acquired reference to release") +int nosleep_iter_release_without_acquire(const void *ctx) +{ + struct task_struct *task = bpf_get_current_task_btf(); + struct bpf_iter_task_vma it; + + bpf_iter_task_vma_new(&it, task, 0); + /* No _next called, so no acquired reference exists */ + bpf_iter_task_vma_release(&it); + bpf_iter_task_vma_destroy(&it); + return 0; +} + +/* Negative test: nested iterators, releasing inner should not allow sleeping + * because the outer iterator still holds a nosleep reference. + */ +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +__failure __msg("sleepable helper bpf_copy_from_user#148 in nosleep region") +int nosleep_iter_nested_release_inner(const void *ctx) +{ + struct task_struct *task = bpf_get_current_task_btf(); + struct vm_area_struct *vma_outer, *vma_inner; + + bpf_for_each(task_vma, vma_outer, task, 0) { + bpf_for_each(task_vma, vma_inner, task, 0) { + __u64 start = vma_inner->vm_start; + char buf[8]; + + bpf_iter_task_vma_release(&___it); + /* Inner released, but outer still holds nosleep ref. + * Sleeping should still be forbidden. + */ + bpf_copy_from_user(&buf, sizeof(buf), (void *)start); + break; + } + break; + } + return 0; +} -- 2.47.3