BPF List
 help / color / mirror / Atom feed
* [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths
@ 2026-07-10 14:44 Yonghong Song
  2026-07-10 14:44 ` [PATCH bpf 2/2] selftests/bpf: Add test for >8 byte return value on fexit attach Yonghong Song
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Yonghong Song @ 2026-07-10 14:44 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..bbb44ef8f87a 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 the R0:R2 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] 10+ messages in thread

* [PATCH bpf 2/2] selftests/bpf: Add test for >8 byte return value on fexit attach
  2026-07-10 14:44 [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths Yonghong Song
@ 2026-07-10 14:44 ` Yonghong Song
  2026-07-10 15:29   ` Leon Hwang
  2026-07-10 14:58 ` [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths sashiko-bot
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 10+ messages in thread
From: Yonghong Song @ 2026-07-10 14:44 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.

__int128 is only available on 64-bit targets (where the compiler defines
__SIZEOF_INT128__). Guard the testmod function and the test with
__SIZEOF_INT128__ so a 32-bit build still compiles; on such builds
the subtest is simply not run.

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 .../selftests/bpf/prog_tests/tracing_failure.c      | 12 ++++++++++++
 tools/testing/selftests/bpf/progs/tracing_failure.c |  6 ++++++
 .../testing/selftests/bpf/test_kmods/bpf_testmod.c  | 13 +++++++++++++
 3 files changed, 31 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
index f9f9e1cb87bf..6c199f5ff4db 100644
--- a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
+++ b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
@@ -76,6 +76,14 @@ static void test_fexit_noreturns(void)
 			       "Attaching fexit/fsession/fmod_ret to __noreturn function 'do_exit' is rejected.");
 }
 
+#ifdef __SIZEOF_INT128__
+static void test_fexit_int128_ret(void)
+{
+	test_tracing_fail_prog("fexit_int128_ret",
+			       "with a >8 byte return value is not supported for this attach type");
+}
+#endif
+
 void test_tracing_failure(void)
 {
 	if (test__start_subtest("bpf_spin_lock"))
@@ -86,4 +94,8 @@ void test_tracing_failure(void)
 		test_tracing_deny();
 	if (test__start_subtest("fexit_noreturns"))
 		test_fexit_noreturns();
+#ifdef __SIZEOF_INT128__
+	if (test__start_subtest("fexit_int128_ret"))
+		test_fexit_int128_ret();
+#endif
 }
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/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 30f1cd23093c..902dbf658250 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -161,6 +161,15 @@ 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;
+}
+#endif
+
 __weak noinline void bpf_testmod_looooooooooooooooooooooooooooooong_name(void)
 {
 }
@@ -514,6 +523,10 @@ 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);
+#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] 10+ messages in thread

