Skip to content

Commit 5b1b5d3

Browse files
kkdwvdAlexei Starovoitov
authored andcommitted
selftests/bpf: Add tests for async cb context
Add tests to verify that async callback's sleepable attribute is correctly determined by the callback type, not the arming program's context, reflecting its true execution context. Introduce verifier_async_cb_context.c with tests for all three async callback primitives: bpf_timer, bpf_wq, and bpf_task_work. Each primitive is tested when armed from both sleepable (lsm.s/file_open) and non-sleepable (fentry) programs. Test coverage: - bpf_timer callbacks: Verify they are never sleepable, even when armed from sleepable programs. Both tests should fail when attempting to use sleepable helper bpf_copy_from_user() in the callback. - bpf_wq callbacks: Verify they are always sleepable, even when armed from non-sleepable programs. Both tests should succeed when using sleepable helpers in the callback. - bpf_task_work callbacks: Verify they are always sleepable, even when armed from non-sleepable programs. Both tests should succeed when using sleepable helpers in the callback. Acked-by: Eduard Zingerman <[email protected]> Signed-off-by: Kumar Kartikeya Dwivedi <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent f233d48 commit 5b1b5d3

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "verifier_arena.skel.h"
88
#include "verifier_arena_large.skel.h"
99
#include "verifier_array_access.skel.h"
10+
#include "verifier_async_cb_context.skel.h"
1011
#include "verifier_basic_stack.skel.h"
1112
#include "verifier_bitfield_write.skel.h"
1213
#include "verifier_bounds.skel.h"
@@ -280,6 +281,7 @@ void test_verifier_array_access(void)
280281
verifier_array_access__elf_bytes,
281282
init_array_access_maps);
282283
}
284+
void test_verifier_async_cb_context(void) { RUN(verifier_async_cb_context); }
283285

284286
static int init_value_ptr_arith_maps(struct bpf_object *obj)
285287
{
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3+
4+
#include <vmlinux.h>
5+
#include <bpf/bpf_helpers.h>
6+
#include <bpf/bpf_tracing.h>
7+
#include "bpf_misc.h"
8+
#include "bpf_experimental.h"
9+
10+
char _license[] SEC("license") = "GPL";
11+
12+
/* Timer tests */
13+
14+
struct timer_elem {
15+
struct bpf_timer t;
16+
};
17+
18+
struct {
19+
__uint(type, BPF_MAP_TYPE_ARRAY);
20+
__uint(max_entries, 1);
21+
__type(key, int);
22+
__type(value, struct timer_elem);
23+
} timer_map SEC(".maps");
24+
25+
static int timer_cb(void *map, int *key, struct bpf_timer *timer)
26+
{
27+
u32 data;
28+
/* Timer callbacks are never sleepable, even from non-sleepable programs */
29+
bpf_copy_from_user(&data, sizeof(data), NULL);
30+
return 0;
31+
}
32+
33+
SEC("fentry/bpf_fentry_test1")
34+
__failure __msg("helper call might sleep in a non-sleepable prog")
35+
int timer_non_sleepable_prog(void *ctx)
36+
{
37+
struct timer_elem *val;
38+
int key = 0;
39+
40+
val = bpf_map_lookup_elem(&timer_map, &key);
41+
if (!val)
42+
return 0;
43+
44+
bpf_timer_init(&val->t, &timer_map, 0);
45+
bpf_timer_set_callback(&val->t, timer_cb);
46+
return 0;
47+
}
48+
49+
SEC("lsm.s/file_open")
50+
__failure __msg("helper call might sleep in a non-sleepable prog")
51+
int timer_sleepable_prog(void *ctx)
52+
{
53+
struct timer_elem *val;
54+
int key = 0;
55+
56+
val = bpf_map_lookup_elem(&timer_map, &key);
57+
if (!val)
58+
return 0;
59+
60+
bpf_timer_init(&val->t, &timer_map, 0);
61+
bpf_timer_set_callback(&val->t, timer_cb);
62+
return 0;
63+
}
64+
65+
/* Workqueue tests */
66+
67+
struct wq_elem {
68+
struct bpf_wq w;
69+
};
70+
71+
struct {
72+
__uint(type, BPF_MAP_TYPE_ARRAY);
73+
__uint(max_entries, 1);
74+
__type(key, int);
75+
__type(value, struct wq_elem);
76+
} wq_map SEC(".maps");
77+
78+
static int wq_cb(void *map, int *key, void *value)
79+
{
80+
u32 data;
81+
/* Workqueue callbacks are always sleepable, even from non-sleepable programs */
82+
bpf_copy_from_user(&data, sizeof(data), NULL);
83+
return 0;
84+
}
85+
86+
SEC("fentry/bpf_fentry_test1")
87+
__success
88+
int wq_non_sleepable_prog(void *ctx)
89+
{
90+
struct wq_elem *val;
91+
int key = 0;
92+
93+
val = bpf_map_lookup_elem(&wq_map, &key);
94+
if (!val)
95+
return 0;
96+
97+
if (bpf_wq_init(&val->w, &wq_map, 0) != 0)
98+
return 0;
99+
if (bpf_wq_set_callback_impl(&val->w, wq_cb, 0, NULL) != 0)
100+
return 0;
101+
return 0;
102+
}
103+
104+
SEC("lsm.s/file_open")
105+
__success
106+
int wq_sleepable_prog(void *ctx)
107+
{
108+
struct wq_elem *val;
109+
int key = 0;
110+
111+
val = bpf_map_lookup_elem(&wq_map, &key);
112+
if (!val)
113+
return 0;
114+
115+
if (bpf_wq_init(&val->w, &wq_map, 0) != 0)
116+
return 0;
117+
if (bpf_wq_set_callback_impl(&val->w, wq_cb, 0, NULL) != 0)
118+
return 0;
119+
return 0;
120+
}
121+
122+
/* Task work tests */
123+
124+
struct task_work_elem {
125+
struct bpf_task_work tw;
126+
};
127+
128+
struct {
129+
__uint(type, BPF_MAP_TYPE_ARRAY);
130+
__uint(max_entries, 1);
131+
__type(key, int);
132+
__type(value, struct task_work_elem);
133+
} task_work_map SEC(".maps");
134+
135+
static int task_work_cb(struct bpf_map *map, void *key, void *value)
136+
{
137+
u32 data;
138+
/* Task work callbacks are always sleepable, even from non-sleepable programs */
139+
bpf_copy_from_user(&data, sizeof(data), NULL);
140+
return 0;
141+
}
142+
143+
SEC("fentry/bpf_fentry_test1")
144+
__success
145+
int task_work_non_sleepable_prog(void *ctx)
146+
{
147+
struct task_work_elem *val;
148+
struct task_struct *task;
149+
int key = 0;
150+
151+
val = bpf_map_lookup_elem(&task_work_map, &key);
152+
if (!val)
153+
return 0;
154+
155+
task = bpf_get_current_task_btf();
156+
if (!task)
157+
return 0;
158+
159+
bpf_task_work_schedule_resume(task, &val->tw, &task_work_map, task_work_cb, NULL);
160+
return 0;
161+
}
162+
163+
SEC("lsm.s/file_open")
164+
__success
165+
int task_work_sleepable_prog(void *ctx)
166+
{
167+
struct task_work_elem *val;
168+
struct task_struct *task;
169+
int key = 0;
170+
171+
val = bpf_map_lookup_elem(&task_work_map, &key);
172+
if (!val)
173+
return 0;
174+
175+
task = bpf_get_current_task_btf();
176+
if (!task)
177+
return 0;
178+
179+
bpf_task_work_schedule_resume(task, &val->tw, &task_work_map, task_work_cb, NULL);
180+
return 0;
181+
}

0 commit comments

Comments
 (0)