* [PATCH bpf-next v3 1/3] bpf: Reject >8 byte return values on return-reading trampoline paths
2026-07-10 22:52 [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values Yonghong Song
@ 2026-07-10 22:52 ` Yonghong Song
2026-07-10 22:52 ` [PATCH bpf-next v3 2/3] bpf, x86: Fix trampoline stack size for 128-bit arguments Yonghong Song
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Yonghong Song @ 2026-07-10 22:52 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, kernel-team, Leon Hwang
btf_distill_func_proto() builds the function model used for the
fentry/fexit/fmod_ret/fsession trampolines and struct_ops. It has
accepted a 16-byte __int128 return value since the trampoline was
introduced: __get_type_size() returns the integer's type size, and the
return-type check only rejected ret < 0.
But the BPF trampoline preserves only 8 bytes of the return value (RAX on
x86, i.e. R0). For an attach type that reads the target's return value the
second half (RDX / R3) is neither saved nor restored, so a program
attached to a function returning a 16-byte value corrupts the value seen
by the real caller and itself observes only half of it. struct_ops
trampolines have the same limitation.
This affects the attach types that read the target's return value: fexit,
fmod_ret and fsession (plus the _multi variants of fexit and fsession),
and struct_ops. fentry/fentry_multi run before the target returns and are
unaffected.
Reject a >8 byte return value for these attach types in
bpf_check_attach_target() and bpf_check_attach_btf_id_multi(), and for
struct_ops in bpf_struct_ops_desc_init().
Fixes: fec56f5890d9 ("bpf: Introduce BPF trampoline")
Cc: Leon Hwang <leon.hwang@linux.dev>
Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
kernel/bpf/bpf_struct_ops.c | 12 ++++++++++++
kernel/bpf/verifier.c | 25 +++++++++++++++++++++++++
2 files changed, 37 insertions(+)
diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
index 51b16e5f5534..4e7a48c02be5 100644
--- a/kernel/bpf/bpf_struct_ops.c
+++ b/kernel/bpf/bpf_struct_ops.c
@@ -445,6 +445,18 @@ int bpf_struct_ops_desc_init(struct bpf_struct_ops_desc *st_ops_desc,
goto errout;
}
+ /*
+ * A >8 byte return value is passed back in a register pair,
+ * which the struct_ops trampoline does not preserve (only
+ * 8 bytes of the return value are saved and restored).
+ */
+ if (st_ops->func_models[i].ret_size > 8) {
+ pr_warn("func ptr %s in struct %s has a >8 byte return value, which is not supported\n",
+ mname, st_ops->name);
+ err = -EOPNOTSUPP;
+ goto errout;
+ }
+
stub_func_addr = *(void **)(st_ops->cfi_stubs + moff);
err = prepare_arg_info(btf, st_ops->name, mname,
func_proto, stub_func_addr,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6515d4d3c003..26d281b77a6e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -18873,6 +18873,20 @@ static int btf_id_allow_sleepable(u32 btf_id, unsigned long addr, const struct b
return -EINVAL;
}
+static bool attach_uses_trampoline_retval(enum bpf_attach_type type)
+{
+ switch (type) {
+ case BPF_MODIFY_RETURN:
+ case BPF_TRACE_FEXIT:
+ case BPF_TRACE_FEXIT_MULTI:
+ case BPF_TRACE_FSESSION:
+ case BPF_TRACE_FSESSION_MULTI:
+ return true;
+ default:
+ return false;
+ }
+}
+
int bpf_check_attach_target(struct bpf_verifier_log *log,
const struct bpf_prog *prog,
const struct bpf_prog *tgt_prog,
@@ -19137,6 +19151,14 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
if (ret < 0)
return ret;
+ if (tgt_info->fmodel.ret_size > 8 &&
+ attach_uses_trampoline_retval(prog->expected_attach_type)) {
+ bpf_log(log,
+ "Attach to function %s with a >8 byte return value is not supported for this attach type\n",
+ tname);
+ return -EOPNOTSUPP;
+ }
+
/*
* *.multi programs don't need an address during program
* verification, we just take the module ref if needed.
@@ -19413,6 +19435,9 @@ int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 bt
err = btf_distill_func_proto(NULL, btf, t, tname, &tgt_info->fmodel);
if (err < 0)
return err;
+ if (tgt_info->fmodel.ret_size > 8 &&
+ attach_uses_trampoline_retval(prog->expected_attach_type))
+ return -EOPNOTSUPP;
if (btf_is_module(btf)) {
/* The bpf program already holds reference to module. */
if (WARN_ON_ONCE(!prog->aux->mod))
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH bpf-next v3 2/3] bpf, x86: Fix trampoline stack size for 128-bit arguments
2026-07-10 22:52 [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values Yonghong Song
2026-07-10 22:52 ` [PATCH bpf-next v3 1/3] bpf: Reject >8 byte return values on return-reading trampoline paths Yonghong Song
@ 2026-07-10 22:52 ` Yonghong Song
2026-07-10 22:52 ` [PATCH bpf-next v3 3/3] selftests/bpf: Add tests for >8 byte return value and " Yonghong Song
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Yonghong Song @ 2026-07-10 22:52 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, kernel-team
btf_distill_func_proto() accepts a function argument up to 16 bytes, so a
128-bit scalar such as __int128 reaches the x86 trampoline with
arg_size == 16. But the current implementation assumes an __int128
argument only needs one register, so the register save area is
under-allocated and save_args() overwrites adjacent stack slots.
Compute the register count from arg_size for all arguments to fix it.
Fixes: a9c5ad31fbdc ("bpf: x86: Support in-register struct arguments in trampoline programs")
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
arch/x86/net/bpf_jit_comp.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index de7515ea1bea..0b8bd30f7c87 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -3369,11 +3369,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
WARN_ON_ONCE((flags & BPF_TRAMP_F_INDIRECT) &&
(flags & ~(BPF_TRAMP_F_INDIRECT | BPF_TRAMP_F_RET_FENTRY_RET)));
- /* extra registers for struct arguments */
- for (i = 0; i < m->nr_args; i++) {
- if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
- nr_regs += (m->arg_size[i] + 7) / 8 - 1;
- }
+ for (i = 0; i < m->nr_args; i++)
+ nr_regs += (m->arg_size[i] + 7) / 8 - 1;
/* x86-64 supports up to MAX_BPF_FUNC_ARGS arguments. 1-6
* are passed through regs, the remains are through stack.
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH bpf-next v3 3/3] selftests/bpf: Add tests for >8 byte return value and 128-bit arguments
2026-07-10 22:52 [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values Yonghong Song
2026-07-10 22:52 ` [PATCH bpf-next v3 1/3] bpf: Reject >8 byte return values on return-reading trampoline paths Yonghong Song
2026-07-10 22:52 ` [PATCH bpf-next v3 2/3] bpf, x86: Fix trampoline stack size for 128-bit arguments Yonghong Song
@ 2026-07-10 22:52 ` Yonghong Song
2026-07-11 1:02 ` [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values Kumar Kartikeya Dwivedi
2026-07-11 12:18 ` Leon Hwang
4 siblings, 0 replies; 7+ messages in thread
From: Yonghong Song @ 2026-07-10 22:52 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, kernel-team
The BPF trampoline preserves only 8 bytes of the target's return value
(R0), so attaching an fexit/fmod_ret/fsession program to a function that
returns a >8 byte value is now rejected by the verifier. Add a bpf_testmod
function returning __int128 and an fexit program that targets it. The
program is expected to fail to load with the "with a >8 byte return value
is not supported for this attach type" message.
A 128-bit __int128 argument is passed in a register pair and occupies two
trampoline context slots. Add a bpf_testmod function taking a leading
__int128 argument followed by an int and a long, and an fexit program that
reads those two trailing arguments and the return value, verifying that the
trampoline reserves enough stack for the 128-bit argument and places the
following arguments and the return value at the right context slots.
__int128 is only available on 64-bit targets (where the compiler defines
__SIZEOF_INT128__). The argument test additionally depends on the calling
convention: x86_64 and arm64 pass an __int128 in a register pair as the
trampoline expects, while other architectures pass it differently (e.g.
s390x passes larger arguments by reference), so that subtest runs only on
x86_64 and arm64 and is skipped elsewhere.
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
.../bpf/prog_tests/tracing_failure.c | 12 +++++++
.../selftests/bpf/prog_tests/tracing_struct.c | 36 +++++++++++++++++++
.../selftests/bpf/progs/tracing_failure.c | 6 ++++
.../bpf/progs/tracing_struct_int128.c | 18 ++++++++++
.../selftests/bpf/test_kmods/bpf_testmod.c | 32 +++++++++++++++++
5 files changed, 104 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/tracing_struct_int128.c
diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
index f9f9e1cb87bf..345dd21ad621 100644
--- a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
+++ b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
@@ -76,6 +76,16 @@ static void test_fexit_noreturns(void)
"Attaching fexit/fsession/fmod_ret to __noreturn function 'do_exit' is rejected.");
}
+static void test_fexit_int128_ret(void)
+{
+#ifdef __SIZEOF_INT128__
+ test_tracing_fail_prog("fexit_int128_ret",
+ "with a >8 byte return value is not supported for this attach type");
+#else
+ test__skip();
+#endif
+}
+
void test_tracing_failure(void)
{
if (test__start_subtest("bpf_spin_lock"))
@@ -86,4 +96,6 @@ void test_tracing_failure(void)
test_tracing_deny();
if (test__start_subtest("fexit_noreturns"))
test_fexit_noreturns();
+ if (test__start_subtest("fexit_int128_ret"))
+ test_fexit_int128_ret();
}
diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_struct.c b/tools/testing/selftests/bpf/prog_tests/tracing_struct.c
index 6f8c0bfb0415..15b95d0235b5 100644
--- a/tools/testing/selftests/bpf/prog_tests/tracing_struct.c
+++ b/tools/testing/selftests/bpf/prog_tests/tracing_struct.c
@@ -4,6 +4,7 @@
#include <test_progs.h>
#include "tracing_struct.skel.h"
#include "tracing_struct_many_args.skel.h"
+#include "tracing_struct_int128.skel.h"
static void test_struct_args(void)
{
@@ -112,6 +113,39 @@ static void test_struct_many_args(void)
tracing_struct_many_args__destroy(skel);
}
+static void test_int128_args(void)
+{
+ /*
+ * __int128 arguments are passed in a register pair on x86_64 and
+ * arm64, which the trampoline packs into two context slots. Other
+ * architectures pass a __int128 differently (e.g. s390x passes larger
+ * arguments by reference), so only exercise this on x86_64 and arm64.
+ */
+#if defined(__x86_64__) || defined(__aarch64__)
+ struct tracing_struct_int128 *skel;
+ int err;
+
+ skel = tracing_struct_int128__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "tracing_struct_int128__open_and_load"))
+ return;
+
+ err = tracing_struct_int128__attach(skel);
+ if (!ASSERT_OK(err, "tracing_struct_int128__attach"))
+ goto destroy_skel;
+
+ ASSERT_OK(trigger_module_test_read(256), "trigger_read");
+
+ ASSERT_EQ(skel->bss->t_b, 2, "t:b");
+ ASSERT_EQ(skel->bss->t_c, 3, "t:c");
+ ASSERT_EQ(skel->bss->t_ret, 6, "t ret");
+
+destroy_skel:
+ tracing_struct_int128__destroy(skel);
+#else
+ test__skip();
+#endif
+}
+
static void test_union_args(void)
{
struct tracing_struct *skel;
@@ -145,6 +179,8 @@ void test_tracing_struct(void)
test_struct_args();
if (test__start_subtest("struct_many_args"))
test_struct_many_args();
+ if (test__start_subtest("int128_args"))
+ test_int128_args();
if (test__start_subtest("union_args"))
test_union_args();
}
diff --git a/tools/testing/selftests/bpf/progs/tracing_failure.c b/tools/testing/selftests/bpf/progs/tracing_failure.c
index 65e485c4468c..f7a095767679 100644
--- a/tools/testing/selftests/bpf/progs/tracing_failure.c
+++ b/tools/testing/selftests/bpf/progs/tracing_failure.c
@@ -30,3 +30,9 @@ int BPF_PROG(fexit_noreturns)
{
return 0;
}
+
+SEC("?fexit/bpf_testmod_test_int128_ret")
+int BPF_PROG(fexit_int128_ret)
+{
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/tracing_struct_int128.c b/tools/testing/selftests/bpf/progs/tracing_struct_int128.c
new file mode 100644
index 000000000000..4638dfec1f38
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tracing_struct_int128.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_helpers.h>
+
+long t_b, t_c, t_ret;
+
+SEC("fexit/bpf_testmod_test_int128_arg")
+int test_int128_arg_fexit(unsigned long long *ctx)
+{
+ t_b = (int)ctx[2];
+ t_c = (long)ctx[3];
+ t_ret = (long)ctx[4];
+ 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 30f1cd23093c..eb0f9b5e18d8 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -161,6 +161,33 @@ bpf_testmod_test_arg_ptr_to_struct(struct bpf_testmod_struct_arg_1 *a) {
return bpf_testmod_test_struct_arg_result;
}
+#ifdef __SIZEOF_INT128__
+noinline __int128
+bpf_testmod_test_int128_ret(int a)
+{
+ bpf_testmod_test_struct_arg_result = a;
+ return (__int128)a;
+}
+
+/*
+ * The __int128 'a' is the first argument on purpose. On arm64 a 16-byte
+ * argument must start in an even-numbered register pair, so placing it
+ * after a single-register scalar would leave a padding register (x1)
+ * unused. pahole maps parameters to registers positionally and would then
+ * see the following argument in an "unexpected" register and skip BTF
+ * encoding of the whole function, making it unattachable. Keeping the
+ * __int128 first (x0:x1) avoids the padding while still exercising the
+ * trampoline packing of a 128-bit argument together with the trailing
+ * int and long arguments.
+ */
+noinline long
+bpf_testmod_test_int128_arg(__int128 a, int b, long c)
+{
+ bpf_testmod_test_struct_arg_result = (long)a + b + c;
+ return bpf_testmod_test_struct_arg_result;
+}
+#endif
+
__weak noinline void bpf_testmod_looooooooooooooooooooooooooooooong_name(void)
{
}
@@ -514,6 +541,11 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj,
(void)bpf_testmod_test_arg_ptr_to_struct(&struct_arg1_2);
+#ifdef __SIZEOF_INT128__
+ (void)bpf_testmod_test_int128_ret(i);
+ (void)bpf_testmod_test_int128_arg((__int128)1, 2, 3);
+#endif
+
(void)trace_bpf_testmod_test_raw_tp_null_tp(NULL);
bpf_testmod_test_struct_ops3();
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values
2026-07-10 22:52 [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values Yonghong Song
` (2 preceding siblings ...)
2026-07-10 22:52 ` [PATCH bpf-next v3 3/3] selftests/bpf: Add tests for >8 byte return value and " Yonghong Song
@ 2026-07-11 1:02 ` Kumar Kartikeya Dwivedi
2026-07-11 3:58 ` Yonghong Song
2026-07-11 12:18 ` Leon Hwang
4 siblings, 1 reply; 7+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-07-11 1:02 UTC (permalink / raw)
To: Yonghong Song, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, kernel-team
On Sat Jul 11, 2026 at 12:52 AM CEST, Yonghong Song wrote:
> The BPF trampoline preserves only 8 bytes of a target function's return
> value (R0), and its register save area under-allocates space for 128-bit
> arguments for x86_64. These two problems lead to memory corruption or
> incorrect values observed by BPF programs and the real caller.
>
> This series fixes both issues and adds two selftests, otherwise, each of
> them will fail if without the corresponding fix.
>
This is almost ready, but CI currently fails for tracing_struct tests. [0]
My AI agent suggests the following root cause, which sounds like a pahole issue.
"
• The failure is caused by missing module BTF, not by the trampoline fix itself.
tracing_struct/int128_args fails with -ESRCH because pahole omits
bpf_testmod_test_int128_arg() from bpf_testmod.ko’s BTF. The failure occurs
before the BPF program attaches; see .kdev/logs/vm-cmd.log:3.
The problematic signature is tools/testing/selftests/bpf/test_kmods/bpf_testmod.c:184:
bpf_testmod_test_int128_arg(__int128 a, int b, long c)
On x86-64:
- a consumes rdi:rsi.
- b, the second source parameter, is therefore in rdx.
- pahole maps the second parameter positionally to rsi, sees b in rdx, and
marks the function as having unexpected register usage.
- Kernel BTF generation enables consistent_func, so the complete function is
omitted from BTF (scripts/Makefile.btf:17).
Verbose pahole confirmed:
bpf_testmod_test_int128_arg : skipping BTF encoding of function due to unexpected register usage for parameter
This also means the comment claiming that putting __int128 first prevents the pahole issue is incorrect.
A scratch probe with the exact kdev Clang and pahole showed:
int128_first : skipping BTF encoding ...
FUNC int128_last ...
The likely test correction is to use:
bpf_testmod_test_int128_arg(int b, long c, __int128 a)
and update tools/testing/selftests/bpf/progs/tracing_struct_int128.c:12 to
read b from ctx[0], c from ctx[1], and the return value from ctx[4]. With two
preceding scalar registers, the 128-bit argument starts at an even register
pair on arm64 too. This still exercises the four-slot allocation that the x86
patch fixes.
"
[0]: https://github.com/kernel-patches/bpf/pull/12778
> Changelogs:
> v2 -> v3:
> - v2: https://lore.kernel.org/bpf/20260710182204.1085329-1-yonghong.song@linux.dev/
> - Align __int128 argument at even position enforced by arm64.
> v1 -> v2:
> - v1: https://lore.kernel.org/bpf/20260710144404.2579671-1-yonghong.song@linux.dev/
> - Also handle __int128 arguments for x86_64.
>
> Yonghong Song (3):
> bpf: Reject >8 byte return values on return-reading trampoline paths
> bpf, x86: Fix trampoline stack size for 128-bit arguments
> selftests/bpf: Add tests for >8 byte return value and 128-bit
> arguments
>
> arch/x86/net/bpf_jit_comp.c | 7 ++--
> kernel/bpf/bpf_struct_ops.c | 12 +++++++
> kernel/bpf/verifier.c | 25 +++++++++++++
> .../bpf/prog_tests/tracing_failure.c | 12 +++++++
> .../selftests/bpf/prog_tests/tracing_struct.c | 36 +++++++++++++++++++
> .../selftests/bpf/progs/tracing_failure.c | 6 ++++
> .../bpf/progs/tracing_struct_int128.c | 18 ++++++++++
> .../selftests/bpf/test_kmods/bpf_testmod.c | 32 +++++++++++++++++
> 8 files changed, 143 insertions(+), 5 deletions(-)
> create mode 100644 tools/testing/selftests/bpf/progs/tracing_struct_int128.c
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values
2026-07-11 1:02 ` [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values Kumar Kartikeya Dwivedi
@ 2026-07-11 3:58 ` Yonghong Song
0 siblings, 0 replies; 7+ messages in thread
From: Yonghong Song @ 2026-07-11 3:58 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, kernel-team, Alan Maguire
On 7/10/26 6:02 PM, Kumar Kartikeya Dwivedi wrote:
> On Sat Jul 11, 2026 at 12:52 AM CEST, Yonghong Song wrote:
>> The BPF trampoline preserves only 8 bytes of a target function's return
>> value (R0), and its register save area under-allocates space for 128-bit
>> arguments for x86_64. These two problems lead to memory corruption or
>> incorrect values observed by BPF programs and the real caller.
>>
>> This series fixes both issues and adds two selftests, otherwise, each of
>> them will fail if without the corresponding fix.
>>
> This is almost ready, but CI currently fails for tracing_struct tests. [0]
>
> My AI agent suggests the following root cause, which sounds like a pahole issue.
>
> "
> • The failure is caused by missing module BTF, not by the trampoline fix itself.
>
> tracing_struct/int128_args fails with -ESRCH because pahole omits
> bpf_testmod_test_int128_arg() from bpf_testmod.ko’s BTF. The failure occurs
> before the BPF program attaches; see .kdev/logs/vm-cmd.log:3.
>
> The problematic signature is tools/testing/selftests/bpf/test_kmods/bpf_testmod.c:184:
>
> bpf_testmod_test_int128_arg(__int128 a, int b, long c)
>
> On x86-64:
>
> - a consumes rdi:rsi.
> - b, the second source parameter, is therefore in rdx.
> - pahole maps the second parameter positionally to rsi, sees b in rdx, and
> marks the function as having unexpected register usage.
> - Kernel BTF generation enables consistent_func, so the complete function is
> omitted from BTF (scripts/Makefile.btf:17).
>
> Verbose pahole confirmed:
>
> bpf_testmod_test_int128_arg : skipping BTF encoding of function due to unexpected register usage for parameter
>
> This also means the comment claiming that putting __int128 first prevents the pahole issue is incorrect.
>
> A scratch probe with the exact kdev Clang and pahole showed:
>
> int128_first : skipping BTF encoding ...
> FUNC int128_last ...
>
> The likely test correction is to use:
>
> bpf_testmod_test_int128_arg(int b, long c, __int128 a)
>
> and update tools/testing/selftests/bpf/progs/tracing_struct_int128.c:12 to
> read b from ctx[0], c from ctx[1], and the return value from ctx[4]. With two
> preceding scalar registers, the 128-bit argument starts at an even register
> pair on arm64 too. This still exercises the four-slot allocation that the x86
> patch fixes.
> "
>
> [0]: https://github.com/kernel-patches/bpf/pull/12778
Thanks, Kumar,
This indeed is a pahole issue. My local test is working because my local pahole
has the following patch set:
pahole: Encode true signatures in kernel BTF
https://lore.kernel.org/bpf/20260625020148.1883082-1-yonghong.song@linux.dev/
With this, e.g, running selftest like './test_progs -t tracing_struct' will succeed
for both clang21 and gcc15.
Without the above pahole patch set (pahole: Encode true signatures in kernel BTF),
the 'tracing_struct' test will fail with either llvm21 or gcc15:
libbpf: prog 'test_int128_arg_fexit': failed to find kernel BTF type ID of 'bpf_testmod_test_int128_arg': -ESRCH
libbpf: prog 'test_int128_arg_fexit': failed to prepare load attributes: -ESRCH
libbpf: prog 'test_int128_arg_fexit': failed to load: -ESRCH
libbpf: failed to load object 'tracing_struct_int128'
libbpf: failed to load BPF skeleton 'tracing_struct_int128': -ESRCH
test_int128_args:FAIL:tracing_struct_int128__open_and_load unexpected error: -3
#539/3 tracing_struct/int128_args:FAIL
So in order to unblock this patch set, the pahole patch set
pahole: Encode true signatures in kernel BTF
should be merged soon (maybe after some minor adjustment).
I will contact Alan for this as I will take some time off in the next couple of weeks.
>
>> Changelogs:
>> v2 -> v3:
>> - v2: https://lore.kernel.org/bpf/20260710182204.1085329-1-yonghong.song@linux.dev/
>> - Align __int128 argument at even position enforced by arm64.
>> v1 -> v2:
>> - v1: https://lore.kernel.org/bpf/20260710144404.2579671-1-yonghong.song@linux.dev/
>> - Also handle __int128 arguments for x86_64.
>>
>> Yonghong Song (3):
>> bpf: Reject >8 byte return values on return-reading trampoline paths
>> bpf, x86: Fix trampoline stack size for 128-bit arguments
>> selftests/bpf: Add tests for >8 byte return value and 128-bit
>> arguments
>>
>> arch/x86/net/bpf_jit_comp.c | 7 ++--
>> kernel/bpf/bpf_struct_ops.c | 12 +++++++
>> kernel/bpf/verifier.c | 25 +++++++++++++
>> .../bpf/prog_tests/tracing_failure.c | 12 +++++++
>> .../selftests/bpf/prog_tests/tracing_struct.c | 36 +++++++++++++++++++
>> .../selftests/bpf/progs/tracing_failure.c | 6 ++++
>> .../bpf/progs/tracing_struct_int128.c | 18 ++++++++++
>> .../selftests/bpf/test_kmods/bpf_testmod.c | 32 +++++++++++++++++
>> 8 files changed, 143 insertions(+), 5 deletions(-)
>> create mode 100644 tools/testing/selftests/bpf/progs/tracing_struct_int128.c
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values
2026-07-10 22:52 [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values Yonghong Song
` (3 preceding siblings ...)
2026-07-11 1:02 ` [PATCH bpf-next v3 0/3] bpf: Fix trampoline handling of 128-bit values Kumar Kartikeya Dwivedi
@ 2026-07-11 12:18 ` Leon Hwang
4 siblings, 0 replies; 7+ messages in thread
From: Leon Hwang @ 2026-07-11 12:18 UTC (permalink / raw)
To: Yonghong Song, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, kernel-team
On 2026/7/11 06:52, Yonghong Song wrote:
> The BPF trampoline preserves only 8 bytes of a target function's return
> value (R0), and its register save area under-allocates space for 128-bit
> arguments for x86_64. These two problems lead to memory corruption or
> incorrect values observed by BPF programs and the real caller.
>
> This series fixes both issues and adds two selftests, otherwise, each of
> them will fail if without the corresponding fix.
lgtm,
Acked-by: Leon Hwang <leon.hwang@linux.dev>
[...]
^ permalink raw reply [flat|nested] 7+ messages in thread