BPF List
 help / color / mirror / Atom feed
From: Yonghong Song <yonghong.song@linux.dev>
To: sashiko-reviews@lists.linux.dev
Cc: bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v8 07/22] bpf: Follow an unwind to its landing pad in the verifier
Date: Fri, 2 Oct 2026 20:31:19 +0100	[thread overview]
Message-ID: <e4059411-8cc4-4bdf-90ca-513ba84c2c90@linux.dev> (raw)
In-Reply-To: <20261001135024.39F561F00898@smtp.kernel.org>



On 10/1/26 6:50 AM, sashiko-bot@kernel.org wrote:
> Subject: bpf: Follow an unwind to its landing pad in the verifier
>
> This patch updates the BPF verifier to follow unwind operations to their
> landing pads. It handles how an unwind rewrites return addresses of frames
> and evaluates the proper precision backtracking.
>
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
> [ ... ]
>> @@ -19126,6 +19145,127 @@ enum {
>>   	INSN_IDX_UPDATED = 2,
>>   };
>>   
>> +/*
>> + * The current frame is leaving through an unwind. Its caller's saved return
>> + * address now points at the pad covering the call, or, with none, at the
>> + * caller's epilogue, and so on down. Follow that from the state the frame
>> + * leaves in -- anything it wrote into its callers' stacks included -- to the
>> + * first pad, or to the main program's frame returning.
>> + */
>> +static int unwind_frames(struct bpf_verifier_env *env, bool *do_print_state)
>> +{
>> +	struct bpf_verifier_state *state = env->cur_state;
>> +	u32 frameno = state->curframe;
>> +	struct bpf_func_state *callee, *caller;
>> +	int err, pad;
>> +
>> +	while (state->curframe) {
>> +		callee = cur_func(env);
>> +		caller = state->frame[state->curframe - 1];
>> +		pad = bpf_exc_pad_of_call(env, callee->callsite);
>> +		/* The caller is at its call now, not at this frame's insn. */
>> +		state->insn_idx = callee->callsite;
>> +		account_processed_insns(env, callee, caller);
>> +		free_func_state(callee);
>> +		state->frame[state->curframe--] = NULL;
>> +		invalidate_outgoing_stack_args(env, caller);
>> +		if (pad < 0)
>> +			continue;
> [Severity: Critical]
> If a callback subprogram (such as one passed to bpf_loop()) calls
> bpf_unwind(), does this loop incorrectly pop frames in the verifier state past
> the C helper?
>
> It appears bpf_unwind() and bpf_unwind_resume() lack the callback restriction
> that bpf_throw() has in do_check_subprogs().
>
> At runtime, the exception unwinder stops unwinding when it encounters the
> non-BPF C helper frame and returns. The callback returns 0, the C helper
> resumes, and it returns normally to the BPF program.
>
> However, if the verifier simulates an unwind through the C helper to the
> caller's landing pad, it marks the normal execution path as dead code
> and replaces it with a ja -1.
>
> Could this divergence between verifier state and runtime execution cause an
> infinite loop and hang the kernel?

This won'd happen. See

int bpf_exc_check_callback(struct bpf_verifier_env *env, int subprog)
{
         if (!env->subprog_info[subprog].might_unwind)
                 return 0;
                 
         verbose(env, "subprog %d may unwind and is used as a callback\n", subprog);
         return -EINVAL;
}

If any callback function may be might_unwind which implies some callee(s)
or further down callee(s) must be a bpf_unwind(), then bpf_exc_check_callback()
will fail.

>
> [ ... ]
>> @@ -19380,13 +19520,32 @@ static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)
>>   					return -EINVAL;
>>   				}
>>   			}
>> +			if (bpf_is_unwind_kfunc(insn) || bpf_is_unwind_resume_kfunc(insn)) {
>> +				err = check_kfunc_allowed_only(env, insn, env->insn_idx);
>> +				if (err)
>> +					return err;
>> +				if (bpf_is_unwind_kfunc(insn))
>> +					return process_bpf_unwind(env, &env->insn_idx,
>> +								  do_print_state);
>> +				/*
>> +				 * The fixups lower this to 'r0 = 0; exit', and
>> +				 * the unwind goes on below this frame.
>> +				 */
>> +				return unwind_frames(env, do_print_state);
>> +			}
>>   			mark_reg_scratched(env, BPF_REG_0);
>>   			if (bpf_in_stack_arg_cnt(&env->subprog_info[cur_func(env)->subprogno]))
>>   				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 unwind_out_of_global_call(env, call_idx, do_print_state);
>> +			}
> [Severity: Critical]
> If check_func_call() processes a global subprogram that is marked with both
> might_throw and might_unwind, it evaluates might_throw, calls
> process_bpf_exit_full(), and returns PROCESS_BPF_EXIT.

The global subprog is marked with both might_throw and might_unwind. The previous
suggested like

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

and we have

