From: Qiliang Yuan <odys.yuan@gmail.com>
To: Andrii Nakryiko <andrii@kernel.org>,
Eduard Zingerman <eddyz87@gmail.com>,
Ihor Solodrai <ihor.solodrai@linux.dev>,
Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Martin KaFai Lau <martin.lau@linux.dev>,
Song Liu <song@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Jiri Olsa <jolsa@kernel.org>,
Emil Tsalapatis <emil@etsalapatis.com>,
Shuah Khan <shuah@kernel.org>, Amery Hung <ameryhung@gmail.com>
Cc: bpf@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org, Qiliang Yuan <odys.yuan@gmail.com>
Subject: [PATCH bpf-next v5 3/4] selftests/bpf: Test freeing a struct_ops map under a running prog
Date: Mon, 19 Oct 2026 10:27:57 +0800 [thread overview]
Message-ID: <20261019-selftests-bpf-struct-ops-assoc-timer-v5-3-77b2e181e5cc@gmail.com> (raw)
In-Reply-To: <20261019-selftests-bpf-struct-ops-assoc-timer-v5-0-77b2e181e5cc@gmail.com>
st_ops_assoc_in_timer_no_uref drops every reference to the struct_ops
map while a timer callback of its prog calls .test_1 through the
association, and checks that the callback ends up with no struct_ops.
The timer first fires 500ms later and .test_1 returns right away, so the
callback hardly ever runs .test_1 while the map is freed, and the test
can't catch a map that is freed under a running .test_1.
Add st_ops_assoc_in_timer_free, which starts the timer right away,
restarts it with no delay until the association is gone, and makes
.test_1 spin in bpf_loop() when the timer callback calls it. Share the
steps of st_ops_assoc_in_timer_no_uref with it through a helper, which
stops when associating or running syscall_prog fails instead of waiting
for a timer that never starts.
st_ops_assoc_in_timer_free run in a loop on kernels of the same bpf-next
commit with KASAN enabled, x86_64 VM with 32 vCPUs, without and with
the previous fix:
without the fix with the fix
outcome Oops (int3) on run 1 100 of 100 passed, median 0.27 s
Signed-off-by: Qiliang Yuan <odys.yuan@gmail.com>
---
.../bpf/prog_tests/test_struct_ops_assoc.c | 37 ++++++++++++++++++----
.../bpf/progs/struct_ops_assoc_in_timer.c | 16 ++++++++++
2 files changed, 47 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
index 461ded7223515..6c85d368bf167 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
@@ -136,7 +136,8 @@ static void test_st_ops_assoc_in_timer(void)
struct_ops_assoc_in_timer__destroy(skel);
}
-static void test_st_ops_assoc_in_timer_no_uref(void)
+static void run_st_ops_assoc_in_timer_no_uref(int timer_ns, int wait_map_free,
+ int spin_loops)
{
struct struct_ops_assoc_in_timer *skel = NULL;
struct bpf_link *link;
@@ -148,7 +149,8 @@ static void test_st_ops_assoc_in_timer_no_uref(void)
err = bpf_program__assoc_struct_ops(skel->progs.syscall_prog,
skel->maps.st_ops_map, NULL);
- ASSERT_OK(err, "bpf_program__assoc_struct_ops");
+ if (!ASSERT_OK(err, "bpf_program__assoc_struct_ops"))
+ goto out;
link = bpf_map__attach_struct_ops(skel->maps.st_ops_map);
if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops"))
@@ -157,12 +159,18 @@ static void test_st_ops_assoc_in_timer_no_uref(void)
/*
* Run .test_1 by calling kfunc bpf_kfunc_multi_st_ops_test_1_prog_arg() and checks
* the return value. .test_1 will also schedule timer_cb that runs .test_1 again.
- * timer_cb will run 500ms after syscall_prog runs, when the user space no longer
- * holds a reference to st_ops_map.
+ * timer_cb will run timer_ns after syscall_prog runs and, with wait_map_free, every
+ * timer_ns after that until the map is freed. With 500ms, it first runs when the
+ * user space no longer holds a reference to st_ops_map.
*/
- skel->bss->timer_ns = 500000000;
+ skel->bss->timer_ns = timer_ns;
+ skel->bss->wait_map_free = wait_map_free;
+ skel->bss->spin_loops = spin_loops;
err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog), NULL);
- ASSERT_OK(err, "bpf_prog_test_run_opts");
+ if (!ASSERT_OK(err, "bpf_prog_test_run_opts")) {
+ bpf_link__destroy(link);
+ goto out;
+ }
/* Detach and close struct_ops map to cause it to be freed */
bpf_link__destroy(link);
@@ -178,6 +186,21 @@ static void test_st_ops_assoc_in_timer_no_uref(void)
struct_ops_assoc_in_timer__destroy(skel);
}
+static void test_st_ops_assoc_in_timer_no_uref(void)
+{
+ run_st_ops_assoc_in_timer_no_uref(500000000, 0, 0);
+}
+
+/*
+ * Keep calling .test_1 through the association from the timer callback, and
+ * stay in it for a while each time, while the map is being freed. The map
+ * must not be freed under a running .test_1.
+ */
+static void test_st_ops_assoc_in_timer_free(void)
+{
+ run_st_ops_assoc_in_timer_no_uref(0, 1, 1 << 20);
+}
+
void test_struct_ops_assoc(void)
{
if (test__start_subtest("st_ops_assoc"))
@@ -188,4 +211,6 @@ void test_struct_ops_assoc(void)
test_st_ops_assoc_in_timer();
if (test__start_subtest("st_ops_assoc_in_timer_no_uref"))
test_st_ops_assoc_in_timer_no_uref();
+ if (test__start_subtest("st_ops_assoc_in_timer_free"))
+ test_st_ops_assoc_in_timer_free();
}
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c
index 0bed49e9f2170..47a44c8e8b114 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c
@@ -25,6 +25,13 @@ int test_err;
int timer_ns;
int timer_test_1_ret;
int timer_cb_run;
+int wait_map_free;
+int spin_loops;
+
+static int spin(u32 i, void *ctx)
+{
+ return 0;
+}
__noinline static int timer_cb(void *map, int *key, struct bpf_timer *timer)
{
@@ -34,6 +41,12 @@ __noinline static int timer_cb(void *map, int *key, struct bpf_timer *timer)
timer_test_1_ret = bpf_kfunc_multi_st_ops_test_1_assoc(&args);
recur--;
+ /* Try again later until the map is freed */
+ if (wait_map_free && timer_test_1_ret != -1) {
+ bpf_timer_start(timer, timer_ns, 0);
+ return 0;
+ }
+
timer_cb_run++;
return 0;
@@ -53,6 +66,9 @@ int BPF_PROG(test_1, struct st_ops_args *args)
bpf_timer_init(timer, &array_map, 1);
bpf_timer_set_callback(timer, timer_cb);
bpf_timer_start(timer, timer_ns, 0);
+ } else if (spin_loops) {
+ /* Stay in the trampoline while the map may be freed */
+ bpf_loop(spin_loops, spin, NULL, 0);
}
return MAP_MAGIC;
--
2.43.0
next prev parent reply other threads:[~2026-10-19 2:28 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-19 2:27 [PATCH bpf-next v5 0/4] bpf: Fix a struct_ops use-after-free behind a flaky assoc_in_timer test Qiliang Yuan
2026-10-19 2:27 ` [PATCH bpf-next v5 1/4] bpf: Protect the struct_ops association with a spinlock Qiliang Yuan
2026-10-19 2:27 ` [PATCH bpf-next v5 2/4] bpf: Dissociate struct_ops progs before the grace period of the map Qiliang Yuan
2026-10-19 2:27 ` Qiliang Yuan [this message]
2026-10-19 2:27 ` [PATCH bpf-next v5 4/4] selftests/bpf: Wait for the struct_ops map to be freed in assoc_in_timer Qiliang Yuan
2026-10-21 0:01 ` [PATCH bpf-next v5 0/4] bpf: Fix a struct_ops use-after-free behind a flaky assoc_in_timer test Alexei Starovoitov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261019-selftests-bpf-struct-ops-assoc-timer-v5-3-77b2e181e5cc@gmail.com \
--to=odys.yuan@gmail.com \
--cc=ameryhung@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=ihor.solodrai@linux.dev \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=shuah@kernel.org \
--cc=song@kernel.org \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.