Re: [PATCH bpf-next v4 15/16] bpf: Report Verifier Limit errors
Eduard Zingerman <[email protected]>
| Newsgroups | org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
On Thu, 2026-08-13 at 01:33 +0200, Kumar Kartikeya Dwivedi wrote:
...
> diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c
> index 1c997aeba6fa..f39f01637ac0 100644
> --- a/kernel/bpf/liveness.c
> +++ b/kernel/bpf/liveness.c
> @@ -8,6 +8,8 @@
> #include <linux/slab.h>
> #include <linux/sort.h>
>
> +#include "diagnostics.h"
> +
> #define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args)
>
> struct per_frame_masks {
> @@ -1856,6 +1858,10 @@ static int analyze_subprog(struct bpf_verifier_env *env,
> if (++env->liveness->subprog_calls > 10000) {
> verbose(env, "liveness analysis exceeded complexity limit (%d calls)\n",
> env->liveness->subprog_calls);
> + bpf_diag_limit(
> + env, start, "liveness analysis complexity",
> + "Reduce the number of distinct call paths or argument patterns reaching these subprograms.",
Nit: "these subprograms" is not very clear here, drop it?
> + "The verifier recomputed subprogram liveness too many times while tracking stack and register reads across call paths");
Nit: it's not a "subprogram liveness" ->
"Stack liveness analysis failed to reach a fixed point after %d iterations".
> return -E2BIG;
> }
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 8e5319f47ccb..a14315d19866 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -5252,6 +5252,38 @@ struct bpf_subprog_call_depth_info {
> int frame; /* # of consecutive static call stack frames on top of stack */
> };
>
> +static const char *bpf_diag_append_subprog_chain(struct bpf_verifier_env *env,
> + const char *chain, int subprog)
> +{
> + const char *prefix = chain && *chain ? " -> " : "";
> + const char *name = bpf_subprog_name(env, subprog);
> + const char *old = chain ?: "";
> +
> + if (name && *name)
> + return bpf_diag_fmt(env, "%s%s%s", old, prefix, name);
> + return bpf_diag_fmt(env, "%s%ssubprogram %d", old, prefix, subprog);
> +}
> +
> +static const char *bpf_diag_alloc_subprog_call_chain(struct bpf_verifier_env *env,
> + struct bpf_subprog_call_depth_info *dinfo,
> + int idx)
Nit: drop the 'bpf_diag_' prefix.
> +{
> + int call_chain[MAX_CALL_FRAMES + 1];
> + int i, subprog, cnt = 0;
> + const char *chain = NULL;
> +
> + for (subprog = idx; subprog >= 0 && cnt < ARRAY_SIZE(call_chain);
> + subprog = dinfo[subprog].caller)
> + call_chain[cnt++] = subprog;
> +
> + if (subprog >= 0)
> + chain = "...";
> + for (i = cnt - 1; i >= 0; i--)
> + chain = bpf_diag_append_subprog_chain(env, chain, call_chain[i]);
> +
> + return chain;
> +}
> +
> /* starting from main bpf function walk all instructions of the function
> * and recursively walk all callees that given function can call.
> * Ignore jump and exit insns.
> @@ -5294,9 +5326,17 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
> * of caller's stack as shown on the example above.
> */
> if (idx && subprog[idx].has_tail_call && depth >= 256) {
> + const char *chain = bpf_diag_alloc_subprog_call_chain(env, dinfo, idx);
Nit: bubble up `chain` definition to the top of the function.
> +
> verbose(env,
> "tail_calls are not allowed when call stack of previous frames is %d bytes. Too large\n",
> depth);
> + bpf_diag_limit(
> + env, subprog[idx].start, "call stack with tail calls",
"call stack with tail calls" -> "stack depth at tail call"
> + "Reduce stack usage in caller frames, or avoid combining deep bpf2bpf calls with tail calls.",
> + "Call chain %s reaches a subprogram with tail calls after caller frames already use %d bytes; "
> + "tail-call paths are limited to 256 bytes in caller frames",
> + chain ?: "the current call chain", depth);
"the current call chain" is useless, if it's a guard for ENOMEM,
let's make it "<can't render callchain>".
> return -EACCES;
> }
>
...