BPF List
 help / color / mirror / Atom feed
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 04/12] bpf: Track R2 of register-pair returns in precision backtracking
Date: Wed,  8 Jul 2026 13:10:00 -0700	[thread overview]
Message-ID: <20260708201000.2157684-1-yonghong.song@linux.dev> (raw)
In-Reply-To: <20260708200939.2153664-1-yonghong.song@linux.dev>

A function returning a value larger than 8 bytes (a struct/union, or an
__int128) uses R2 as a second return register alongside R0. Previous
precision backtracking only treated R0 as a return register at a
call/return boundary, so marking the second half of such a return precise
would trip the "unexpected regs" checks in backtrack_insn() and reject a
valid program with -EFAULT.

Handle R2 like R0 in the three boundaries where a call defines the return
registers:

 - static subprog exit (BPF_EXIT): R2 is a return register, not a leftover
   argument; clear it before the args check and propagate its precision
   into the callee.
 - global subprog call: a global subprog returning >8 bytes also sets R2;
   clear it before the args check.
 - kfunc call (BPF_CALL): a kfunc returning >8 bytes (model ret_size > 8)
   also sets R2; clear it like R0.

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 include/linux/bpf_verifier.h |  2 ++
 kernel/bpf/backtrack.c       | 32 +++++++++++++++++++++++++++++++-
 kernel/bpf/verifier.c        | 13 ++++++++++++-
 3 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 317e99b9acc0..fa255f326240 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1431,6 +1431,8 @@ int bpf_jmp_offset(struct bpf_insn *insn);
 struct bpf_iarray *bpf_insn_successors(struct bpf_verifier_env *env, u32 idx);
 void bpf_fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask);
 bool bpf_subprog_is_global(const struct bpf_verifier_env *env, int subprog);
+bool bpf_ret_reg_pair(struct bpf_verifier_env *env, int subprog);
+int bpf_get_kfunc_ret_size(const struct bpf_prog *prog, u32 func_id, u16 offset);
 
 int bpf_find_subprog(struct bpf_verifier_env *env, int off);
 bool bpf_is_throw_kfunc(struct bpf_insn *insn);
diff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c
index 2e4ae0ef0860..c46c2778bf60 100644
--- a/kernel/bpf/backtrack.c
+++ b/kernel/bpf/backtrack.c
@@ -424,6 +424,8 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
 				 */
 				verifier_bug_if(idx + 1 != subseq_idx, env,
 						"extra insn from subprog");
+				if (bpf_ret_reg_pair(env, subprog))
+					bt_clear_reg(bt, BPF_REG_2);
 				/* r1-r5 are invalidated after subprog call,
 				 * so for global func call it shouldn't be set
 				 * anymore
@@ -507,6 +509,12 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
 				return -ENOTSUPP;
 			/* regular helper call sets R0 */
 			bt_clear_reg(bt, BPF_REG_0);
+			/* On error the return is negative, R2 stays set, and the
+			 * check below catches it as an unexpected register.
+			 */
+			if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL &&
+			    bpf_get_kfunc_ret_size(env->prog, insn->imm, insn->off) > 8)
+				bt_clear_reg(bt, BPF_REG_2);
 			if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) {
 				/* if backtracking was looking for registers R1-R5
 				 * they should have been found already.
@@ -521,7 +529,25 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
 					return -EFAULT;
 			}
 		} else if (opcode == BPF_EXIT) {
-			bool r0_precise;
+			bool r0_precise, r2_precise = false;
+
+			/* A subprog returning a value larger than 8 bytes (a
+			 * struct/union or an __int128) uses R2 as a second
+			 * return register alongside R0. When exiting such a
+			 * subprog, R2 is a return register rather than a
+			 * leftover argument, so handle it like R0 below.
+			 */
+			if (subseq_idx - 1 >= 0 &&
+			    bpf_pseudo_call(&env->prog->insnsi[subseq_idx - 1])) {
+				int call_idx = subseq_idx - 1;
+				int tgt = call_idx + env->prog->insnsi[call_idx].imm + 1;
+				int subprog = bpf_find_subprog(env, tgt);
+
+				if (subprog >= 0 &&
+				    bpf_ret_reg_pair(env, subprog) &&
+				    bt_is_reg_set(bt, BPF_REG_2))
+					r2_precise = true;
+			}
 
 			/* Backtracking to a nested function call, 'idx' is a part of
 			 * the inner frame 'subseq_idx' is a part of the outer frame.
@@ -534,6 +560,8 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
 			if (subseq_idx >= 0 && bpf_calls_callback(env, subseq_idx))
 				for (i = BPF_REG_1; i <= BPF_REG_5; i++)
 					bt_clear_reg(bt, i);
+			if (r2_precise)
+				bt_clear_reg(bt, BPF_REG_2);
 			if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) {
 				verifier_bug(env, "backtracking exit unexpected regs %x",
 					     bt_reg_mask(bt));
@@ -558,6 +586,8 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
 
 			if (r0_precise)
 				bt_set_reg(bt, BPF_REG_0);
+			if (r2_precise)
+				bt_set_reg(bt, BPF_REG_2);
 			/* r6-r9 and stack slots will stay set in caller frame
 			 * bitmasks until we return back from callee(s)
 			 */
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e3f934cf702d..fcbf532159a5 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -424,7 +424,7 @@ static bool subprog_returns_void(struct bpf_verifier_env *env, int subprog)
 	return btf_type_is_void(type);
 }
 
-static bool bpf_ret_reg_pair(struct bpf_verifier_env *env, int subprog)
+bool bpf_ret_reg_pair(struct bpf_verifier_env *env, int subprog)
 {
 	const struct btf_type *type, *func, *func_proto;
 	const struct btf *btf = env->prog->aux->btf;
@@ -2521,6 +2521,17 @@ int bpf_get_kfunc_addr(const struct bpf_prog *prog, u32 func_id,
 	return 0;
 }
 
+int bpf_get_kfunc_ret_size(const struct bpf_prog *prog, u32 func_id, u16 offset)
+{
+	const struct bpf_kfunc_desc *desc;
+
+	desc = find_kfunc_desc(prog, func_id, offset);
+	if (!desc)
+		return -EFAULT;
+
+	return desc->func_model.ret_size;
+}
+
 #define BPF_FD_SLOT_BTF	1UL
 
 static void fd_slot_set_map(struct bpf_fd_array *slot, struct bpf_map *map)
-- 
2.53.0-Meta


  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 ` Yonghong Song [this message]
2026-07-08 21:10   ` [PATCH bpf-next 04/12] bpf: Track R2 of register-pair returns in precision backtracking 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 ` [PATCH bpf-next 11/12] selftests/bpf: Cover tracing on >8 and <=16 byte return targets Yonghong Song
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=20260708201000.2157684-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