BPF List
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>, bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next 3/5] bpf, arm64: Support unwinding via r3
Date: Sun, 18 Oct 2026 01:34:29 +0000	[thread overview]
Message-ID: <sashiko-outbox-175873@kernel.org> (raw)
In-Reply-To: <20261018010246.1643877-4-alexei.starovoitov@gmail.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] bpf, arm64: stale jump_target in bpf_jit_unwind_exit() skips landing pad
--

commit e9dd702da2c478ecd8523b3f071fba585759acfc
Author: Alexei Starovoitov <ast@kernel.org>

bpf, arm64: Support unwinding via r3

This commit adds arm64 JIT support for unwinding via r3 by replacing the
post-call conditional branch with an exception table entry and rewriting the
return address in the callee frame record upon unwinding exit.

> diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
> index 506c4201fec85..9b702930486a1 100644
> --- a/arch/arm64/net/bpf_jit_comp.c
> +++ b/arch/arm64/net/bpf_jit_comp.c
[ ... ]
> @@ -1781,6 +1863,13 @@ static int build_insn(const struct bpf_verifier_env *env, const struct bpf_insn
>  			emit_kfunc_arg_moves(ctx, fm);
>  		}
>  		emit_call(func_addr, ctx);
> +		if (bpf_jit_unwind_site(env, ctx->prog, i)) {
> +			ret = add_unwind_exentry(ctx, i + 2 + insn[1].off);
> +			if (ret)
> +				return ret;
> +			emit(A64_MOV(1, r0, A64_R(0)), ctx);
> +			return 1;
> +		}
>  		/*
>  		 * Call to arch_bpf_timed_may_goto() is emitted by the
>  		 * verifier and called with custom calling convention with
> @@ -1799,6 +1888,13 @@ static int build_insn(const struct bpf_verifier_env *env, const struct bpf_insn
>  		 * counter and the private stack are callee saved.
>  		 */
>  		emit(A64_BLR(dst), ctx);
> +		if (bpf_jit_unwind_site(env, ctx->prog, i)) {
> +			ret = add_unwind_exentry(ctx, i + 2 + insn[1].off);
> +			if (ret)
> +				return ret;
> +			emit(A64_MOV(1, bpf2a64[BPF_REG_0], A64_R(0)), ctx);
> +			return 1;
> +		}
>  		emit(A64_MOV(1, bpf2a64[BPF_REG_0], A64_R(0)), ctx);
>  		break;
>  	/* tail call */
> @@ -1808,6 +1904,9 @@ static int build_insn(const struct bpf_verifier_env *env, const struct bpf_insn
>  		break;
>  	/* function return */
>  	case BPF_JMP | BPF_EXIT:
> +		unw = bpf_jit_unwind_exit(env, ctx->prog, i);
> +		if (unw != BPF_EXIT_RETURNS)
> +			emit_unwind_ret_addr(ctx, unw == BPF_EXIT_LOOK_AT_R3);

[Severity: Critical]
Could stale jump_target metadata in env->insn_aux_data cause
bpf_jit_unwind_exit() or bpf_jit_unwind_site() to skip the landing pad when
unwinding?

In build_insn(), bpf_jit_unwind_site() elides the caller's post-call branch
and relies on the callee rewriting its return address via
emit_unwind_ret_addr(). Both bpf_jit_unwind_site() and bpf_jit_unwind_exit()
use bpf_is_jump_target() at JIT time to determine whether an instruction can
be reached by a jump rather than falling through from the preceding
instruction:

kernel/bpf/core.c:bpf_jit_unwind_exit() {
	...
	if (!i || bpf_is_jump_target(env, prog->aux->subprog_start + i) ||
	    insn[-1].code != (BPF_ALU64 | BPF_MOV | BPF_K) ||
	    insn[-1].dst_reg != BPF_REG_UNWIND)
		return BPF_EXIT_LOOK_AT_R3;
	return insn[-1].imm ? BPF_EXIT_UNWINDS : BPF_EXIT_RETURNS;
}

However, env->insn_aux_data[].jump_target is populated during bpf_check_cfg()
(which only marks branch targets, not fallthrough edges) and is not preserved
across post-CFG instruction removal passes in verifier_remove_insns():

kernel/bpf/fixups.c:verifier_remove_insns() {
	...
	memmove(aux_data + off,	aux_data + off + cnt,
		sizeof(*aux_data) * (orig_prog_len - off - cnt));
	env->insn_aux_data_len -= cnt;
}

For example, in a subprogram annotated with btf_decl_tag("unwind"):

1. Path A sets r3 = 1; goto L, while path B sets r3 = 0 and falls through to
   L, where L is a conditional branch whose target is dead (such as
   if r6 != 0 goto dead; followed by exit;).

2. bpf_check_cfg() marks L with jump_target = true (from goto L), but leaves
   the fallthrough exit at L + 1 with jump_target = false.

3. bpf_prune_dead_branches() or bpf_opt_hard_wire_dead_code_branches()
   rewrites L into goto +0 without setting jump_target on L + 1.

4. bpf_opt_remove_nops() -> verifier_remove_insns() removes the NOP at L,
   shifting exit into slot L (so goto L now targets exit directly with
   r3 = 1) and overwriting aux_data[L].jump_target with
   aux_data[L + 1].jump_target (false). A similar loss of jump_target can
   happen when bpf_remove_fastcall_spills_fills() turns a jump-target spill
   into NOPs that bpf_opt_remove_nops() removes, or when a NOP before the
   post-call if r3 != 0 goto pad is removed.

When bpf_int_jit_compile() -> build_body() -> build_insn() runs,
bpf_jit_unwind_exit() sees !bpf_is_jump_target() and insn[-1] as r3 = 0,
returning BPF_EXIT_RETURNS. As a result, build_insn() omits
emit_unwind_ret_addr() while also eliding the caller's if r3 != 0 goto pad
branch via bpf_jit_unwind_site().

When path A executes at runtime and exits with r3 = 1, won't execution bypass
the landing pad and fall through into the caller's normal return path where
the verifier assumed r3 == 0?

>  		/* Optimization: when last instruction is EXIT,
>  		   simply fallthrough to epilogue. */
>  		if (i == ctx->prog->len - 1)
[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261018010246.1643877-1-alexei.starovoitov@gmail.com?part=3

  reply	other threads:[~2026-10-18  1:34 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-18  1:02 [PATCH bpf-next 0/5] bpf: Unwinding via r3 Alexei Starovoitov
2026-10-18  1:02 ` [PATCH bpf-next 1/5] bpf: Support unwinding " Alexei Starovoitov
2026-10-18  1:32   ` sashiko-bot
2026-10-18  1:48   ` bot+bpf-ci
2026-10-18  1:02 ` [PATCH bpf-next 2/5] bpf, x86: Replace 'if r3 != 0 goto pad' after a call with an extable entry Alexei Starovoitov
2026-10-18  1:28   ` sashiko-bot
2026-10-18  1:02 ` [PATCH bpf-next 3/5] bpf, arm64: Support unwinding via r3 Alexei Starovoitov
2026-10-18  1:34   ` sashiko-bot [this message]
2026-10-18  1:02 ` [PATCH bpf-next 4/5] selftests/bpf: Add tests for " Alexei Starovoitov
2026-10-18  1:38   ` bot+bpf-ci
2026-10-18  1:02 ` [PATCH bpf-next 5/5] selftests/bpf: Add a C++ prog with exceptions Alexei Starovoitov
2026-10-18  1:38   ` bot+bpf-ci
2026-10-18  1:31 ` [PATCH bpf-next 0/5] bpf: Unwinding via r3 Kumar Kartikeya Dwivedi

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=sashiko-outbox-175873@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=alexei.starovoitov@gmail.com \
    --cc=bpf@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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