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?
next prev parent 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