bool bpf_prog_may_unwind(const struct bpf_verifier_env *env)
{
         u32 i;

         for (i = 0; i < env->subprog_cnt; i++)
                 if (env->subprog_info[i].might_unwind)
                         return true;
         return false;
}

so bpf_exc_check_prog() will fail if prog has both might_throw and might_unwind.


>
> Does this cause do_check_insn() to return early here with err = 1, completely
> bypassing the unwind_out_of_global_call() logic?
>
> If bypassed, the landing pad associated with the call is never verified and
> is replaced with a ja -1 (dead code). At runtime, if the global subprogram
> calls bpf_unwind(), the exception unwinder routes execution to this unverified
> landing pad.
>
> Can this result in hitting the ja -1 infinite loop and hanging the kernel?


  reply	other threads:[~2026-10-02 19:31 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-01 13:30 [PATCH bpf-next v8 00/22] bpf: Run exception cleanup landing pads when bpf_unwind() unwinds Yonghong Song
2026-10-01 13:30 ` [PATCH bpf-next v8 01/22] bpf: Pack bpf_insn_aux_data flags into bit fields Yonghong Song
2026-10-01 13:30 ` [PATCH bpf-next v8 02/22] bpf: Accept the compiler's exception cleanup table at program load Yonghong Song
2026-10-01 13:30 ` [PATCH bpf-next v8 03/22] bpf: Add the bpf_unwind() and bpf_unwind_resume() kfuncs Yonghong Song
2026-10-01 13:30 ` [PATCH bpf-next v8 04/22] bpf: Add lookups for exception cleanup resumes and landing pads Yonghong Song
2026-10-01 13:48   ` sashiko-bot
2026-10-02 18:17     ` Yonghong Song
2026-10-01 13:30 ` [PATCH bpf-next v8 05/22] bpf: Prepare for an exception cleanup table before the CFG walk Yonghong Song
2026-10-01 14:31   ` bot+bpf-ci
2026-10-02 19:06     ` Yonghong Song
2026-10-01 13:30 ` [PATCH bpf-next v8 06/22] bpf: Make exception landing pads reachable in the CFG Yonghong Song
2026-10-01 13:30 ` [PATCH bpf-next v8 07/22] bpf: Follow an unwind to its landing pad in the verifier Yonghong Song
2026-10-01 13:50   ` sashiko-bot
2026-10-02 19:31     ` Yonghong Song [this message]
2026-10-01 14:31   ` bot+bpf-ci
2026-10-02 20:49     ` Yonghong Song
2026-10-03 12:23   ` Alexei Starovoitov
2026-10-04 17:56     ` Yonghong Song
2026-10-01 13:30 ` [PATCH bpf-next v8 08/22] bpf: Require an unwind to leave a frame holding what it entered with Yonghong Song
2026-10-01 14:31   ` bot+bpf-ci
2026-10-02 21:10     ` Yonghong Song
2026-10-03 12:25   ` Alexei Starovoitov
2026-10-04 17:59     ` Yonghong Song
2026-10-01 13:30 ` [PATCH bpf-next v8 09/22] bpf: Refuse a landing pad that does not resume Yonghong Song
2026-10-03 12:25   ` Alexei Starovoitov
2026-10-04 18:26     ` Yonghong Song
2026-10-01 13:30 ` [PATCH bpf-next v8 10/22] bpf: Do not use a private stack for a program that can unwind Yonghong Song
2026-10-01 13:53   ` sashiko-bot
2026-10-02 21:38     ` Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 11/22] bpf: Dispatch cleanup pads by rewriting return addresses Yonghong Song
2026-10-01 14:31   ` bot+bpf-ci
2026-10-02 21:48     ` Yonghong Song
2026-10-03 12:26   ` Alexei Starovoitov
2026-10-04 18:28     ` Yonghong Song
2026-10-04 18:29     ` Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 12/22] bpf, x86: Dispatch exception cleanup pads at run time Yonghong Song
2026-10-01 13:49   ` sashiko-bot
2026-10-02 21:54     ` Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 13/22] bpf, arm64: " Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 14/22] libbpf: Resolve the compiler's _Unwind_Resume to the kernel's kfunc Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 15/22] libbpf: Add cleanup_info to bpf_prog_load_opts Yonghong Song
2026-10-01 13:46   ` sashiko-bot
2026-10-02 22:09     ` Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 16/22] libbpf: Collect .bpf_cleanup records and pass them to the kernel Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 17/22] libbpf: Carry the exception cleanup table through the light skeleton Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 18/22] libbpf: Let the static linker carry .bpf_cleanup relocations Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 19/22] selftests/bpf: Add end-to-end and negative .bpf_cleanup exception tests Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 20/22] selftests/bpf: Add __set_global() and __ret_global() test tags Yonghong Song
2026-10-01 13:31 ` [PATCH bpf-next v8 21/22] selftests/bpf: Cover more accepted .bpf_cleanup exception shapes Yonghong Song
2026-10-01 13:32 ` [PATCH bpf-next v8 22/22] 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=e4059411-8cc4-4bdf-90ca-513ba84c2c90@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