BPF List
 help / color / mirror / Atom feed
From: Yonghong Song <yonghong.song@linux.dev>
To: sashiko-reviews@lists.linux.dev, bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v11 07/26] bpf: Verify an unwind through landing pads and epilogues
Date: Thu, 15 Oct 2026 08:43:26 -0700	[thread overview]
Message-ID: <3617adcb-de80-4caf-89c8-9bd0e4f7d154@linux.dev> (raw)
In-Reply-To: <sashiko-outbox-172270@kernel.org>



On 10/14/26 9:29 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] bpf: precision tracking fails to backtrack from unwind landing pad
> - [High] bpf: global subprogram unwind pad bypassed if might_throw is true
> --
>
> commit d697ce1fb72f895b857d6e1eeaa219cab7a013bc
> Author: Yonghong Song <yonghong.song@linux.dev>
>
> bpf: Verify an unwind through landing pads and epilogues
>
> The commit adds support for verifying bpf_unwind() through landing pads and
> epilogues. It updates verifier control flow, state tracking, and precision
> backtracking to understand these non-linear execution paths.
>
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 96f5ea8ea8bf8..fe24291260a0e 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
> [ ... ]
>> @@ -19674,6 +19682,133 @@ enum {
>>   	INSN_IDX_UPDATED = 2,
>>   };
>>   
>> +static int unwind_frames(struct bpf_verifier_env *env, bool *do_print_state)
>> +{
> [ ... ]
>> +		/*
>> +		 * Mark the unwinding insn with the frame it ran in: backtracking
>> +		 * from the pad comes back to that insn and goes on in that frame.
>> +		 */
>> +		err = bpf_push_jmp_history(env, state, INSN_F_UNWIND, 0, frameno, NULL, 0);
>> +		if (err)
>> +			return err;
>> +		clear_caller_saved_regs(env, caller->regs);
>> +		mark_reg_unknown(env, caller->regs, BPF_REG_0);
>> +		env->insn_idx = pad;
>> +		if (do_print_state)
>> +			*do_print_state = true;
>> +		return INSN_IDX_UPDATED;
> [Severity: High]
> Does this code break precision backtracking?
>
> The jmp_history entry is pushed before env->insn_idx is updated to pad. This
> means the entry's idx will record the unwinding instruction instead of the pad
> target.
>
> During backtracking, when the verifier reaches pad, it calls
> get_prev_insn_idx() which uses st->jmp_history entries where idx matches the
> jump target. Because idx was recorded as the unwinding instruction,
> get_prev_insn_idx() will fail to find it and fall back to linear backtracking
> (pad - 1) instead of jumping back to the source instruction.
>
> Since exception landing pads are typically emitted out-of-line by the compiler,
> backtracking into pad - 1 jumps into completely unrelated code, corrupting
> precision tracking.

Landing pad backtracking into pad - 1 is incorrect.
In cfg.c, visit_cleanup_pad_edge() calls mark_jmp_point(env, w) on every pad.
So when backtracking happens, pad will goto unwinding insn instead pad - 1.

>
> [ ... ]
>> +static int process_global_call_unwind(struct bpf_verifier_env *env,
>> +				      int call_idx, bool *do_print_state)
>> +{
>> +	const struct bpf_insn *insn = &env->prog->insnsi[call_idx];
>> +	int subprog = bpf_find_subprog(env, call_idx + insn->imm + 1);
>> +	struct bpf_verifier_state *branch;
>> +	struct bpf_func_state *frame;
>> +	int pad;
>> +
>> +	if (!bpf_subprog_is_global(env, subprog) ||
>> +	    !env->subprog_info[subprog].might_unwind)
>> +		return 0;
>> +
>> +	/* The call returning normally is walked later. */
>> +	branch = push_stack(env, call_idx + 1, call_idx, false);
>> +	if (IS_ERR(branch))
>> +		return PTR_ERR(branch);
>> +
>> +	pad = bpf_exc_pad_of_call(env, call_idx);
>> +	if (pad < 0)
>> +		return unwind_frames(env, do_print_state);
>> +	frame = cur_func(env);
>> +	clear_caller_saved_regs(env, frame->regs);
>> +	mark_reg_unknown(env, frame->regs, BPF_REG_0);
>> +	env->insn_idx = pad;
>> +	*do_print_state = true;
>> +	return INSN_IDX_UPDATED;
>> +}
> [Severity: High]
> Can this fail to record the non-linear edge for backtracking?
>
> If a landing pad is found (pad >= 0), env->insn_idx is directly set to pad and
> the function returns. However, it seems a jmp_history entry linking pad back to
> call_idx is never pushed.
>
> Just like in unwind_frames(), this missing history entry causes
> get_prev_insn_idx() to fall back to linear backtracking (pad - 1) during the
> backtrack walk, which jumps into unrelated code and can severely corrupt
> precision tracking.

Actually, as mentioned in the first case, there is a mark_jmp_point
to link call_idx -> pad. So when backtracking happens, pad -> call_idx
should work properly.

>
> [ ... ]
>> @@ -19935,8 +20070,14 @@ static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)
>>   				cur_func(env)->no_stack_arg_load = true;
>>   			if (bpf_is_callx(insn))
>>   				return check_func_callx(env, insn, &env->insn_idx);
>> -			if (insn->src_reg == BPF_PSEUDO_CALL)
>> -				return check_func_call(env, insn, &env->insn_idx);
>> +			if (insn->src_reg == BPF_PSEUDO_CALL) {
>> +				int call_idx = env->insn_idx;
>> +
>> +				err = check_func_call(env, insn, &env->insn_idx);
>> +				if (err)
>> +					return err;
>> +				return process_global_call_unwind(env, call_idx, do_print_state);
>> +			}
> [Severity: High]
> Does this check inadvertently bypass the unwind pad if the global subprogram
> can also throw?
>
> A global subprogram can be marked with both might_throw = true and
> might_unwind = true if it can call both bpf_throw() and bpf_unwind().

If bpf_unwind() exists in the bpf prog, then
bpf_throw() will be rejected in bpf_exc_check_prog(env).

-       if (env->cleanup_info_cnt) {
+       if (env->cleanup_info_cnt || bpf_prog_may_unwind(env)) {
                 ret = bpf_exc_check_prog(env);
                 if (ret)
                         goto skip_full_check;

So this is not an issue.

>
> When verifying a call to such a global subprogram, check_func_call() checks
> might_throw, pushes the normal return branch, and returns PROCESS_BPF_EXIT
> (which is non-zero) to simulate the exception exit:
>
> check_func_call() {
>      ...
>      if (env->subprog_info[subprog].might_throw) {
>          ...
>          return process_bpf_exit_full(env, NULL, true);
>      }
>      ...
> }
>
> Because check_func_call() returns PROCESS_BPF_EXIT, if (err) evaluates to true
> here, causing the verifier to return immediately and skip
> process_global_call_unwind().
>
> As a result, the unwind landing pad for this call is never pushed or explored.
> Dead code sanitization will replace the unvisited pad with a jump to itself
> (an infinite loop). If the global subprogram unwinds at runtime, it will jump
> to this unverified pad and enter an infinite loop, causing a kernel denial of
> service.
>


  reply	other threads:[~2026-10-15 15:43 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15  4:01 [PATCH bpf-next v11 00/26] bpf: Run exception cleanup landing pads when bpf_unwind() unwinds Yonghong Song
2026-10-15  4:01 ` [PATCH bpf-next v11 01/26] bpf: Pack bpf_insn_aux_data flags into bit fields Yonghong Song
2026-10-15  4:01 ` [PATCH bpf-next v11 02/26] bpf: Accept the compiler's exception cleanup table at program load Yonghong Song
2026-10-15  4:01 ` [PATCH bpf-next v11 03/26] bpf: Add the bpf_unwind() and bpf_unwind_resume() kfuncs Yonghong Song
2026-10-15  4:02 ` [PATCH bpf-next v11 04/26] bpf: Keep a call site's landing pad in insn_aux_data, add lookups Yonghong Song
2026-10-15  4:16   ` sashiko-bot
2026-10-15 15:19     ` Yonghong Song
2026-10-15  4:02 ` [PATCH bpf-next v11 05/26] bpf: Mark covered call sites and check a program can take a table Yonghong Song
2026-10-15  4:02 ` [PATCH bpf-next v11 06/26] bpf: Make exception landing pads reachable in the CFG Yonghong Song
2026-10-15  4:02 ` [PATCH bpf-next v11 07/26] bpf: Verify an unwind through landing pads and epilogues Yonghong Song
2026-10-15  4:29   ` sashiko-bot
2026-10-15 15:43     ` Yonghong Song [this message]
2026-10-15  4:02 ` [PATCH bpf-next v11 08/26] bpf: Refuse a callback that can unwind Yonghong Song
2026-10-15  4:02 ` [PATCH bpf-next v11 09/26] bpf: Refuse a landing pad that does not resume Yonghong Song
2026-10-15  4:02 ` [PATCH bpf-next v11 10/26] bpf: Do not use a private stack for a program that can unwind Yonghong Song
2026-10-15  4:02 ` [PATCH bpf-next v11 11/26] bpf: Prepare JITed programs for dispatching cleanup pads Yonghong Song
2026-10-15  4:02 ` [PATCH bpf-next v11 12/26] bpf: Keep an epilogue for each subprog an unwind can pass through Yonghong Song
2026-10-15  4:02 ` [PATCH bpf-next v11 13/26] bpf: Allow a subprog to end with a call that does not return Yonghong Song
2026-10-15  4:02 ` [PATCH bpf-next v11 14/26] bpf: Dispatch cleanup pads by rewriting return addresses Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 15/26] bpf: Refuse a trampoline that calls a subprog that can unwind Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 16/26] bpf, x86: Dispatch exception cleanup pads at run time Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 17/26] bpf, arm64: " Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 18/26] libbpf: Resolve the compiler's _Unwind_Resume to the kernel's kfunc Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 19/26] libbpf: Add cleanup_info to bpf_prog_load_opts Yonghong Song
2026-10-15  4:13   ` sashiko-bot
2026-10-15 15:45     ` Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 20/26] libbpf: Collect .bpf_cleanup records and pass them to the kernel Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 21/26] libbpf: Carry the exception cleanup table through the light skeleton Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 22/26] libbpf: Let the static linker carry .bpf_cleanup relocations Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 23/26] selftests/bpf: Add end-to-end and negative .bpf_cleanup exception tests Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 24/26] selftests/bpf: Add __set_global() and __ret_global() test tags Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 25/26] selftests/bpf: Cover more accepted .bpf_cleanup exception shapes Yonghong Song
2026-10-15  4:03 ` [PATCH bpf-next v11 26/26] selftests/bpf: Load an exception cleanup program from a light skeleton 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=3617adcb-de80-4caf-89c8-9bd0e4f7d154@linux.dev \
    --to=yonghong.song@linux.dev \
    --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