From: Yonghong Song <yonghong.song@linux.dev>
To: bot+bpf-ci@kernel.org, bpf@vger.kernel.org
Cc: ast@kernel.org, andrii@kernel.org, daniel@iogearbox.net,
eddyz87@gmail.com, kernel-team@fb.com, martin.lau@kernel.org,
clm@meta.com, ihor.solodrai@linux.dev
Subject: Re: [PATCH bpf-next v4 07/13] bpf: Add verifier support for 16-byte returns in R0:R2
Date: Wed, 12 Aug 2026 13:12:08 -0700 [thread overview]
Message-ID: <636f9c57-6bca-4f51-9c06-5c2918992ffc@linux.dev> (raw)
In-Reply-To: <f7b8141d61d6b7089679d057252d90cc7e05f22d1bf000ae2bf16259a45cf845@mail.kernel.org>
On 8/10/26 6:24 PM, bot+bpf-ci@kernel.org wrote:
>> bpf: Add verifier support for 16-byte returns in R0:R2
>>
>> LLVM 23 added support for returning a value in two registers for an
>> __int128, or a struct/union whose size is greater than 8 but not more than
>> 16 bytes. See LLVM patches [1] and [2].
>>
>> Before LLVM 23 the BPF backend could not return these values at all. A
>> by-value struct or union return (of any size) was rejected at compile time
>> with:
>>
>> error: aggregate returns are not supported
>>
>> and an __int128 return failed later in the backend with:
>>
>> fatal error: error in backend: unable to allocate function return #1
>>
>> Both are resolved in LLVM 23, which lowers such returns into the R0:R2
>> register pair.
>>
>> This patch adds handling for returns greater than 8 bytes in several
>> places: BPF subprogram returns (the main program, and both global and
>> static subprograms) and kfunc returns.
>>
>> The R0:R2 convention is only implemented in the JIT. The BPF interpreter
>> has no notion of a second return register: a BPF-to-BPF call goes through
>> JMP_CALL_ARGS and a BPF_EXIT hands back BPF_R0 alone, so a caller reading
>> R2 would see a stale value. Force the JIT wherever a caller can observe the
>> pair, that is at the call to a global subprogram in check_func_call() and
>> at the return from a static subprogram in prepare_func_exit(). Kfunc calls
>> need no separate handling since bpf_add_kfunc_call() already sets
>> jit_required for every kfunc call.
>>
>> A by-value struct or union returned by a kfunc must be composed only of
>> scalars, since the verifier models the returned register bits as an unknown
>> scalar and a pointer field would otherwise be laundered into one, escaping
>> provenance and reference tracking.
>>
>> A global subprogram must return a scalar in every return register. The
>> existing exemption for arena pointers now applies only when the return
>> value fits in R0 alone: both halves of a register pair carry a piece of a
>>> 8 byte scalar, so an arena pointer in either of them is a leak rather than
>> a legitimate return value. A subprogram whose whole return value is an
>> arena pointer is unaffected.
>>
>> A static subprogram is handled differently. The verifier walks into its
>> frame, so prepare_func_exit() propagates the return register(s) to the
>> caller. R0 holding a stack pointer has long been rejected outright there,
>> but R2 is deliberately not treated the same way. LLVM owns both sides of a
>> static call and is not bound by the ABI, so even with a 9..16 byte declared
>> return type it may leave R2 untouched when the caller only consumes the low
>> half; R2 can then hold an incidental stack pointer that is not a return
>> value at all, and rejecting the program would be a false positive.
>> Propagating the register as is would be worse: the callee frame is freed
>> immediately afterwards, leaving the caller with a PTR_TO_STACK that refers
>> to a frame which no longer exists. So the caller's R2 is marked
>> uninitialized instead, and only a caller that actually reads the returned
>> upper half fails. As with R0, a pointer into the caller's own frame is
>> scrubbed too, which is conservative but keeps the two registers consistent.
>>
>> Once callers read R0:R2, an extension program can no longer replace a
>> function with a >8 byte return value: an extension's own return is
>> capped at 8 bytes by the program-exit check above, so it would leave R2
>> stale for the target's callers. btf_check_type_match() cannot catch
>> this, as it compares return types by btf_type->info only and an int
>> carries no vlen, so a 16-byte __int128 and an 8-byte long compare equal.
>> Reject such an attach in bpf_check_attach_target() instead.
>>
>> [1] https://github.com/llvm/llvm-project/pull/190894
>> [2] https://github.com/llvm/llvm-project/pull/206876
>>
>> Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
> The main program is listed among the places that gain handling for a >8
> byte return:
>
>> This patch adds handling for returns greater than 8 bytes in several
>> places: BPF subprogram returns (the main program, and both global and
>> static subprograms) and kfunc returns.
> For the main program the patch does the opposite, though.
> process_bpf_exit_full() gains an outright rejection:
>
> if (!cur_frame->subprogno && bpf_ret_reg_pair(env, 0)) {
> verbose(env,
> "return value larger than 8 bytes is not supported at program exit\n");
> return -EINVAL;
> }
>
> That rejection and its error message are not described anywhere in the
> changelog. The only trace of it is the later backward reference:
>
>> an extension's own return is
>> capped at 8 bytes by the program-exit check above
> which points at a check the changelog never introduced, and the extension
> reject added in bpf_check_attach_target() rests entirely on that cap.
>
> Could the changelog say directly that a >8 byte return from the main
> program is rejected at BPF_EXIT, rather than listing the main program as a
> place where such returns are now handled?
Okay, will update commit message as you suggested in the above.
>
>
> ---
> 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/31446101762
next prev parent reply other threads:[~2026-08-12 20:12 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-11 0:09 [PATCH bpf-next v4 00/13] bpf: Support aggregate return values up to 16 bytes Yonghong Song
2026-08-11 0:09 ` [PATCH bpf-next v4 01/13] bpf: Factor check_global_ret_scalar_reg() out of the global return check Yonghong Song
2026-08-11 0:09 ` [PATCH bpf-next v4 02/13] bpf: Add helpers to describe the R0:R2 return register pair Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 19:31 ` Yonghong Song
2026-08-12 20:07 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 03/13] bpf: Wire up JIT support for 16-byte kfunc returns Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 19:48 ` Yonghong Song
2026-08-12 20:42 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 04/13] bpf: Track R2 of register-pair returns in precision backtracking Yonghong Song
2026-08-12 21:16 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 05/13] bpf: Account R2 of register-pair returns in live register analysis Yonghong Song
2026-08-11 1:09 ` bot+bpf-ci
2026-08-12 19:55 ` Yonghong Song
2026-08-12 21:21 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 06/13] bpf: Reject callbacks returning more than 8 bytes Yonghong Song
2026-08-12 21:41 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 07/13] bpf: Add verifier support for 16-byte returns in R0:R2 Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 20:12 ` Yonghong Song [this message]
2026-08-12 22:12 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 08/13] bpf: Reject register-pair returns when the subprog BTF is unreliable Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 20:26 ` Yonghong Song
2026-08-12 22:24 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 09/13] bpf: Enable aggregate return types up to 16 bytes Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 20:29 ` Yonghong Song
2026-08-12 22:47 ` Eduard Zingerman
2026-08-11 0:10 ` [PATCH bpf-next v4 10/13] selftests/bpf: Add C tests for 16-byte returns in R0:R2 Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 20:49 ` Yonghong Song
2026-08-12 23:15 ` Eduard Zingerman
2026-08-11 0:10 ` [PATCH bpf-next v4 11/13] selftests/bpf: Add inline-asm and subprog tests for R0:R2 returns Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 21:08 ` Yonghong Song
2026-08-12 23:29 ` Eduard Zingerman
2026-08-13 0:09 ` Eduard Zingerman
2026-08-11 0:10 ` [PATCH bpf-next v4 12/13] selftests/bpf: Add tests for callbacks returning more than 8 bytes Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 21:22 ` Yonghong Song
2026-08-13 0:10 ` Eduard Zingerman
2026-08-11 0:10 ` [PATCH bpf-next v4 13/13] Documentation/bpf: Document up to 16-byte kfunc return values in R0:R2 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=636f9c57-6bca-4f51-9c06-5c2918992ffc@linux.dev \
--to=yonghong.song@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bot+bpf-ci@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=clm@meta.com \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=ihor.solodrai@linux.dev \
--cc=kernel-team@fb.com \
--cc=martin.lau@kernel.org \
/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.