Skip to content

Commit b54fe0a

Browse files
committed
selftests/bpf: Add tests for split task_vma iterator
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 <puranjay@kernel.org>
1 parent cd41207 commit b54fe0a

File tree

4 files changed

+122
-0
lines changed

4 files changed

+122
-0
lines changed

tools/testing/selftests/bpf/bpf_experimental.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ extern int bpf_iter_task_vma_new(struct bpf_iter_task_vma *it,
165165
struct task_struct *task,
166166
__u64 addr) __ksym;
167167
extern struct vm_area_struct *bpf_iter_task_vma_next(struct bpf_iter_task_vma *it) __ksym;
168+
extern void bpf_iter_task_vma_release(struct bpf_iter_task_vma *it) __ksym;
168169
extern void bpf_iter_task_vma_destroy(struct bpf_iter_task_vma *it) __ksym;
169170

170171
/* Convenience macro to wrap over bpf_obj_drop_impl */

tools/testing/selftests/bpf/prog_tests/iters.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "iters_css_task.skel.h"
2222
#include "iters_css.skel.h"
2323
#include "iters_task_failure.skel.h"
24+
#include "iters_task_vma_nosleep.skel.h"
2425

2526
static void subtest_num_iters(void)
2627
{
@@ -152,6 +153,17 @@ static void subtest_task_vma_iters(void)
152153
if (!ASSERT_EQ(skel->bss->vmas_seen, seen, "vmas_seen_eq"))
153154
goto cleanup;
154155

156+
/* Test release+sleepable: trigger the release_and_copy program */
157+
skel->bss->release_vmas_seen = 0;
158+
err = iters_task_vma__attach(skel);
159+
if (!ASSERT_OK(err, "skel_reattach"))
160+
goto cleanup;
161+
162+
getpgid(skel->bss->target_pid);
163+
iters_task_vma__detach(skel);
164+
165+
ASSERT_GT(skel->bss->release_vmas_seen, 0, "release_vmas_seen_gt_zero");
166+
155167
cleanup:
156168
if (f)
157169
fclose(f);
@@ -322,4 +334,5 @@ void test_iters(void)
322334
if (test__start_subtest("css"))
323335
subtest_css_iters();
324336
RUN_TESTS(iters_task_failure);
337+
RUN_TESTS(iters_task_vma_nosleep);
325338
}

tools/testing/selftests/bpf/progs/iters_task_vma.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,45 @@ int iter_task_vma_for_each(const void *ctx)
4040
return 0;
4141
}
4242

43+
unsigned int release_vmas_seen = 0;
44+
__u64 release_vm_starts[1000];
45+
46+
SEC("fentry.s/" SYS_PREFIX "sys_getpgid")
47+
int iter_task_vma_release_and_copy(const void *ctx)
48+
{
49+
struct task_struct *task = bpf_get_current_task_btf();
50+
struct vm_area_struct *vma;
51+
unsigned int seen = 0;
52+
53+
if (task->pid != target_pid)
54+
return 0;
55+
56+
if (release_vmas_seen)
57+
return 0;
58+
59+
bpf_for_each(task_vma, vma, task, 0) {
60+
__u64 start;
61+
char buf[8];
62+
63+
if (bpf_cmp_unlikely(seen, >=, 1000))
64+
break;
65+
66+
/* Phase 1: mmap_lock held, read VMA data */
67+
start = vma->vm_start;
68+
69+
/* Transition: release mmap_lock */
70+
bpf_iter_task_vma_release(&___it);
71+
/* VMA pointer is now invalid; sleepable helpers allowed */
72+
73+
/* Phase 2: mmap_lock released, sleepable call */
74+
bpf_copy_from_user(&buf, sizeof(buf), (void *)start);
75+
76+
release_vm_starts[seen] = start;
77+
seen++;
78+
}
79+
80+
release_vmas_seen = seen;
81+
return 0;
82+
}
83+
4384
char _license[] SEC("license") = "GPL";
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3+
4+
#include "vmlinux.h"
5+
#include "bpf_experimental.h"
6+
#include <bpf/bpf_helpers.h>
7+
#include "bpf_misc.h"
8+
9+
char _license[] SEC("license") = "GPL";
10+
11+
/* Negative test: sleepable call without release should be rejected */
12+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
13+
__failure __msg("sleepable helper bpf_copy_from_user#148 in nosleep region")
14+
int nosleep_iter_sleep_without_release(const void *ctx)
15+
{
16+
struct task_struct *task = bpf_get_current_task_btf();
17+
struct vm_area_struct *vma;
18+
19+
bpf_for_each(task_vma, vma, task, 0) {
20+
char buf[8];
21+
22+
/* Attempt to call sleepable helper without releasing mmap_lock.
23+
* Verifier should reject this.
24+
*/
25+
bpf_copy_from_user(&buf, sizeof(buf), (void *)vma->vm_start);
26+
break;
27+
}
28+
return 0;
29+
}
30+
31+
/* Negative test: VMA access after release should be rejected */
32+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
33+
__failure __msg("invalid mem access 'scalar'")
34+
int nosleep_iter_vma_access_after_release(const void *ctx)
35+
{
36+
struct task_struct *task = bpf_get_current_task_btf();
37+
struct vm_area_struct *vma;
38+
__u64 val = 0;
39+
40+
bpf_for_each(task_vma, vma, task, 0) {
41+
bpf_iter_task_vma_release(&___it);
42+
/* VMA pointer is now invalid. Accessing it should be rejected. */
43+
val = vma->vm_start;
44+
break;
45+
}
46+
__sink(val);
47+
return 0;
48+
}
49+
50+
/* Positive test: release then sleepable call should succeed */
51+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
52+
__success
53+
int nosleep_iter_release_then_sleep(const void *ctx)
54+
{
55+
struct task_struct *task = bpf_get_current_task_btf();
56+
struct vm_area_struct *vma;
57+
58+
bpf_for_each(task_vma, vma, task, 0) {
59+
__u64 start = vma->vm_start;
60+
char buf[8];
61+
62+
bpf_iter_task_vma_release(&___it);
63+
bpf_copy_from_user(&buf, sizeof(buf), (void *)start);
64+
break;
65+
}
66+
return 0;
67+
}

0 commit comments

Comments
 (0)