* Re: [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths
  2026-07-10 14:44 [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths Yonghong Song
  2026-07-10 14:44 ` [PATCH bpf 2/2] selftests/bpf: Add test for >8 byte return value on fexit attach Yonghong Song
@ 2026-07-10 14:58 ` sashiko-bot
  2026-07-10 15:45   ` Yonghong Song
  2026-07-10 15:45 ` bot+bpf-ci
  2026-07-10 16:37 ` Leon Hwang
  3 siblings, 1 reply; 10+ messages in thread
From: sashiko-bot @ 2026-07-10 14:58 UTC (permalink / raw)
  To: Yonghong Song; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] The x86 BPF trampoline generation under-allocates stack space for 16-byte scalar arguments (e.g., __int128), leading to an out-of-bounds stack write that overwrites the saved RBP and return address.
--

--- Patch [1]: [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths ---
commit 8947b8106804bc7e7328c045366441fc0d165459
Author: Yonghong Song <yonghong.song@linux.dev>

bpf: Reject >8 byte return values on return-reading trampoline paths

This patch rejects BPF trampoline attachments for types like fexit, fmod_ret,
and struct_ops if the target function has a return value larger than 8 bytes.
Previously, the trampoline accepted 16-byte return values (e.g., __int128) but
only saved and restored 8 bytes, which corrupted the return value seen by the
caller.

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 6515d4d3c0032..26d281b77a6e7 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c

[ ... ]

> @@ -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)) {
[Severity: High]
This is a pre-existing issue, but while this patch addresses >8 byte return
values, does the BPF trampoline also have a similar problem with >8 byte
arguments?

In kernel/bpf/btf.c:btf_distill_func_proto(), 16-byte arguments like __int128
are accepted, but __get_type_fmodel_flags() does not set BTF_FMODEL_STRUCT_ARG
for them because they are BTF_KIND_INT, not structs.

In arch/x86/net/bpf_jit_comp.c:__arch_prepare_bpf_trampoline(), the stack
size calculation relies on this flag:

		if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
			nr_regs += (m->arg_size[i] + 7) / 8 - 1;

Because the flag isn't set for __int128, it is incorrectly counted as
1 register, under-allocating the stack space by 8 bytes per 16-byte
scalar argument.

Later during code generation, save_args() computes
arg_regs = (m->arg_size[i] + 7) / 8, which evaluates to 2. It performs
two 8-byte stack stores, which will underflow the allocated boundary and
overwrite the saved RBP and potentially the return address.

Could this lead to out-of-bounds stack writes for fentry/fexit programs
attached to functions with 16-byte scalar arguments?

> +			bpf_log(log,
> +				"Attach to function %s with a >8 byte return value is not supported for this attach type\n",
> +				tname);
> +			return -EOPNOTSUPP;
> +		}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260710144404.2579671-1-yonghong.song@linux.dev?part=1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf 2/2] selftests/bpf: Add test for >8 byte return value on fexit attach
  2026-07-10 14:44 ` [PATCH bpf 2/2] selftests/bpf: Add test for >8 byte return value on fexit attach Yonghong Song
@ 2026-07-10 15:29   ` Leon Hwang
  2026-07-10 15:43     ` Yonghong Song
  0 siblings, 1 reply; 10+ messages in thread
From: Leon Hwang @ 2026-07-10 15:29 UTC (permalink / raw)
  To: Yonghong Song, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, kernel-team

On 2026/7/10 22:44, Yonghong Song wrote:
> 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.
> 
> __int128 is only available on 64-bit targets (where the compiler defines
> __SIZEOF_INT128__). Guard the testmod function and the test with
> __SIZEOF_INT128__ so a 32-bit build still compiles; on such builds
> the subtest is simply not run.
> 
> Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
> ---
>  .../selftests/bpf/prog_tests/tracing_failure.c      | 12 ++++++++++++
>  tools/testing/selftests/bpf/progs/tracing_failure.c |  6 ++++++
>  .../testing/selftests/bpf/test_kmods/bpf_testmod.c  | 13 +++++++++++++
>  3 files changed, 31 insertions(+)
> 
> diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
> index f9f9e1cb87bf..6c199f5ff4db 100644
> --- a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
> +++ b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
> @@ -76,6 +76,14 @@ static void test_fexit_noreturns(void)
>  			       "Attaching fexit/fsession/fmod_ret to __noreturn function 'do_exit' is rejected.");
>  }
>  
> +#ifdef __SIZEOF_INT128__
> +static void test_fexit_int128_ret(void)
> +{
> +	test_tracing_fail_prog("fexit_int128_ret",
> +			       "with a >8 byte return value is not supported for this attach type");
> +}


Seems that this subtest can be simplified to

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
}

Then, no '#ifdef' below.

Thanks,
Leon

> +#endif
> +
>  void test_tracing_failure(void)
>  {
>  	if (test__start_subtest("bpf_spin_lock"))
> @@ -86,4 +94,8 @@ void test_tracing_failure(void)
>  		test_tracing_deny();
>  	if (test__start_subtest("fexit_noreturns"))
>  		test_fexit_noreturns();
> +#ifdef __SIZEOF_INT128__
> +	if (test__start_subtest("fexit_int128_ret"))
> +		test_fexit_int128_ret();
> +#endif
>  }
> 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/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> index 30f1cd23093c..902dbf658250 100644
> --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> @@ -161,6 +161,15 @@ 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;
> +}
> +#endif
> +
>  __weak noinline void bpf_testmod_looooooooooooooooooooooooooooooong_name(void)
>  {
>  }
> @@ -514,6 +523,10 @@ 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);
> +#endif
> +
>  	(void)trace_bpf_testmod_test_raw_tp_null_tp(NULL);
>  
>  	bpf_testmod_test_struct_ops3();


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf 2/2] selftests/bpf: Add test for >8 byte return value on fexit attach
  2026-07-10 15:29   ` Leon Hwang
@ 2026-07-10 15:43     ` Yonghong Song
  0 siblings, 0 replies; 10+ messages in thread
