From: Yonghong Song <yonghong.song@linux.dev>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Eduard Zingerman <eddyz87@gmail.com>,
kernel-team@fb.com
Subject: [PATCH bpf-next 11/12] selftests/bpf: Cover tracing on >8 and <=16 byte return targets
Date: Wed, 8 Jul 2026 13:10:36 -0700 [thread overview]
Message-ID: <20260708201036.2162000-1-yonghong.song@linux.dev> (raw)
In-Reply-To: <20260708200939.2153664-1-yonghong.song@linux.dev>
Tracing attach types differ in whether they need the target's return value
preserved across the BPF trampoline, which saves and restores only 8 bytes
of it. fentry runs before the target returns and never observes the return
value, so it can attach to a function returning a >8 byte value; fexit,
fmod_ret and fsession read the return value and must keep rejecting such a
target.
Add fentry, fexit, fentry_multi and fexit_multi tests that attach to
functions returning a >8 and <=16 byte value (a 16-byte struct and an
__int128).
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
.../selftests/bpf/prog_tests/aggregate_ret.c | 128 ++++++++++++++++++
.../selftests/bpf/progs/aggregate_ret_trace.c | 44 ++++++
.../selftests/bpf/test_kmods/bpf_testmod.c | 32 +++++
.../selftests/bpf/test_kmods/bpf_testmod.h | 5 +
4 files changed, 209 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/aggregate_ret_trace.c
diff --git a/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c b/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c
index 2f1e5c704959..7caf815a2c06 100644
--- a/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c
+++ b/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c
@@ -8,6 +8,7 @@
#include "aggregate_ret_run.skel.h"
#include "aggregate_ret_func.skel.h"
#include "aggregate_ret_kfunc.skel.h"
+#include "aggregate_ret_trace.skel.h"
/*
* Run one program and check its result. The program returns 0 on success, or
@@ -37,6 +38,131 @@ static void run_prog(struct bpf_program *prog, bool may_skip)
ASSERT_EQ(topts.retval, 0, "aggregate_ret_result");
}
+/*
+ * Multi-attach (BPF_TRACE_*_MULTI) requires CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS,
+ * which today only x86_64 selects. Otherwise bpf_tracing_multi_attach() returns
+ * -EOPNOTSUPP unconditionally.
+ */
+static bool tracing_multi_supported(struct bpf_program *prog)
+{
+ const char *small_pat = "bpf_testmod:bpf_testmod_trampoline_count_test";
+ struct bpf_link *link;
+
+ link = bpf_program__attach_tracing_multi(prog, small_pat, NULL);
+ if (libbpf_get_error(link) == -EOPNOTSUPP)
+ return false;
+ bpf_link__destroy(link);
+ return true;
+}
+
+static void test_tracing_attach_types(void)
+{
+ const char *i128_pat = "bpf_testmod:bpf_testmod_aggregate_ret_i128_fn";
+ int err;
+
+ if (test__start_subtest("trace_fentry")) {
+ struct aggregate_ret_trace *trace = NULL;
+
+ trace = aggregate_ret_trace__open();
+ if (!ASSERT_OK_PTR(trace, "trace_fentry_open"))
+ return;
+
+ bpf_program__set_autoload(trace->progs.fexit_bpf_testmod_aggregate_ret_fn, false);
+ bpf_program__set_autoload(trace->progs.fentry_multi, false);
+ bpf_program__set_autoload(trace->progs.fexit_multi, false);
+
+ err = aggregate_ret_trace__load(trace);
+ if (!ASSERT_OK(err, "trace_fentry_load"))
+ goto destroy_trace_fentry;
+
+ err = aggregate_ret_trace__attach(trace);
+ if (!ASSERT_OK(err, "trace_fentry_attach"))
+ goto destroy_trace_fentry;
+
+ ASSERT_OK(trigger_module_test_read(1), "trace_fentry_trigger");
+ ASSERT_EQ(trace->bss->fentry_hit, 1, "trace_fentry_hit");
+
+destroy_trace_fentry:
+ aggregate_ret_trace__destroy(trace);
+ }
+
+ if (test__start_subtest("trace_fexit_reject")) {
+ struct aggregate_ret_trace *trace = NULL;
+
+ trace = aggregate_ret_trace__open();
+ if (!ASSERT_OK_PTR(trace, "trace_fexit_open"))
+ return;
+
+ bpf_program__set_autoload(trace->progs.fentry_bpf_testmod_aggregate_ret_fn, false);
+ bpf_program__set_autoload(trace->progs.fentry_multi, false);
+ bpf_program__set_autoload(trace->progs.fexit_multi, false);
+
+ err = aggregate_ret_trace__load(trace);
+ ASSERT_EQ(err, -EOPNOTSUPP, "trace_fexit_load");
+
+ aggregate_ret_trace__destroy(trace);
+ }
+
+ if (test__start_subtest("trace_fentry_multi")) {
+ struct aggregate_ret_trace *trace = NULL;
+ struct bpf_link *link;
+
+ trace = aggregate_ret_trace__open();
+ if (!ASSERT_OK_PTR(trace, "trace_fentry_multi_open"))
+ return;
+
+ bpf_program__set_autoload(trace->progs.fentry_bpf_testmod_aggregate_ret_fn, false);
+ bpf_program__set_autoload(trace->progs.fexit_bpf_testmod_aggregate_ret_fn, false);
+ bpf_program__set_autoload(trace->progs.fexit_multi, false);
+
+ if (!ASSERT_OK(aggregate_ret_trace__load(trace), "trace_fentry_multi_load"))
+ goto destroy_fentry_multi;
+
+ if (!tracing_multi_supported(trace->progs.fentry_multi)) {
+ test__skip();
+ goto destroy_fentry_multi;
+ }
+
+ link = bpf_program__attach_tracing_multi(trace->progs.fentry_multi, i128_pat, NULL);
+ if (!ASSERT_OK_PTR(link, "trace_fentry_multi_attach"))
+ goto destroy_fentry_multi;
+
+ ASSERT_OK(trigger_module_test_read(1), "trace_fentry_multi_trigger");
+ ASSERT_EQ(trace->bss->fentry_multi_hit, 1, "trace_fentry_multi_hit");
+
+ bpf_link__destroy(link);
+destroy_fentry_multi:
+ aggregate_ret_trace__destroy(trace);
+ }
+
+ if (test__start_subtest("trace_fexit_multi_reject")) {
+ struct aggregate_ret_trace *trace = NULL;
+ struct bpf_link *link;
+
+ trace = aggregate_ret_trace__open();
+ if (!ASSERT_OK_PTR(trace, "trace_fexit_multi_open"))
+ return;
+
+ bpf_program__set_autoload(trace->progs.fentry_bpf_testmod_aggregate_ret_fn, false);
+ bpf_program__set_autoload(trace->progs.fexit_bpf_testmod_aggregate_ret_fn, false);
+ bpf_program__set_autoload(trace->progs.fentry_multi, false);
+
+ if (!ASSERT_OK(aggregate_ret_trace__load(trace), "trace_fexit_multi_load"))
+ goto destroy_fexit_multi;
+
+ if (!tracing_multi_supported(trace->progs.fexit_multi)) {
+ test__skip();
+ goto destroy_fexit_multi;
+ }
+
+ link = bpf_program__attach_tracing_multi(trace->progs.fexit_multi, i128_pat, NULL);
+ ASSERT_EQ(libbpf_get_error(link), -EOPNOTSUPP, "trace_fexit_multi_reject");
+ bpf_link__destroy(link);
+destroy_fexit_multi:
+ aggregate_ret_trace__destroy(trace);
+ }
+}
+
void test_aggregate_ret(void)
{
struct aggregate_ret_struct_c *skel_struct_c;
@@ -118,4 +244,6 @@ void test_aggregate_ret(void)
RUN_TESTS(aggregate_ret_func);
RUN_TESTS(aggregate_ret_kfunc);
+
+ test_tracing_attach_types();
}
diff --git a/tools/testing/selftests/bpf/progs/aggregate_ret_trace.c b/tools/testing/selftests/bpf/progs/aggregate_ret_trace.c
new file mode 100644
index 000000000000..ff9fd4036121
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/aggregate_ret_trace.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+struct bpf_testmod_aggregate_ret {
+ __u64 lo;
+ __u64 hi;
+};
+
+int fentry_hit;
+int fexit_hit;
+int fentry_multi_hit;
+
+SEC("fentry/bpf_testmod:bpf_testmod_aggregate_ret_fn")
+int BPF_PROG2(fentry_bpf_testmod_aggregate_ret_fn, __u64, lo, __u64, hi)
+{
+ fentry_hit++;
+ return 0;
+}
+
+SEC("fexit/bpf_testmod:bpf_testmod_aggregate_ret_fn")
+int BPF_PROG2(fexit_bpf_testmod_aggregate_ret_fn, __u64, lo, __u64, hi,
+ struct bpf_testmod_aggregate_ret, ret)
+{
+ fexit_hit += ret.lo == lo && ret.hi == hi;
+ return 0;
+}
+
+SEC("fentry.multi/bpf_testmod:bpf_testmod_aggregate_ret_i128_fn")
+int BPF_PROG(fentry_multi)
+{
+ fentry_multi_hit++;
+ return 0;
+}
+
+SEC("fexit.multi/bpf_testmod:bpf_testmod_aggregate_ret_i128_fn")
+int BPF_PROG(fexit_multi)
+{
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index c9e211d8a719..b558c538400e 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -469,12 +469,34 @@ noinline void bpf_testmod_stacktrace_test_1(void)
}
int bpf_testmod_fentry_ok;
+u64 bpf_testmod_aggregate_ret_seen;
noinline int bpf_testmod_trampoline_count_test(void)
{
return 0;
}
+__noclone noinline struct bpf_testmod_aggregate_ret
+bpf_testmod_aggregate_ret_fn(u64 lo, u64 hi)
+{
+ struct bpf_testmod_aggregate_ret ret = {
+ .lo = lo,
+ .hi = hi,
+ };
+
+ bpf_testmod_aggregate_ret_seen += lo + hi;
+ return ret;
+}
+
+__noclone noinline __int128
+bpf_testmod_aggregate_ret_i128_fn(u64 lo, u64 hi)
+{
+ __int128 ret = ((__int128)hi << 64) | lo;
+
+ bpf_testmod_aggregate_ret_seen += lo + hi;
+ return ret;
+}
+
noinline ssize_t
bpf_testmod_test_read(struct file *file, struct kobject *kobj,
const struct bin_attribute *bin_attr,
@@ -492,6 +514,8 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj,
struct bpf_testmod_struct_arg_5 struct_arg5 = {23, 24, 25, 26};
union bpf_testmod_union_arg_1 union_arg1 = { .arg = {1} };
union bpf_testmod_union_arg_2 union_arg2 = { .arg = {2, 3} };
+ struct bpf_testmod_aggregate_ret agg_ret;
+ __int128 agg_i128;
int i = 1;
while (bpf_testmod_return_ptr(i))
@@ -534,6 +558,14 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj,
trace_bpf_testmod_test_nullable_bare_tp(NULL);
+ agg_ret = bpf_testmod_aggregate_ret_fn(len, off);
+ if (agg_ret.lo == U64_MAX && agg_ret.hi == U64_MAX)
+ goto out;
+
+ agg_i128 = bpf_testmod_aggregate_ret_i128_fn(len, off);
+ if (agg_i128 == (__int128)-1)
+ goto out;
+
/* Magic number to enable writable tp */
if (len == 64) {
struct bpf_testmod_test_writable_ctx writable = {
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h
index 863fd10f1619..4dcbfd64de62 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h
@@ -25,6 +25,11 @@ struct bpf_testmod_test_writable_ctx {
int val;
};
+struct bpf_testmod_aggregate_ret {
+ __u64 lo;
+ __u64 hi;
+};
+
/* BPF iter that returns *value* *n* times in a row */
struct bpf_iter_testmod_seq {
s64 value;
--
2.53.0-Meta
next prev parent reply other threads:[~2026-07-08 20:10 UTC|newest]
Thread overview: 56+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-08 20:09 [PATCH bpf-next 00/12] bpf: Support 16-byte return values in the R0:R2 register pair Yonghong Song
2026-07-08 20:09 ` [PATCH bpf-next 01/12] bpf: Factor check_global_ret_scalar_reg() out of the global return check Yonghong Song
2026-07-08 20:09 ` [PATCH bpf-next 02/12] bpf: Add verifier support for 16-byte returns in R0:R2 Yonghong Song
2026-07-08 21:17 ` bot+bpf-ci
2026-07-09 16:40 ` Yonghong Song
2026-07-09 22:21 ` Eduard Zingerman
2026-07-10 4:57 ` Yonghong Song
2026-07-08 20:09 ` [PATCH bpf-next 03/12] bpf: Wire up JIT support for 16-byte kfunc returns Yonghong Song
2026-07-10 0:06 ` Eduard Zingerman
2026-07-08 20:10 ` [PATCH bpf-next 04/12] bpf: Track R2 of register-pair returns in precision backtracking Yonghong Song
2026-07-08 21:10 ` bot+bpf-ci
2026-07-09 16:41 ` Yonghong Song
2026-07-10 0:31 ` Eduard Zingerman
2026-07-08 20:10 ` [PATCH bpf-next 05/12] bpf: Account R2 of register-pair returns in live register analysis Yonghong Song
2026-07-08 21:10 ` bot+bpf-ci
2026-07-09 21:07 ` Eduard Zingerman
2026-07-10 5:06 ` Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 06/12] bpf: Force JIT for programs using the R0:R2 register pair Yonghong Song
2026-07-08 21:10 ` bot+bpf-ci
2026-07-09 3:15 ` Leon Hwang
2026-07-09 16:44 ` Yonghong Song
2026-07-09 20:30 ` Eduard Zingerman
2026-07-10 5:07 ` Yonghong Song
2026-07-09 22:27 ` Eduard Zingerman
2026-07-10 5:09 ` Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 07/12] bpf: Reject >8 byte return values on return-reading trampoline paths Yonghong Song
2026-07-09 3:16 ` Leon Hwang
2026-07-09 16:52 ` Yonghong Song
2026-07-10 2:01 ` Leon Hwang
2026-07-09 23:08 ` Eduard Zingerman
2026-07-10 2:02 ` Leon Hwang
2026-07-10 5:27 ` Yonghong Song
2026-07-10 5:12 ` Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 08/12] bpf: Enable 16-byte aggregate return types Yonghong Song
2026-07-08 20:28 ` sashiko-bot
2026-07-09 19:40 ` Yonghong Song
2026-07-10 0:45 ` Eduard Zingerman
2026-07-10 5:29 ` Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 09/12] selftests/bpf: Add C tests for 16-byte returns in R0:R2 Yonghong Song
2026-07-08 20:24 ` sashiko-bot
2026-07-09 19:51 ` Yonghong Song
2026-07-08 21:10 ` bot+bpf-ci
2026-07-09 19:54 ` Yonghong Song
2026-07-10 1:19 ` Eduard Zingerman
2026-07-10 5:32 ` Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 10/12] selftests/bpf: Add inline-asm and subprog tests for R0:R2 returns Yonghong Song
2026-07-08 20:28 ` sashiko-bot
2026-07-09 19:57 ` Yonghong Song
2026-07-10 1:38 ` Eduard Zingerman
2026-07-10 5:35 ` Yonghong Song
2026-07-08 20:10 ` Yonghong Song [this message]
2026-07-08 20:10 ` [PATCH bpf-next 12/12] Documentation/bpf: Document 16-byte kfunc return values in R0:R2 Yonghong Song
2026-07-10 0:56 ` [PATCH bpf-next 00/12] bpf: Support 16-byte return values in the R0:R2 register pair Eduard Zingerman
2026-07-10 6:00 ` Yonghong Song
2026-07-10 6:13 ` Eduard Zingerman
2026-07-10 15:18 ` Yonghong Song
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=20260708201036.2162000-1-yonghong.song@linux.dev \
--to=yonghong.song@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=kernel-team@fb.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox