All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Eduard Zingerman <eddyz87@gmail.com>,
	Emil Tsalapatis <emil@etsalapatis.com>,
	kkd@meta.com, kernel-team@meta.com
Subject: [PATCH bpf-next v2 15/17] bpf: Report Verifier Limit errors
Date: Fri, 19 Jun 2026 22:59:28 +0200	[thread overview]
Message-ID: <20260619205934.1312876-16-memxor@gmail.com> (raw)
In-Reply-To: <20260619205934.1312876-1-memxor@gmail.com>

Augment selected verifier limit failures with Verifier Limit reports. These
reports focus on the limit that was exceeded and the observed value or
condition, rather than causal branch history.

Cover tail-call stack constraints, per-subprogram stack depth, combined call
stack depth, static and runtime bpf2bpf call-frame depth, processed-instruction
complexity, and liveness analysis complexity.

Format reason text in diagnostics.c and allocate call-chain descriptions only
on failure paths, preserving useful call-chain context without adding large
local buffers to verifier frames.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 kernel/bpf/diagnostics.c |  40 ++++++++++++++
 kernel/bpf/diagnostics.h |   4 ++
 kernel/bpf/liveness.c    |   6 +++
 kernel/bpf/verifier.c    | 114 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 163 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c
index f199a6eeea54..18217f5f709a 100644
--- a/kernel/bpf/diagnostics.c
+++ b/kernel/bpf/diagnostics.c
@@ -1155,6 +1155,46 @@ void bpf_diag_report_policy(struct bpf_verifier_env *env, u32 insn_idx,
 	bpf_diag_report_suggestion(env, "%s", suggestion);
 }
 
+void bpf_diag_report_limit(struct bpf_verifier_env *env, u32 insn_idx,
+			   const char *limit, const char *suggestion,
+			   const char *reason_fmt, ...)
+{
+	char *reason, *text;
+	va_list args;
+
+	bpf_diag_report_header(env, BPF_DIAG_CATEGORY_VERIFIER_LIMIT,
+			       "limit exceeded");
+	bpf_diag_report_section(env, "Reason");
+
+	va_start(args, reason_fmt);
+	reason = kvasprintf(GFP_KERNEL_ACCOUNT, reason_fmt, args);
+	va_end(args);
+	if (!reason) {
+		bpf_diag_write(env, "%s<failed to allocate diagnostic text>\n",
+			       BPF_DIAG_TEXT_INDENT);
+		goto source;
+	}
+
+	text = kasprintf(GFP_KERNEL_ACCOUNT, "The %s limit was exceeded: %s.",
+			 limit, reason);
+	kfree(reason);
+	if (!text) {
+		bpf_diag_write(env, "%s<failed to allocate diagnostic text>\n",
+			       BPF_DIAG_TEXT_INDENT);
+		goto source;
+	}
+
+	bpf_diag_print_wrapped_text(env, text);
+	kfree(text);
+
+source:
+	bpf_diag_report_section(env, "At");
+	bpf_diag_report_source(env, insn_idx, "error",
+			       "limit exceeded: %s", limit);
+
+	bpf_diag_report_suggestion(env, "%s", suggestion);
+}
+
 void bpf_diag_report_invalid_deref(struct bpf_verifier_env *env, u32 insn_idx,
 				   int regno, const char *reg_name,
 				   const char *type_name,
diff --git a/kernel/bpf/diagnostics.h b/kernel/bpf/diagnostics.h
index 99f82292a740..559c0169062c 100644
--- a/kernel/bpf/diagnostics.h
+++ b/kernel/bpf/diagnostics.h
@@ -224,6 +224,10 @@ void bpf_diag_report_program_structure(struct bpf_verifier_env *env,
 void bpf_diag_report_policy(struct bpf_verifier_env *env, u32 insn_idx,
 			    const char *operation, const char *reason,
 			    const char *suggestion);
+void bpf_diag_report_limit(struct bpf_verifier_env *env, u32 insn_idx,
+			   const char *limit, const char *suggestion,
+			   const char *reason_fmt, ...)
+		__printf(5, 6);
 void bpf_diag_record_branch(struct bpf_verifier_env *env, u32 insn_idx,
 			    bool cond_true);
 void bpf_diag_record_reg_mod(struct bpf_verifier_env *env, u32 insn_idx,
diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c
index 0aadfbae0acc..288d47b6a408 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 {
@@ -1862,6 +1864,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_report_limit(env, start,
+				      "liveness analysis complexity",
+				      "Reduce the number of distinct call paths or argument patterns reaching these subprograms.",
+				      "The verifier recomputed subprogram liveness too many times while tracking stack and register reads across call paths");
 		return -E2BIG;
 	}
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 7938c51eb454..ec51e0c81053 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5277,6 +5277,46 @@ struct bpf_subprog_call_depth_info {
 	int frame; /* # of consecutive static call stack frames on top of stack */
 };
 
+static char *bpf_diag_append_subprog_chain(const struct bpf_verifier_env *env,
+					   char *chain, int subprog)
+{
+	const char *prefix = chain && *chain ? " -> " : "";
+	const char *name = subprog_name(env, subprog);
+	const char *old = chain ?: "";
+	char *next;
+
+	if (name && *name)
+		next = kasprintf(GFP_KERNEL_ACCOUNT, "%s%s%s",
+				 old, prefix, name);
+	else
+		next = kasprintf(GFP_KERNEL_ACCOUNT, "%s%ssubprogram %d",
+				 old, prefix, subprog);
+	if (!next)
+		return chain;
+
+	kfree(chain);
+	return next;
+}
+
+static char *
+bpf_diag_alloc_subprog_call_chain(const struct bpf_verifier_env *env,
+				  struct bpf_subprog_call_depth_info *dinfo,
+				  int idx)
+{
+	int call_chain[MAX_CALL_FRAMES + 1];
+	int i, subprog, cnt = 0;
+	char *chain = NULL;
+
+	for (subprog = idx; subprog >= 0 && cnt < ARRAY_SIZE(call_chain);
+	     subprog = dinfo[subprog].caller)
+		call_chain[cnt++] = subprog;
+
+	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.
@@ -5319,9 +5359,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) {
+		char *chain = bpf_diag_alloc_subprog_call_chain(env, dinfo, idx);
+
 		verbose(env,
 			"tail_calls are not allowed when call stack of previous frames is %d bytes. Too large\n",
 			depth);
+		bpf_diag_report_limit(env, subprog[idx].start,
+				      "call stack with tail calls",
+				      "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);
+		kfree(chain);
 		return -EACCES;
 	}
 
@@ -5343,8 +5391,18 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
 		if (subprog_depth > env->max_stack_depth)
 			env->max_stack_depth = subprog_depth;
 		if (subprog_depth > MAX_BPF_STACK) {
+			char *chain;
+
 			verbose(env, "stack size of subprog %d is %d. Too large\n",
 				idx, subprog_depth);
+			chain = bpf_diag_alloc_subprog_call_chain(env, dinfo, idx);
+			bpf_diag_report_limit(env, subprog[idx].start,
+					      "subprogram stack depth",
+					      "Reduce stack usage in this subprogram, or move large data out of the BPF stack.",
+					      "Call chain %s reaches a subprogram that uses %d bytes of stack, exceeding the %d byte limit for one BPF stack frame",
+					      chain ?: "the current call chain",
+					      subprog_depth, MAX_BPF_STACK);
+			kfree(chain);
 			return -EACCES;
 		}
 	} else {
@@ -5358,13 +5416,25 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
 
 			verbose(env, "combined stack size of %d calls is %d. Too large\n",
 				total, depth);
+			{
+				char *chain;
+
+				chain = bpf_diag_alloc_subprog_call_chain(env, dinfo, idx);
+				bpf_diag_report_limit(env, subprog[idx].start,
+						      "combined call stack depth",
+						      "Reduce stack usage or call depth along this call chain.",
+						      "Call chain %s uses %d bytes of stack across %d nested calls, exceeding the %d byte limit",
+						      chain ?: "the current call chain",
+						      depth, total, MAX_BPF_STACK);
+				kfree(chain);
+			}
 			return -EACCES;
 		}
 	}
 continue_func:
 	subprog_end = subprog[idx + 1].start;
 	for (; i < subprog_end; i++) {
-		int next_insn, sidx;
+		int next_insn, call_insn, sidx;
 
 		if (bpf_pseudo_kfunc_call(insn + i) && !insn[i].off) {
 			bool err = false;
@@ -5415,6 +5485,7 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
 		/* push caller idx into callee's dinfo */
 		dinfo[sidx].caller = idx;
 
+		call_insn = i;
 		i = next_insn;
 
 		idx = sidx;
@@ -5426,8 +5497,18 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
 
 		frame = bpf_subprog_is_global(env, idx) ? 0 : frame + 1;
 		if (frame >= MAX_CALL_FRAMES) {
+			char *chain;
+
 			verbose(env, "the call stack of %d frames is too deep !\n",
 				frame);
+			chain = bpf_diag_alloc_subprog_call_chain(env, dinfo, idx);
+			bpf_diag_report_limit(env, call_insn,
+					      "bpf2bpf call frames",
+					      "Reduce the number of nested bpf2bpf calls on this path.",
+					      "Call chain %s reaches %d static bpf2bpf call frames, exceeding the %d frame limit",
+					      chain ?: "the current call chain",
+					      frame, MAX_CALL_FRAMES);
+			kfree(chain);
 			return -E2BIG;
 		}
 		goto process_func;
@@ -9646,6 +9727,24 @@ typedef int (*set_callee_state_fn)(struct bpf_verifier_env *env,
 				   struct bpf_func_state *callee,
 				   int insn_idx);
 
+static char *
+bpf_diag_alloc_state_call_chain(const struct bpf_verifier_env *env,
+				const struct bpf_verifier_state *state,
+				int next_subprog)
+{
+	char *chain = NULL;
+	int i;
+
+	for (i = 0; i <= state->curframe; i++)
+		chain = bpf_diag_append_subprog_chain(env, chain,
+						      state->frame[i]->subprogno);
+
+	if (next_subprog >= 0)
+		chain = bpf_diag_append_subprog_chain(env, chain, next_subprog);
+
+	return chain;
+}
+
 static int set_callee_state(struct bpf_verifier_env *env,
 			    struct bpf_func_state *caller,
 			    struct bpf_func_state *callee, int insn_idx);
@@ -9658,8 +9757,17 @@ static int setup_func_entry(struct bpf_verifier_env *env, int subprog, int calls
 	int err;
 
 	if (state->curframe + 1 >= MAX_CALL_FRAMES) {
+		char *chain;
+
 		verbose(env, "the call stack of %d frames is too deep\n",
 			state->curframe + 2);
+		chain = bpf_diag_alloc_state_call_chain(env, state, subprog);
+		bpf_diag_report_limit(env, callsite, "bpf2bpf call frames",
+				      "Reduce the number of nested bpf2bpf calls on this path.",
+				      "Call chain %s would create %d verifier call frames, exceeding the %d frame limit",
+				      chain ?: "the current call chain",
+				      state->curframe + 2, MAX_CALL_FRAMES);
+		kfree(chain);
 		return -E2BIG;
 	}
 
@@ -18370,6 +18478,10 @@ static int do_check(struct bpf_verifier_env *env)
 			verbose(env,
 				"BPF program is too large. Processed %d insn\n",
 				env->insn_processed);
+			bpf_diag_report_limit(env, env->insn_idx,
+					      "processed instruction complexity",
+					      "Simplify control flow, reduce branching, or split the program into smaller pieces.",
+					      "The verifier explored more instructions than the complexity limit allows");
 			return -E2BIG;
 		}
 
-- 
2.53.0


  parent reply	other threads:[~2026-06-19 20:59 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-19 20:59 [PATCH bpf-next v2 00/17] Redesign Verification Errors Kumar Kartikeya Dwivedi
2026-06-19 20:59 ` [PATCH bpf-next v2 01/17] bpf: Add verifier diagnostics report helpers Kumar Kartikeya Dwivedi
2026-06-19 21:09   ` sashiko-bot
2026-06-19 20:59 ` [PATCH bpf-next v2 02/17] bpf: Add source and instruction diagnostic context Kumar Kartikeya Dwivedi
2026-06-19 21:46   ` bot+bpf-ci
2026-06-19 20:59 ` [PATCH bpf-next v2 03/17] bpf: Add verifier diagnostic event log Kumar Kartikeya Dwivedi
2026-06-19 21:46   ` bot+bpf-ci
2026-06-19 20:59 ` [PATCH bpf-next v2 04/17] bpf: Prune verifier diagnostics on backtracking Kumar Kartikeya Dwivedi
2026-06-19 21:46   ` bot+bpf-ci
2026-06-19 20:59 ` [PATCH bpf-next v2 05/17] bpf: Track verifier register diagnostic events Kumar Kartikeya Dwivedi
2026-06-19 21:18   ` sashiko-bot
2026-06-19 23:35   ` Alexei Starovoitov
2026-06-19 20:59 ` [PATCH bpf-next v2 06/17] bpf: Track verifier reference " Kumar Kartikeya Dwivedi
2026-06-19 20:59 ` [PATCH bpf-next v2 07/17] bpf: Track verifier context " Kumar Kartikeya Dwivedi
2026-06-19 21:13   ` sashiko-bot
2026-06-19 21:19     ` Kumar Kartikeya Dwivedi
2026-06-19 21:46   ` bot+bpf-ci
2026-06-19 20:59 ` [PATCH bpf-next v2 08/17] bpf: Report Register Type Safety errors Kumar Kartikeya Dwivedi
2026-06-19 20:59 ` [PATCH bpf-next v2 09/17] bpf: Report Memory Safety bounds errors Kumar Kartikeya Dwivedi
2026-06-19 21:46   ` bot+bpf-ci
2026-06-19 23:40   ` Alexei Starovoitov
2026-06-19 20:59 ` [PATCH bpf-next v2 10/17] bpf: Report Resource Lifetime reference leaks Kumar Kartikeya Dwivedi
2026-06-19 21:12   ` sashiko-bot
2026-06-19 23:42   ` Alexei Starovoitov
2026-06-19 20:59 ` [PATCH bpf-next v2 11/17] bpf: Report Call Type Safety argument errors Kumar Kartikeya Dwivedi
2026-06-19 21:47   ` bot+bpf-ci
2026-06-19 20:59 ` [PATCH bpf-next v2 12/17] bpf: Report Execution Context Safety errors Kumar Kartikeya Dwivedi
2026-06-19 21:19   ` sashiko-bot
2026-06-19 23:44   ` Alexei Starovoitov
2026-06-19 20:59 ` [PATCH bpf-next v2 13/17] bpf: Report Program Structure CFG errors Kumar Kartikeya Dwivedi
2026-06-19 20:59 ` [PATCH bpf-next v2 14/17] bpf: Report Policy helper and kfunc errors Kumar Kartikeya Dwivedi
2026-06-19 20:59 ` Kumar Kartikeya Dwivedi [this message]
2026-06-19 20:59 ` [PATCH bpf-next v2 16/17] bpf: Report Verifier Internal errors Kumar Kartikeya Dwivedi
2026-06-19 20:59 ` [PATCH bpf-next v2 17/17] bpf: Gate verifier diagnostics on log level 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=20260619205934.1312876-16-memxor@gmail.com \
    --to=memxor@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=emil@etsalapatis.com \
    --cc=kernel-team@meta.com \
    --cc=kkd@meta.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.