Skip to content

Commit 594bbd2

Browse files
ameryhungKernel Patches Daemon
authored andcommitted
selftests/bpf: Test getting associated struct_ops in timer callback
Make sure 1) a timer callback can also reference the associated struct_ops, and then make sure 2) the timer callback cannot get a dangled pointer to the struct_ops when the map is freed. The test schedules a timer callback from a struct_ops program since struct_ops programs do not pin the map. It is possible for the timer callback to run after the map is freed. The timer callback calls a kfunc that runs .test_1() of the associated struct_ops, which should return MAP_MAGIC when the map is still alive or -1 when the map is gone. The first subtest added in this patch schedules the timer callback to run immediately, while the map is still alive. The second subtest added schedules the callback to run 500ms after syscall_prog runs and then frees the map right after syscall_prog runs. Both subtests then wait until the callback runs to check the return of the kfunc. Signed-off-by: Amery Hung <[email protected]>
1 parent 025ead2 commit 594bbd2

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed

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

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <test_progs.h>
44
#include "struct_ops_assoc.skel.h"
55
#include "struct_ops_assoc_reuse.skel.h"
6+
#include "struct_ops_assoc_in_timer.skel.h"
67

78
static void test_st_ops_assoc(void)
89
{
@@ -101,10 +102,93 @@ static void test_st_ops_assoc_reuse(void)
101102
struct_ops_assoc_reuse__destroy(skel);
102103
}
103104

105+
static void test_st_ops_assoc_in_timer(void)
106+
{
107+
struct struct_ops_assoc_in_timer *skel = NULL;
108+
int err;
109+
110+
skel = struct_ops_assoc_in_timer__open_and_load();
111+
if (!ASSERT_OK_PTR(skel, "struct_ops_assoc_reuse__open"))
112+
goto out;
113+
114+
err = bpf_program__assoc_struct_ops(skel->progs.syscall_prog,
115+
skel->maps.st_ops_map, NULL);
116+
ASSERT_OK(err, "bpf_program__assoc_struct_ops");
117+
118+
err = struct_ops_assoc_in_timer__attach(skel);
119+
if (!ASSERT_OK(err, "struct_ops_assoc__attach"))
120+
goto out;
121+
122+
/*
123+
* Run .test_1 by calling kfunc bpf_kfunc_multi_st_ops_test_1_prog_arg() and checks
124+
* the return value. .test_1 will also schedule timer_cb that runs .test_1 again
125+
* immediately.
126+
*/
127+
err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog), NULL);
128+
ASSERT_OK(err, "bpf_prog_test_run_opts");
129+
130+
/* Check the return of the kfunc after timer_cb runs */
131+
while (!READ_ONCE(skel->bss->timer_cb_run))
132+
sched_yield();
133+
ASSERT_EQ(skel->bss->timer_test_1_ret, 1234, "skel->bss->timer_test_1_ret");
134+
ASSERT_EQ(skel->bss->test_err, 0, "skel->bss->test_err_a");
135+
out:
136+
struct_ops_assoc_in_timer__destroy(skel);
137+
}
138+
139+
static void test_st_ops_assoc_in_timer_no_uref(void)
140+
{
141+
struct struct_ops_assoc_in_timer *skel = NULL;
142+
struct bpf_link *link;
143+
int err;
144+
145+
skel = struct_ops_assoc_in_timer__open_and_load();
146+
if (!ASSERT_OK_PTR(skel, "struct_ops_assoc_reuse__open"))
147+
goto out;
148+
149+
err = bpf_program__assoc_struct_ops(skel->progs.syscall_prog,
150+
skel->maps.st_ops_map, NULL);
151+
ASSERT_OK(err, "bpf_program__assoc_struct_ops");
152+
153+
link = bpf_map__attach_struct_ops(skel->maps.st_ops_map);
154+
if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops"))
155+
goto out;
156+
157+
/*
158+
* Run .test_1 by calling kfunc bpf_kfunc_multi_st_ops_test_1_prog_arg() and checks
159+
* the return value. .test_1 will also schedule timer_cb that runs .test_1 again.
160+
* timer_cb will run 500ms after syscall_prog runs, when the user space no longer
161+
* holds a reference to st_ops_map.
162+
*/
163+
skel->bss->timer_ns = 500000000;
164+
err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog), NULL);
165+
ASSERT_OK(err, "bpf_prog_test_run_opts");
166+
167+
/*
168+
* Detach and close struct_ops map. timer_cb holding a reference to the map should
169+
* prevent it from being freed.
170+
*/
171+
bpf_link__destroy(link);
172+
close(bpf_program__fd(skel->progs.syscall_prog));
173+
close(bpf_map__fd(skel->maps.st_ops_map));
174+
175+
/* Check the return of the kfunc after timer_cb runs */
176+
while (!READ_ONCE(skel->bss->timer_cb_run))
177+
sched_yield();
178+
ASSERT_EQ(skel->bss->timer_test_1_ret, 1234, "skel->bss->timer_test_1_ret");
179+
ASSERT_EQ(skel->bss->test_err, 0, "skel->bss->test_err_a");
180+
out:
181+
struct_ops_assoc_in_timer__destroy(skel);
182+
}
183+
104184
void test_struct_ops_assoc(void)
105185
{
106186
if (test__start_subtest("st_ops_assoc"))
107187
test_st_ops_assoc();
108188
if (test__start_subtest("st_ops_assoc_reuse"))
109189
test_st_ops_assoc_reuse();
190+
if (test__start_subtest("st_ops_assoc_in_timer"))
191+
test_st_ops_assoc_in_timer();
192+
if (test__start_subtest("st_ops_assoc_in_timer_no_uref"))
193+
test_st_ops_assoc_in_timer_no_uref();
110194
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <vmlinux.h>
4+
#include <bpf/bpf_tracing.h>
5+
#include "bpf_misc.h"
6+
#include "../test_kmods/bpf_testmod.h"
7+
#include "../test_kmods/bpf_testmod_kfunc.h"
8+
9+
char _license[] SEC("license") = "GPL";
10+
11+
struct elem {
12+
struct bpf_timer timer;
13+
};
14+
15+
struct {
16+
__uint(type, BPF_MAP_TYPE_ARRAY);
17+
__uint(max_entries, 1);
18+
__type(key, int);
19+
__type(value, struct elem);
20+
} array_map SEC(".maps");
21+
22+
#define MAP_MAGIC 1234
23+
int recur;
24+
int test_err;
25+
int timer_ns;
26+
int timer_test_1_ret;
27+
int timer_cb_run;
28+
29+
__noinline static int timer_cb(void *map, int *key, struct bpf_timer *timer)
30+
{
31+
struct st_ops_args args = {};
32+
33+
recur++;
34+
timer_test_1_ret = bpf_kfunc_multi_st_ops_test_1_prog_arg(&args, NULL);
35+
recur--;
36+
37+
timer_cb_run++;
38+
39+
return 0;
40+
}
41+
42+
SEC("struct_ops")
43+
int BPF_PROG(test_1, struct st_ops_args *args)
44+
{
45+
struct bpf_timer *timer;
46+
int key = 0;
47+
48+
if (!recur) {
49+
timer = bpf_map_lookup_elem(&array_map, &key);
50+
if (!timer)
51+
return 0;
52+
53+
bpf_timer_init(timer, &array_map, 1);
54+
bpf_timer_set_callback(timer, timer_cb);
55+
bpf_timer_start(timer, timer_ns, 0);
56+
}
57+
58+
return MAP_MAGIC;
59+
}
60+
61+
SEC("syscall")
62+
int syscall_prog(void *ctx)
63+
{
64+
struct st_ops_args args = {};
65+
int ret;
66+
67+
ret = bpf_kfunc_multi_st_ops_test_1_prog_arg(&args, NULL);
68+
if (ret != MAP_MAGIC)
69+
test_err++;
70+
71+
return 0;
72+
}
73+
74+
SEC(".struct_ops.link")
75+
struct bpf_testmod_multi_st_ops st_ops_map = {
76+
.test_1 = (void *)test_1,
77+
};

0 commit comments

Comments
 (0)