From: Yonghong Song @ 2026-07-10 15:43 UTC (permalink / raw)
  To: Leon Hwang, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, kernel-team



On 7/10/26 8:29 AM, Leon Hwang wrote:
> On 2026/7/10 22:44, Yonghong Song wrote:
>> 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.
>>
>> __int128 is only available on 64-bit targets (where the compiler defines
>> __SIZEOF_INT128__). Guard the testmod function and the test with
>> __SIZEOF_INT128__ so a 32-bit build still compiles; on such builds
>> the subtest is simply not run.
>>
>> Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
>> ---
>>   .../selftests/bpf/prog_tests/tracing_failure.c      | 12 ++++++++++++
>>   tools/testing/selftests/bpf/progs/tracing_failure.c |  6 ++++++
>>   .../testing/selftests/bpf/test_kmods/bpf_testmod.c  | 13 +++++++++++++
>>   3 files changed, 31 insertions(+)
>>
>> diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
>> index f9f9e1cb87bf..6c199f5ff4db 100644
>> --- a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
>> +++ b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c
>> @@ -76,6 +76,14 @@ static void test_fexit_noreturns(void)
>>   			       "Attaching fexit/fsession/fmod_ret to __noreturn function 'do_exit' is rejected.");
>>   }
>>   
>> +#ifdef __SIZEOF_INT128__
>> +static void test_fexit_int128_ret(void)
>> +{
>> +	test_tracing_fail_prog("fexit_int128_ret",
>> +			       "with a >8 byte return value is not supported for this attach type");
>> +}
>
> Seems that this subtest can be simplified to
>
> 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
> }
>
> Then, no '#ifdef' below.

Thanks. Let me test this.

>
> Thanks,
> Leon
>
>> +#endif
>> +
>>   void test_tracing_failure(void)
>>   {
>>   	if (test__start_subtest("bpf_spin_lock"))
>> @@ -86,4 +94,8 @@ void test_tracing_failure(void)
>>   		test_tracing_deny();
>>   	if (test__start_subtest("fexit_noreturns"))
>>   		test_fexit_noreturns();
>> +#ifdef __SIZEOF_INT128__
>> +	if (test__start_subtest("fexit_int128_ret"))
>> +		test_fexit_int128_ret();
>> +#endif
>>   }
>> 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/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
>> index 30f1cd23093c..902dbf658250 100644
>> --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
>> +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
>> @@ -161,6 +161,15 @@ 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;
>> +}
>> +#endif
>> +
>>   __weak noinline void bpf_testmod_looooooooooooooooooooooooooooooong_name(void)
>>   {
>>   }
>> @@ -514,6 +523,10 @@ 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);
>> +#endif
>> +
>>   	(void)trace_bpf_testmod_test_raw_tp_null_tp(NULL);
>>   
>>   	bpf_testmod_test_struct_ops3();


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths
  2026-07-10 14:58 ` [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths sashiko-bot
@ 2026-07-10 15:45   ` Yonghong Song
  0 siblings, 0 replies; 10+ messages in thread
From: Yonghong Song @ 2026-07-10 15:45 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: bpf



On 7/10/26 7:58 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] The x86 BPF trampoline generation under-allocates stack space for 16-byte scalar arguments (e.g., __int128), leading to an out-of-bounds stack write that overwrites the saved RBP and return address.
> --
>
> --- Patch [1]: [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths ---
> commit 8947b8106804bc7e7328c045366441fc0d165459
> Author: Yonghong Song <yonghong.song@linux.dev>
>
> bpf: Reject >8 byte return values on return-reading trampoline paths
>
> This patch rejects BPF trampoline attachments for types like fexit, fmod_ret,
> and struct_ops if the target function has a return value larger than 8 bytes.
> Previously, the trampoline accepted 16-byte return values (e.g., __int128) but
> only saved and restored 8 bytes, which corrupted the return value seen by the
> caller.
>
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 6515d4d3c0032..26d281b77a6e7 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
> [ ... ]
>
>> @@ -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)) {
> [Severity: High]
> This is a pre-existing issue, but while this patch addresses >8 byte return
> values, does the BPF trampoline also have a similar problem with >8 byte
> arguments?
>
> In kernel/bpf/btf.c:btf_distill_func_proto(), 16-byte arguments like __int128
> are accepted, but __get_type_fmodel_flags() does not set BTF_FMODEL_STRUCT_ARG
> for them because they are BTF_KIND_INT, not structs.
>
> In arch/x86/net/bpf_jit_comp.c:__arch_prepare_bpf_trampoline(), the stack
> size calculation relies on this flag:
>
> 		if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
> 			nr_regs += (m->arg_size[i] + 7) / 8 - 1;
>
> Because the flag isn't set for __int128, it is incorrectly counted as
> 1 register, under-allocating the stack space by 8 bytes per 16-byte
> scalar argument.
>
> Later during code generation, save_args() computes
> arg_regs = (m->arg_size[i] + 7) / 8, which evaluates to 2. It performs
> two 8-byte stack stores, which will underflow the allocated boundary and
> overwrite the saved RBP and potentially the return address.
>
> Could this lead to out-of-bounds stack writes for fentry/fexit programs
> attached to functions with 16-byte scalar arguments?

Looks like this indeed an issue for x86.
Since the __int128 issue exists for both return value and argument,
I will send v2 to address both of them together.

>
>> +			bpf_log(log,
>> +				"Attach to function %s with a >8 byte return value is not supported for this attach type\n",
>> +				tname);
>> +			return -EOPNOTSUPP;
>> +		}


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths
  2026-07-10 14:44 [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths Yonghong Song
  2026-07-10 14:44 ` [PATCH bpf 2/2] selftests/bpf: Add test for >8 byte return value on fexit attach Yonghong Song
  2026-07-10 14:58 ` [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths sashiko-bot
@ 2026-07-10 15:45 ` bot+bpf-ci
  2026-07-10 15:47   ` Yonghong Song
  2026-07-10 16:37 ` Leon Hwang
  3 siblings, 1 reply; 10+ messages in thread
From: bot+bpf-ci @ 2026-07-10 15:45 UTC (permalink / raw)
  To: yonghong.song, bpf
  Cc: ast, andrii, daniel, eddyz87, kernel-team, leon.hwang, ast,
	andrii, daniel, martin.lau, eddyz87, yonghong.song, clm,
	ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1566 bytes --]

> diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
> index 51b16e5f5534..bbb44ef8f87a 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 the R0:R2 register
                                                                  ^^^^

Does this comment have the correct register pair? Looking at the commit
message, it states the second half of the return value is "RDX / R3", and
in arch/x86/net/bpf_jit_comp.c reg2hex[] shows BPF_REG_0 = RAX and
BPF_REG_3 = RDX, while BPF_REG_2 = RSI (an argument register).

Should this be R0:R3 instead of R0:R2 to match the x86-64 ABI return pair?

> +	 * 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,

[ ... ]


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/29101893933

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths
  2026-07-10 15:45 ` bot+bpf-ci
@ 2026-07-10 15:47   ` Yonghong Song
  0 siblings, 0 replies; 10+ messages in thread
From: Yonghong Song @ 2026-07-10 15:47 UTC (permalink / raw)
  To: bot+bpf-ci, bpf
  Cc: ast, andrii, daniel, eddyz87, kernel-team, leon.hwang, martin.lau,
	clm, ihor.solodrai



On 7/10/26 8:45 AM, bot+bpf-ci@kernel.org wrote:
>> diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
>> index 51b16e5f5534..bbb44ef8f87a 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 the R0:R2 register
>                                                                    ^^^^
>
> Does this comment have the correct register pair? Looking at the commit
> message, it states the second half of the return value is "RDX / R3", and
> in arch/x86/net/bpf_jit_comp.c reg2hex[] shows BPF_REG_0 = RAX and
> BPF_REG_3 = RDX, while BPF_REG_2 = RSI (an argument register).
>
> Should this be R0:R3 instead of R0:R2 to match the x86-64 ABI return pair?

It should be R0:R3. My commit message is correct, but I forgot to update
the comments. Will fix the comment.

>
>> +	 * 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,
> [ ... ]
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/29101893933


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths
  2026-07-10 14:44 [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths Yonghong Song
                   ` (2 preceding siblings ...)
  2026-07-10 15:45 ` bot+bpf-ci
@ 2026-07-10 16:37 ` Leon Hwang
  2026-07-10 17:45   ` Yonghong Song
  3 siblings, 1 reply; 10+ messages in thread
From: Leon Hwang @ 2026-07-10 16:37 UTC (permalink / raw)
  To: Yonghong Song, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, kernel-team

On 2026/7/10 22:44, Yonghong Song wrote:

[...]

> 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))


I think there's no need to introduce the helper
attach_uses_trampoline_retval().

See this untested diff:

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 03e2202cca13..899d5b129b54 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -19023,6 +19023,7 @@ int bpf_check_attach_target(struct
bpf_verifier_log *log,
 	const char prefix[] = "btf_trace_";
 	struct bpf_raw_event_map *btp;
 	int ret = 0, subprog = -1, i;
+	bool attach_retval = false;
 	const struct btf_type *t;
 	bool conservative = true;
 	const char *tname, *fname;
@@ -19232,19 +19233,21 @@ int bpf_check_attach_target(struct
bpf_verifier_log *log,
 		if (ret)
 			return ret;
 		break;
+	case BPF_MODIFY_RETURN:
+	case BPF_TRACE_FEXIT:
+	case BPF_TRACE_FEXIT_MULTI:
+	case BPF_TRACE_FSESSION:
+	case BPF_TRACE_FSESSION_MULTI:
+		attach_retval = true;
+		fallthrough;
 	default:
-		if (!prog_extension)
+		if (!prog_extension && !attach_retval)
 			return -EINVAL;
 		fallthrough;
-	case BPF_MODIFY_RETURN:
 	case BPF_LSM_MAC:
 	case BPF_LSM_CGROUP:
 	case BPF_TRACE_FENTRY:
-	case BPF_TRACE_FEXIT:
-	case BPF_TRACE_FSESSION:
-	case BPF_TRACE_FSESSION_MULTI:
 	case BPF_TRACE_FENTRY_MULTI:
-	case BPF_TRACE_FEXIT_MULTI:
 		if ((prog->expected_attach_type == BPF_TRACE_FSESSION ||
 		    prog->expected_attach_type == BPF_TRACE_FSESSION_MULTI) &&
 		    !bpf_jit_supports_fsession()) {
@@ -19275,6 +19278,13 @@ int bpf_check_attach_target(struct
bpf_verifier_log *log,
 		if (ret < 0)
 			return ret;

+		if (tgt_info->fmodel.ret_size > 8 && attach_retval) {
+			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.
@@ -19551,6 +19561,13 @@ 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;
+	switch (prog->expected_attach_type) {
+	case BPF_TRACE_FEXIT_MULTI:
+	case BPF_TRACE_FSESSION_MULTI:
+		if (tgt_info->fmodel.ret_size > 8)
+			return -EOPNOTSUPP;
+		break;
+	}
 	if (btf_is_module(btf)) {
 		/* The bpf program already holds reference to module. */
 		if (WARN_ON_ONCE(!prog->aux->mod))


WDYT?

Thanks,
Leon


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths
  2026-07-10 16:37 ` Leon Hwang
@ 2026-07-10 17:45   ` Yonghong Song
  0 siblings, 0 replies; 10+ messages in thread
From: Yonghong Song @ 2026-07-10 17:45 UTC (permalink / raw)
  To: Leon Hwang, bpf
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, kernel-team



On 7/10/26 9:37 AM, Leon Hwang wrote:
> On 2026/7/10 22:44, Yonghong Song wrote:
>
> [...]
>
>> 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))
>
> I think there's no need to introduce the helper
> attach_uses_trampoline_retval().
>
> See this untested diff:
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 03e2202cca13..899d5b129b54 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -19023,6 +19023,7 @@ int bpf_check_attach_target(struct
> bpf_verifier_log *log,
>   	const char prefix[] = "btf_trace_";
>   	struct bpf_raw_event_map *btp;
>   	int ret = 0, subprog = -1, i;
> +	bool attach_retval = false;
>   	const struct btf_type *t;
>   	bool conservative = true;
>   	const char *tname, *fname;
> @@ -19232,19 +19233,21 @@ int bpf_check_attach_target(struct
> bpf_verifier_log *log,
>   		if (ret)
>   			return ret;
>   		break;
> +	case BPF_MODIFY_RETURN:
> +	case BPF_TRACE_FEXIT:
> +	case BPF_TRACE_FEXIT_MULTI:
> +	case BPF_TRACE_FSESSION:
> +	case BPF_TRACE_FSESSION_MULTI:
> +		attach_retval = true;
> +		fallthrough;
>   	default:
> -		if (!prog_extension)
> +		if (!prog_extension && !attach_retval)
>   			return -EINVAL;
>   		fallthrough;
> -	case BPF_MODIFY_RETURN:
>   	case BPF_LSM_MAC:
>   	case BPF_LSM_CGROUP:
>   	case BPF_TRACE_FENTRY:
> -	case BPF_TRACE_FEXIT:
> -	case BPF_TRACE_FSESSION:
> -	case BPF_TRACE_FSESSION_MULTI:
>   	case BPF_TRACE_FENTRY_MULTI:
> -	case BPF_TRACE_FEXIT_MULTI:
>   		if ((prog->expected_attach_type == BPF_TRACE_FSESSION ||
>   		    prog->expected_attach_type == BPF_TRACE_FSESSION_MULTI) &&
>   		    !bpf_jit_supports_fsession()) {
> @@ -19275,6 +19278,13 @@ int bpf_check_attach_target(struct

Thanks for the suggestion. I still prefers the helper approach. It provides
a single place in verifier.c to handle this case (tgt_info->fmodel.ret_size > 8).
The above switch already very subtle and the change makes it even more subtle.

> bpf_verifier_log *log,
>   		if (ret < 0)
>   			return ret;
>
> +		if (tgt_info->fmodel.ret_size > 8 && attach_retval) {
> +			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.
> @@ -19551,6 +19561,13 @@ 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;
> +	switch (prog->expected_attach_type) {
> +	case BPF_TRACE_FEXIT_MULTI:
> +	case BPF_TRACE_FSESSION_MULTI:
> +		if (tgt_info->fmodel.ret_size > 8)
> +			return -EOPNOTSUPP;
> +		break;
> +	}
>   	if (btf_is_module(btf)) {
>   		/* The bpf program already holds reference to module. */
>   		if (WARN_ON_ONCE(!prog->aux->mod))
>
>
> WDYT?
>
> Thanks,
> Leon
>


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-07-10 17:45 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-10 14:44 [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths Yonghong Song
2026-07-10 14:44 ` [PATCH bpf 2/2] selftests/bpf: Add test for >8 byte return value on fexit attach Yonghong Song
2026-07-10 15:29   ` Leon Hwang
2026-07-10 15:43     ` Yonghong Song
2026-07-10 14:58 ` [PATCH bpf 1/2] bpf: Reject >8 byte return values on return-reading trampoline paths sashiko-bot
2026-07-10 15:45   ` Yonghong Song
2026-07-10 15:45 ` bot+bpf-ci
2026-07-10 15:47   ` Yonghong Song
2026-07-10 16:37 ` Leon Hwang
2026-07-10 17:45   ` Yonghong Song

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox