BPF List
 help / color / mirror / Atom feed
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


  reply	other threads:[~2026-08-12 20:12 UTC|newest]

Thread overview: 37+ 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-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-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-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-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-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-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox