AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/12 22:38 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch only adds statistics tracking for the number of instructions processed per subprogram during BPF verification and outputs them in the verifier log. It does not alter the verification logic, execution flow, or the generated BPF code.",
  "WorthFuzzing": false
}

1/1 2026/08/12 22:38 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 27621998e77a798ff98296593e7292dec5728410\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Wed Aug 12 22:38:15 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h\nindex 93f7c2075eeaa..7c376451db825 100644\n--- a/include/linux/bpf_verifier.h\n+++ b/include/linux/bpf_verifier.h\n@@ -380,6 +380,8 @@ struct bpf_func_state {\n \t *                           | number of simulations is tracked in frame N\n \t */\n \tu32 callback_depth;\n+\t/* Instructions processed in this frame and callees on the current path. */\n+\tu32 insns_subtotal;\n \n \t/* The following fields should be last. See copy_func_state() */\n \t/* The state of the stack. Each element of the array describes BPF_REG_SIZE\n@@ -798,7 +800,8 @@ struct bpf_subprog_info {\n \tu32 exit_idx; /* Index of one of the BPF_EXIT instructions in this subprogram */\n \tu16 stack_depth; /* max. stack depth used by this function */\n \tu16 stack_extra;\n-\tu32 insn_processed;\n+\tu32 insns_total;\n+\tu32 insns_self;\n \t/* offsets in range [stack_depth .. fastcall_stack_off)\n \t * are used for bpf_fastcall spills and fills.\n \t */\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex add3affc57035..820d2831f3403 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -1593,6 +1593,8 @@ static int copy_func_state(struct bpf_func_state *dst,\n \t\t\t   const struct bpf_func_state *src)\n {\n \tmemcpy(dst, src, offsetof(struct bpf_func_state, stack));\n+\t/* Instruction accounting is path-local, not part of verifier state. */\n+\tdst-\u003einsns_subtotal = 0;\n \treturn copy_stack_state(dst, src);\n }\n \n@@ -9717,6 +9719,42 @@ static int set_task_work_schedule_callback_state(struct bpf_verifier_env *env,\n \n static bool is_rbtree_lock_required_kfunc(u32 btf_id);\n \n+static void account_processed_insn(struct bpf_verifier_env *env)\n+{\n+\tstruct bpf_func_state *frame = cur_func(env);\n+\n+\tenv-\u003einsn_processed++;\n+\tframe-\u003einsns_subtotal++;\n+\tenv-\u003esubprog_info[frame-\u003esubprogno].insns_self++;\n+}\n+\n+static void account_processed_insns(struct bpf_verifier_env *env,\n+\t\t\t\t    struct bpf_func_state *callee,\n+\t\t\t\t    struct bpf_func_state *caller)\n+{\n+\tu32 insns;\n+\n+\tif (!callee)\n+\t\treturn;\n+\n+\tinsns = callee-\u003einsns_subtotal;\n+\n+\tenv-\u003esubprog_info[callee-\u003esubprogno].insns_total += insns;\n+\tif (caller)\n+\t\tcaller-\u003einsns_subtotal += insns;\n+\tcallee-\u003einsns_subtotal = 0;\n+}\n+\n+static void account_current_path(struct bpf_verifier_env *env)\n+{\n+\tstruct bpf_verifier_state *state = env-\u003ecur_state;\n+\tint frame;\n+\n+\tfor (frame = state-\u003ecurframe; frame \u003e= 0; frame--)\n+\t\taccount_processed_insns(env, state-\u003eframe[frame],\n+\t\t\t\t\tframe ? state-\u003eframe[frame - 1] : NULL);\n+}\n+\n /* Are we currently verifying the callback for a rbtree helper that must\n  * be called with lock held? If so, no need to complain about unreleased\n  * lock\n@@ -9813,6 +9851,7 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)\n \t\tverbose(env, \"to caller at %d:\\n\", *insn_idx);\n \t\tprint_verifier_state(env, state, caller-\u003eframeno, true);\n \t}\n+\taccount_processed_insns(env, callee, caller);\n \t/* clear everything in the callee. In case of exceptional exits using\n \t * bpf_throw, this will be done by copy_verifier_state for extra frames. */\n \tfree_func_state(callee);\n@@ -17368,7 +17407,9 @@ static int do_check(struct bpf_verifier_env *env)\n \t\tinsn = \u0026insns[env-\u003einsn_idx];\n \t\tinsn_aux = \u0026env-\u003einsn_aux_data[env-\u003einsn_idx];\n \n-\t\tif (++env-\u003einsn_processed \u003e BPF_COMPLEXITY_LIMIT_INSNS) {\n+\t\taccount_processed_insn(env);\n+\n+\t\tif (env-\u003einsn_processed \u003e BPF_COMPLEXITY_LIMIT_INSNS) {\n \t\t\tverbose(env,\n \t\t\t\t\"BPF program is too large. Processed %d insn\\n\",\n \t\t\t\tenv-\u003einsn_processed);\n@@ -17509,6 +17550,7 @@ static int do_check(struct bpf_verifier_env *env)\n \t\t\t\t\t    \"speculation barrier after jump instruction may not have the desired effect\"))\n \t\t\t\treturn -EFAULT;\n process_bpf_exit:\n+\t\t\taccount_current_path(env);\n \t\t\tmark_verifier_state_scratched(env);\n \t\t\terr = bpf_update_branch_counts(env, env-\u003ecur_state);\n \t\t\tif (err)\n@@ -18415,6 +18457,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)\n \tstruct bpf_prog_aux *aux = env-\u003eprog-\u003eaux;\n \tstruct bpf_verifier_state *state;\n \tstruct bpf_reg_state *regs;\n+\tu32 insn_processed = env-\u003einsn_processed;\n \tint ret, i;\n \n \tenv-\u003eprev_linfo = NULL;\n@@ -18553,9 +18596,19 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)\n \n \tret = do_check(env);\n out:\n+\taccount_current_path(env);\n \tif (!ret \u0026\u0026 pop_log)\n \t\tbpf_vlog_reset(\u0026env-\u003elog, 0);\n \tfree_states(env);\n+\n+\t/*\n+\t * The override is needed to account for async subprograms, which\n+\t * are verified with their own set of stack frames and thus are\n+\t * not accounted as callees by account_current_path().\n+\t * Accumulate their total counts as total counts of the main or\n+\t * global subprog hosting the async call.\n+\t */\n+\tenv-\u003esubprog_info[subprog].insns_total = env-\u003einsn_processed - insn_processed;\n \treturn ret;\n }\n \n@@ -18584,7 +18637,6 @@ static int do_check_subprogs(struct bpf_verifier_env *env)\n \tstruct bpf_prog_aux *aux = env-\u003eprog-\u003eaux;\n \tstruct bpf_func_info_aux *sub_aux;\n \tint i, ret, new_cnt;\n-\tu32 insn_processed;\n \n \tif (!aux-\u003efunc_info)\n \t\treturn 0;\n@@ -18599,8 +18651,6 @@ static int do_check_subprogs(struct bpf_verifier_env *env)\n \t\tif (!bpf_subprog_is_global(env, i))\n \t\t\tcontinue;\n \n-\t\tinsn_processed = env-\u003einsn_processed;\n-\n \t\tsub_aux = subprog_aux(env, i);\n \t\tif (!sub_aux-\u003ecalled || sub_aux-\u003everified)\n \t\t\tcontinue;\n@@ -18608,7 +18658,6 @@ static int do_check_subprogs(struct bpf_verifier_env *env)\n \t\tenv-\u003einsn_idx = env-\u003esubprog_info[i].start;\n \t\tWARN_ON_ONCE(env-\u003einsn_idx == 0);\n \t\tret = do_check_common(env, i);\n-\t\tenv-\u003esubprog_info[i].insn_processed = env-\u003einsn_processed - insn_processed;\n \t\tif (ret) {\n \t\t\treturn ret;\n \t\t} else if (env-\u003elog.level \u0026 BPF_LOG_LEVEL) {\n@@ -18635,12 +18684,10 @@ static int do_check_subprogs(struct bpf_verifier_env *env)\n \n static int do_check_main(struct bpf_verifier_env *env)\n {\n-\tu32 insn_processed = env-\u003einsn_processed;\n \tint ret;\n \n \tenv-\u003einsn_idx = 0;\n \tret = do_check_common(env, 0);\n-\tenv-\u003esubprog_info[0].insn_processed = env-\u003einsn_processed - insn_processed;\n \tif (!ret)\n \t\tenv-\u003eprog-\u003eaux-\u003estack_depth = env-\u003esubprog_info[0].stack_depth;\n \treturn ret;\n@@ -18655,15 +18702,20 @@ static void print_verification_stats(struct bpf_verifier_env *env)\n \tif (env-\u003elog.level \u0026 BPF_LOG_STATS) {\n \t\tverbose(env, \"verification time %lld usec\\n\",\n \t\t\tdiv_u64(env-\u003everification_time, 1000));\n-\t\tverbose(env, \"stack depth %d\", env-\u003esubprog_info[0].stack_depth);\n-\t\tfor (i = 1; i \u003c subprog_cnt; i++)\n-\t\t\tverbose(env, \"+%d\", env-\u003esubprog_info[i].stack_depth);\n-\t\tverbose(env, \" max %d\\n\", env-\u003emax_stack_depth);\n-\t\tverbose(env, \"insns processed %d\", env-\u003esubprog_info[0].insn_processed);\n-\t\tfor (i = 1; i \u003c subprog_cnt; i++)\n-\t\t\tif (bpf_subprog_is_global(env, i))\n-\t\t\t\tverbose(env, \"+%d\", env-\u003esubprog_info[i].insn_processed);\n-\t\tverbose(env, \"\\n\");\n+\t\tverbose(env, \"stack depth max %d\\n\", env-\u003emax_stack_depth);\n+\t\tfor (i = 0; i \u003c subprog_cnt; i++) {\n+\t\t\tconst char *name = env-\u003esubprog_info[i].name;\n+\t\t\tconst char *kind;\n+\n+\t\t\tif (!name || !name[0])\n+\t\t\t\tname = \"\u003cunknown\u003e\";\n+\t\t\tkind = i == 0 ? \"main\" :\n+\t\t\t       bpf_subprog_is_global(env, i) ? \"global\" : \"static\";\n+\t\t\tverbose(env, \"subprog %d (%s) %s insns_self %d insns_total %d stack %d\\n\",\n+\t\t\t\ti, name, kind, env-\u003esubprog_info[i].insns_self,\n+\t\t\t\tenv-\u003esubprog_info[i].insns_total,\n+\t\t\t\tenv-\u003esubprog_info[i].stack_depth);\n+\t\t}\n \t}\n \tverbose(env, \"processed %d insns (limit %d) max_states_per_insn %d \"\n \t\t\"total_states %d peak_states %d mark_read %d\\n\",\ndiff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c\nindex 5b265af3b1d57..8113fea7ba86c 100644\n--- a/tools/testing/selftests/bpf/prog_tests/verifier.c\n+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c\n@@ -102,6 +102,7 @@\n #include \"verifier_stack_arg_order.skel.h\"\n #include \"verifier_stack_ptr.skel.h\"\n #include \"verifier_store_release.skel.h\"\n+#include \"verifier_subprog_insn_stats.skel.h\"\n #include \"verifier_subprog_precision.skel.h\"\n #include \"verifier_subprog_topo.skel.h\"\n #include \"verifier_subreg.skel.h\"\n@@ -262,6 +263,7 @@ void test_verifier_stack_arg(void)            { RUN(verifier_stack_arg); }\n void test_verifier_stack_arg_order(void)      { RUN(verifier_stack_arg_order); }\n void test_verifier_stack_ptr(void)            { RUN(verifier_stack_ptr); }\n void test_verifier_store_release(void)        { RUN(verifier_store_release); }\n+void test_verifier_subprog_insn_stats(void)   { RUN(verifier_subprog_insn_stats); }\n void test_verifier_subprog_precision(void)    { RUN(verifier_subprog_precision); }\n void test_verifier_subprog_topo(void)        { RUN(verifier_subprog_topo); }\n void test_verifier_subreg(void)               { RUN(verifier_subreg); }\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_basic_stack.c b/tools/testing/selftests/bpf/progs/verifier_basic_stack.c\nindex d3df7a9f1d8c8..0eb495ce85c1c 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_basic_stack.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_basic_stack.c\n@@ -27,7 +27,8 @@ __naked void stack_out_of_bounds(void)\n \n SEC(\"socket\")\n __description(\"uninitialized stack1\")\n-__success __log_level(4) __msg(\"stack depth 8\")\n+__success __log_level(4)\n+__msg(\"subprog 0 (uninitialized_stack1) main {{.*}} stack 8\")\n __failure_unpriv __msg_unpriv(\"invalid read from stack\")\n __naked void uninitialized_stack1(void)\n {\n@@ -45,7 +46,8 @@ __naked void uninitialized_stack1(void)\n \n SEC(\"socket\")\n __description(\"uninitialized stack2\")\n-__success __log_level(4) __msg(\"stack depth 8\")\n+__success __log_level(4)\n+__msg(\"subprog 0 (uninitialized_stack2) main insns_self {{[0-9]+}} insns_total {{[0-9]+}} stack 8\")\n __failure_unpriv __msg_unpriv(\"invalid read from stack\")\n __naked void uninitialized_stack2(void)\n {\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c b/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c\nindex 83707faea0499..4cfaa6b4ab405 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c\n@@ -10,7 +10,8 @@\n \n SEC(\"raw_tp\")\n __arch_x86_64\n-__log_level(4) __msg(\"stack depth 8\")\n+__log_level(4)\n+__msg(\"subprog 0 (simple) main insns_self {{[0-9]+}} insns_total {{[0-9]+}} stack 8\")\n __xlated(\"4: r5 = 5\")\n __xlated(\"5: r0 = \")\n __xlated(\"6: r0 = \u0026(void __percpu *)(r0)\")\n@@ -96,7 +97,8 @@ __naked void canary_zero_spills(void)\n \n SEC(\"raw_tp\")\n __arch_x86_64\n-__log_level(4) __msg(\"stack depth 16\")\n+__log_level(4)\n+__msg(\"subprog 0 (wrong_reg_in_pattern1) main {{.*}} stack 16\")\n __xlated(\"1: *(u64 *)(r10 -16) = r1\")\n __xlated(\"...\")\n __xlated(\"3: r0 = \u0026(void __percpu *)(r0)\")\n@@ -598,7 +600,8 @@ __naked static void subprogs_use_independent_offsets_aux(void)\n \n SEC(\"raw_tp\")\n __arch_x86_64\n-__log_level(4) __msg(\"stack depth 8\")\n+__log_level(4)\n+__msg(\"subprog 0 (helper_call_does_not_prevent_bpf_fastcall) main {{.*}} stack 8\")\n __xlated(\"2: r0 = \u0026(void __percpu *)(r0)\")\n __success\n __naked void helper_call_does_not_prevent_bpf_fastcall(void)\n@@ -620,7 +623,8 @@ __naked void helper_call_does_not_prevent_bpf_fastcall(void)\n \n SEC(\"raw_tp\")\n __arch_x86_64\n-__log_level(4) __msg(\"stack depth 24\")\n+__log_level(4)\n+__msg(\"subprog 0 (may_goto_interaction_x86_64) main {{.*}} stack 24\")\n /* may_goto counter at -24 */\n __xlated(\"0: *(u64 *)(r10 -24) =\")\n /* may_goto timestamp at -16 */\n@@ -661,7 +665,8 @@ __naked void may_goto_interaction_x86_64(void)\n SEC(\"raw_tp\")\n __arch_arm64\n __arch_riscv64\n-__log_level(4) __msg(\"stack depth 24\")\n+__log_level(4)\n+__msg(\"subprog 0 (may_goto_interaction) main {{.*}} stack 24\")\n /* may_goto counter at -24 */\n __xlated(\"0: *(u64 *)(r10 -24) =\")\n /* may_goto timestamp at -16 */\n@@ -708,7 +713,9 @@ __naked static void dummy_loop_callback(void)\n \n SEC(\"raw_tp\")\n __arch_x86_64\n-__log_level(4) __msg(\"stack depth 32+0\")\n+__log_level(4)\n+__msg(\"subprog 0 (bpf_loop_interaction1) main {{.*}} stack 32\")\n+__msg(\"subprog 1 (dummy_loop_callback) static {{.*}} stack 0\")\n __xlated(\"2: r1 = 1\")\n __xlated(\"3: r0 =\")\n __xlated(\"4: r0 = \u0026(void __percpu *)(r0)\")\n@@ -756,7 +763,9 @@ __naked int bpf_loop_interaction1(void)\n \n SEC(\"raw_tp\")\n __arch_x86_64\n-__log_level(4) __msg(\"stack depth 40+0\")\n+__log_level(4)\n+__msg(\"subprog 0 (bpf_loop_interaction2) main {{.*}} stack 40\")\n+__msg(\"subprog 1 (dummy_loop_callback) static {{.*}} stack 0\")\n /* call bpf_get_smp_processor_id */\n __xlated(\"2: r1 = 42\")\n __xlated(\"3: r0 =\")\n@@ -800,7 +809,10 @@ __naked int bpf_loop_interaction2(void)\n \n SEC(\"raw_tp\")\n __arch_x86_64\n-__log_level(4) __msg(\"stack depth 512+0 max 512\")\n+__log_level(4)\n+__msg(\"stack depth max 512\")\n+__msg(\"subprog 0 (cumulative_stack_depth) main {{.*}} stack 512\")\n+__msg(\"subprog 1 (cumulative_stack_depth_subprog) static {{.*}} stack 0\")\n /* just to print xlated version when debugging */\n __xlated(\"r0 = \u0026(void __percpu *)(r0)\")\n __success\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c\nindex 67dc352addfd7..7b65eea97ebc2 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c\n@@ -52,7 +52,10 @@ __msg(\"('global_calls_good_only') is global and assumed valid.\")\n /* eventually global_good() is transitively validated as well */\n __msg(\"Validating global_good() func\")\n __msg(\"('global_good') is safe for any args that match its prototype\")\n-__msg(\"insns processed {{[0-9]+\\\\+[0-9]+\\\\+[0-9]+$}}\")\n+__msg(\"subprog 0 (chained_global_func_calls_success) main insns_self 7 insns_total 7 stack\")\n+__msg(\"subprog {{[0-9]+}} (global_calls_good_only) global insns_self 2 insns_total 2 stack\")\n+__msg(\"subprog {{[0-9]+}} (global_good) global insns_self 5 insns_total 5 stack\")\n+__msg(\"processed 14 insns\")\n int chained_global_func_calls_success(void)\n {\n \tint sum = 0;\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_private_stack.c b/tools/testing/selftests/bpf/progs/verifier_private_stack.c\nindex bb8206e10880c..ea0a7e73331da 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_private_stack.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_private_stack.c\n@@ -86,7 +86,9 @@ __naked static void cumulative_stack_depth_subprog(void)\n SEC(\"kprobe\")\n __description(\"Private stack, subtree \u003e MAX_BPF_STACK\")\n __success\n-__log_level(4) __msg(\"stack depth 512+32 max 512\")\n+__log_level(4) __msg(\"stack depth max 512\")\n+__msg(\"subprog 0 (private_stack_nested_1) main {{.*}} stack 512\")\n+__msg(\"subprog 1 (cumulative_stack_depth_subprog) static {{.*}} stack 32\")\n __arch_x86_64\n /* private stack fp for the main prog */\n __jited(\"\tmovabsq\t$0x{{.*}}, %r9\")\n@@ -331,7 +333,11 @@ SEC(\"fentry/bpf_fentry_test9\")\n __description(\"Private stack, async callback, potential nesting\")\n __success __retval(0)\n __load_if_JITed()\n-__log_level(4) __msg(\"stack depth 8+0+256+0 max 272\")\n+__log_level(4) __msg(\"stack depth max 272\")\n+__msg(\"subprog 0 (private_stack_async_callback_2) main {{.*}} stack 8\")\n+__msg(\"subprog 1 (timer_cb1) static {{.*}} stack 0\")\n+__msg(\"subprog 2 (subprog1) static {{.*}} stack 256\")\n+__msg(\"subprog 3 (subprog2) static {{.*}} stack 0\")\n __arch_x86_64\n __jited(\"\tsubq\t$0x100, %rsp\")\n __arch_arm64\n@@ -355,7 +361,10 @@ int private_stack_async_callback_2(void)\n SEC(\"fentry/bpf_fentry_test9\")\n __description(\"private stack, max stack depth is private stack\")\n __success\n-__log_level(4) __msg(\"stack depth 8+256+0 max 256\")\n+__log_level(4) __msg(\"stack depth max 256\")\n+__msg(\"subprog 0 (private_stack_max_depth) main {{.*}} stack 8\")\n+__msg(\"subprog 1 (subprog1) static insns_self {{[0-9]+}} insns_total {{[0-9]+}} stack 256\")\n+__msg(\"subprog 2 (subprog2) static insns_self {{[0-9]+}} insns_total {{[0-9]+}} stack 0\")\n int private_stack_max_depth(void)\n {\n \tint x = 0;\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_subprog_insn_stats.c b/tools/testing/selftests/bpf/progs/verifier_subprog_insn_stats.c\nnew file mode 100644\nindex 0000000000000..8f6082fdb5c84\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/verifier_subprog_insn_stats.c\n@@ -0,0 +1,223 @@\n+// SPDX-License-Identifier: GPL-2.0\n+\n+#include \u003cvmlinux.h\u003e\n+#include \u003cbpf/bpf_helpers.h\u003e\n+#include \"bpf_misc.h\"\n+\n+struct timer_value {\n+\tstruct bpf_timer timer;\n+};\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_ARRAY);\n+\t__uint(max_entries, 1);\n+\t__type(key, __u32);\n+\t__type(value, struct timer_value);\n+} timer_map SEC(\".maps\");\n+\n+SEC(\"?raw_tp\")\n+__success __log_level(4)\n+__msg(\"subprog 0 (stats_main_only) main insns_self 2 insns_total 2 stack 0\")\n+__msg(\"processed 2 insns\")\n+__naked int stats_main_only(void)\n+{\n+\tasm volatile (\n+\t\t\"r0 = 0;\"\n+\t\t\"exit;\"\n+\t);\n+}\n+\n+__naked __noinline __used\n+static int stats_chain_leaf(void)\n+{\n+\tasm volatile (\n+\t\t\"r0 = 0;\"\n+\t\t\"exit;\"\n+\t);\n+}\n+\n+__naked __noinline __used\n+static int stats_chain_parent(void)\n+{\n+\tasm volatile (\n+\t\t\"call stats_chain_leaf;\"\n+\t\t\"exit;\"\n+\t);\n+}\n+\n+SEC(\"?raw_tp\")\n+__success __log_level(4)\n+/*\n+ * self: 2 + 2 + 2 = 6\n+ * totals: leaf 2, parent 2 + 2 = 4, main 2 + 4 = 6\n+ */\n+__msg(\"subprog 0 (stats_static_chain) main insns_self 2 insns_total 6 stack 0\")\n+__msg(\"subprog {{[0-9]+}} (stats_chain_parent) static insns_self 2 insns_total 4 stack 0\")\n+__msg(\"subprog {{[0-9]+}} (stats_chain_leaf) static insns_self 2 insns_total 2 stack 0\")\n+__msg(\"processed 6 insns\")\n+__naked int stats_static_chain(void)\n+{\n+\tasm volatile (\n+\t\t\"call stats_chain_parent;\"\n+\t\t\"exit;\"\n+\t);\n+}\n+\n+__naked __noinline __used\n+static int stats_shared_leaf(void)\n+{\n+\tasm volatile (\n+\t\t\"r0 = 0;\"\n+\t\t\"exit;\"\n+\t);\n+}\n+\n+__naked __noinline __used\n+int stats_global_root(void)\n+{\n+\tasm volatile (\n+\t\t\"call stats_shared_leaf;\"\n+\t\t\"exit;\"\n+\t);\n+}\n+\n+SEC(\"?raw_tp\")\n+__success __log_level(4)\n+/*\n+ * stats_shared_leaf is explored once under each independent root.\n+ * self: main 3 + leaf 4 + global 2 = 9\n+ * root totals: main 5 + global 4 = 9\n+ */\n+__msg(\"subprog 0 (stats_shared_roots) main insns_self 3 insns_total 5 stack 0\")\n+__msg(\"subprog {{[0-9]+}} (stats_shared_leaf) static insns_self 4 insns_total 4 stack 0\")\n+__msg(\"subprog {{[0-9]+}} (stats_global_root) global insns_self 2 insns_total 4 stack 0\")\n+__msg(\"processed 9 insns\")\n+__naked int stats_shared_roots(void)\n+{\n+\tasm volatile (\n+\t\t\"call stats_shared_leaf;\"\n+\t\t\"call stats_global_root;\"\n+\t\t\"exit;\"\n+\t);\n+}\n+\n+__noinline __used\n+static int stats_async_leaf(void *map, __u32 *key, struct bpf_timer *timer)\n+{\n+\treturn 0;\n+}\n+\n+__noinline __used\n+static __u64 stats_async_schedule(struct bpf_map *map, __u32 *key,\n+\t\t\t\t  struct timer_value *value, void *ctx)\n+{\n+\tasm volatile (\n+\t\t\"r1 = %[timer];\"\n+\t\t\"r2 = %[stats_async_leaf];\"\n+\t\t\"call %[bpf_timer_set_callback];\"\n+\t\t:\n+\t\t: [timer] \"r\" (value),\n+\t\t  __imm_ptr(stats_async_leaf),\n+\t\t  __imm(bpf_timer_set_callback)\n+\t\t: __clobber_common\n+\t);\n+\treturn 0;\n+}\n+\n+SEC(\"?raw_tp\")\n+__success __log_level(4)\n+/*\n+ * self: 9 + 7 + 2 = 18\n+ * totals: leaf 2, scheduler 7, main root 18\n+ */\n+__msg(\"subprog 0 (stats_async_direct) main insns_self 9 insns_total 18 stack 0\")\n+__msg(\"subprog {{[0-9]+}} (stats_async_schedule) static insns_self 7 insns_total 7 stack 0\")\n+__msg(\"subprog {{[0-9]+}} (stats_async_leaf) static insns_self 2 insns_total 2 stack 0\")\n+__msg(\"processed 18 insns\")\n+__naked int stats_async_direct(void)\n+{\n+\tasm volatile (\n+\t\t\"r1 = %[timer_map] ll;\"\n+\t\t\"r2 = %[stats_async_schedule];\"\n+\t\t\"r3 = 0;\"\n+\t\t\"r4 = 0;\"\n+\t\t\"call %[bpf_for_each_map_elem];\"\n+\t\t\"r0 = 0;\"\n+\t\t\"exit;\"\n+\t\t:\n+\t\t: __imm_addr(timer_map),\n+\t\t  __imm_ptr(stats_async_schedule),\n+\t\t  __imm(bpf_for_each_map_elem)\n+\t\t: __clobber_common\n+\t);\n+}\n+\n+__noinline __used\n+static int stats_async_nested_leaf(void *map, __u32 *key, struct bpf_timer *timer)\n+{\n+\treturn 0;\n+}\n+\n+__noinline __used\n+static int stats_async_outer(void *map, __u32 *key, struct bpf_timer *timer)\n+{\n+\tasm volatile (\n+\t\t\"r1 = %[timer];\"\n+\t\t\"r2 = %[stats_async_nested_leaf];\"\n+\t\t\"call %[bpf_timer_set_callback];\"\n+\t\t:\n+\t\t: [timer] \"r\" (timer),\n+\t\t  __imm_ptr(stats_async_nested_leaf),\n+\t\t  __imm(bpf_timer_set_callback)\n+\t\t: __clobber_common\n+\t);\n+\treturn 0;\n+}\n+\n+__noinline __used\n+static __u64 stats_async_nested_schedule(struct bpf_map *map, __u32 *key,\n+\t\t\t\t\t struct timer_value *value, void *ctx)\n+{\n+\tasm volatile (\n+\t\t\"r1 = %[timer];\"\n+\t\t\"r2 = %[stats_async_outer];\"\n+\t\t\"call %[bpf_timer_set_callback];\"\n+\t\t:\n+\t\t: [timer] \"r\" (value),\n+\t\t  __imm_ptr(stats_async_outer),\n+\t\t  __imm(bpf_timer_set_callback)\n+\t\t: __clobber_common\n+\t);\n+\treturn 0;\n+}\n+\n+SEC(\"?raw_tp\")\n+__success __log_level(4)\n+/*\n+ * self: 9 + 7 + 7 + 2 = 25\n+ * totals: leaf 2, outer 7, scheduler 7, main root 25\n+ */\n+__msg(\"subprog 0 (stats_async_nested) main insns_self 9 insns_total 25 stack 0\")\n+__msg(\"subprog {{[0-9]+}} (stats_async_nested_schedule) static insns_self 7 insns_total 7 stack 0\")\n+__msg(\"subprog {{[0-9]+}} (stats_async_outer) static insns_self 7 insns_total 7 stack 0\")\n+__msg(\"subprog {{[0-9]+}} (stats_async_nested_leaf) static insns_self 2 insns_total 2 stack 0\")\n+__msg(\"processed 25 insns\")\n+__naked int stats_async_nested(void)\n+{\n+\tasm volatile (\n+\t\t\"r1 = %[timer_map] ll;\"\n+\t\t\"r2 = %[stats_async_nested_schedule];\"\n+\t\t\"r3 = 0;\"\n+\t\t\"r4 = 0;\"\n+\t\t\"call %[bpf_for_each_map_elem];\"\n+\t\t\"r0 = 0;\"\n+\t\t\"exit;\"\n+\t\t:\n+\t\t: __imm_addr(timer_map),\n+\t\t  __imm_ptr(stats_async_nested_schedule),\n+\t\t  __imm(bpf_for_each_map_elem)\n+\t\t: __clobber_common\n+\t);\n+}\n+\n+char _license[] SEC(\"license\") = \"GPL\";\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_var_off.c b/tools/testing/selftests/bpf/progs/verifier_var_off.c\nindex 24cd0a7636730..a63e336750918 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_var_off.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_var_off.c\n@@ -198,7 +198,8 @@ __success\n /* Check that the maximum stack depth is correctly maintained according to the\n  * maximum possible variable offset.\n  */\n-__log_level(4) __msg(\"stack depth 16\")\n+__log_level(4)\n+__msg(\"subprog 0 (stack_write_priv_vs_unpriv) main {{.*}} stack 16\")\n __failure_unpriv\n /* Variable stack access is rejected for unprivileged.\n  */\n@@ -238,7 +239,8 @@ __success\n /* Check that the maximum stack depth is correctly maintained according to the\n  * maximum possible variable offset.\n  */\n-__log_level(4) __msg(\"stack depth 16\")\n+__log_level(4)\n+__msg(\"subprog 0 (stack_write_followed_by_read) main {{.*}} stack 16\")\n __failure_unpriv\n __msg_unpriv(\"R2 variable stack access prohibited for !root\")\n __retval(0)\ndiff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c\nindex a8ae03c57bbae..bffb7360434c5 100644\n--- a/tools/testing/selftests/bpf/test_verifier.c\n+++ b/tools/testing/selftests/bpf/test_verifier.c\n@@ -1560,7 +1560,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,\n \n \topts.expected_attach_type = test-\u003eexpected_attach_type;\n \tif (expected_ret == VERBOSE_ACCEPT)\n-\t\topts.log_level = 2;\n+\t\topts.log_level = 2 | 4;\n \telse if (verbose)\n \t\topts.log_level = verif_log_level | 4; /* force stats */\n \telse\ndiff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c\nindex 8cd626e045515..eb6e3baef412a 100644\n--- a/tools/testing/selftests/bpf/verifier/calls.c\n+++ b/tools/testing/selftests/bpf/verifier/calls.c\n@@ -1091,7 +1091,17 @@\n \t/* stack_main=32, stack_A=256, stack_B=64\n \t * and max(main+A, main+A+B) \u003c 512\n \t */\n-\t.result = ACCEPT,\n+\t.result = VERBOSE_ACCEPT,\n+\t.errstr = \"stack depth max 352\\t\"\n+\t\t  \"subprog 0 (\u003cunknown\u003e) main insns_self \\t\"\n+\t\t  \" insns_total \\t\"\n+\t\t  \" stack 32\\t\"\n+\t\t  \"subprog 1 (\u003cunknown\u003e) static insns_self \\t\"\n+\t\t  \" insns_total \\t\"\n+\t\t  \" stack 256\\t\"\n+\t\t  \"subprog 2 (\u003cunknown\u003e) static insns_self \\t\"\n+\t\t  \" insns_total \\t\"\n+\t\t  \" stack 64\",\n },\n {\n \t\"calls: stack depth check using three frames. test2\",\ndiff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c\nindex c9c257784ee3d..5c0ec3edce720 100644\n--- a/tools/testing/selftests/bpf/veristat.c\n+++ b/tools/testing/selftests/bpf/veristat.c\n@@ -993,13 +993,15 @@ static void free_verif_stats(struct verif_stats *stats, size_t stat_cnt)\n \n static char verif_log_buf[64 * 1024];\n \n-#define MAX_PARSED_LOG_LINES 100\n+/* Keep room for all 256 subprogram records and trailing statistics. */\n+#define MAX_PARSED_LOG_LINES 300\n \n static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *s)\n {\n \tconst char *cur;\n-\tint pos, lines, sub_stack, cnt = 0;\n-\tchar *state = NULL, *token, stack[512];\n+\tlong sub_stack;\n+\tint pos, lines, cnt = 0;\n+\tchar *state = NULL, *token, stack[512] = {};\n \n \tbuf[buf_sz - 1] = '\\0';\n \n@@ -1025,11 +1027,24 @@ static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *\n \t\t\t\t\u0026s-\u003estats[MARK_READ_MAX_LEN]))\n \t\t\tcontinue;\n \n+\t\t/*\n+\t\t * New kernels emit one \"subprog \u003cid\u003e (\u003cname\u003e) \u003ckind\u003e\" record\n+\t\t * per subprogram with the stack depth at the end, while old\n+\t\t * kernels emit a single \"stack depth \u003ca+...+n\u003e max \u003cmax\u003e\"\n+\t\t * line. Match both formats so veristat works against either\n+\t\t * kernel.\n+\t\t */\n+\t\tif (sscanf(cur, \"stack depth max %ld\", \u0026s-\u003estats[MAX_STACK]) == 1)\n+\t\t\tcontinue;\n+\t\tif (sscanf(cur, \"subprog %*d %*s %*s insns_self %*d insns_total %*d stack %ld\", \u0026sub_stack) == 1) {\n+\t\t\ts-\u003estats[STACK] += sub_stack;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (2 == sscanf(cur, \"stack depth %511s max %ld\", stack, \u0026s-\u003estats[MAX_STACK]))\n \t\t\tcontinue;\n \t}\n \twhile ((token = strtok_r(cnt++ ? NULL : stack, \"+\", \u0026state))) {\n-\t\tif (sscanf(token, \"%d\", \u0026sub_stack) == 0)\n+\t\tif (sscanf(token, \"%ld\", \u0026sub_stack) == 0)\n \t\t\tbreak;\n \t\ts-\u003estats[STACK] += sub_stack;\n \t}\n"
}

2/1 2026/08/12 22:38 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds statistics tracking for the number of instructions processed per subprogram during BPF verification and outputs them in the verifier log. It does not alter the verification logic, execution flow, or the generated BPF code.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 27621998e77a798ff98296593e7292dec5728410
Author: syz-cluster <triage@syzkaller.com>
Date:   Wed Aug 12 22:38:15 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 93f7c2075eeaa..7c376451db825 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -380,6 +380,8 @@ struct bpf_func_state {
 	 *                           | number of simulations is tracked in frame N
 	 */
 	u32 callback_depth;
+	/* Instructions processed in this frame and callees on the current path. */
+	u32 insns_subtotal;
 
 	/* The following fields should be last. See copy_func_state() */
 	/* The state of the stack. Each element of the array describes BPF_REG_SIZE
@@ -798,7 +800,8 @@ struct bpf_subprog_info {
 	u32 exit_idx; /* Index of one of the BPF_EXIT instructions in this subprogram */
 	u16 stack_depth; /* max. stack depth used by this function */
 	u16 stack_extra;
-	u32 insn_processed;
+	u32 insns_total;
+	u32 insns_self;
 	/* offsets in range [stack_depth .. fastcall_stack_off)
 	 * are used for bpf_fastcall spills and fills.
 	 */
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index add3affc57035..820d2831f3403 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1593,6 +1593,8 @@ static int copy_func_state(struct bpf_func_state *dst,
 			   const struct bpf_func_state *src)
 {
 	memcpy(dst, src, offsetof(struct bpf_func_state, stack));
+	/* Instruction accounting is path-local, not part of verifier state. */
+	dst->insns_subtotal = 0;
 	return copy_stack_state(dst, src);
 }
 
@@ -9717,6 +9719,42 @@ static int set_task_work_schedule_callback_state(struct bpf_verifier_env *env,
 
 static bool is_rbtree_lock_required_kfunc(u32 btf_id);
 
+static void account_processed_insn(struct bpf_verifier_env *env)
+{
+	struct bpf_func_state *frame = cur_func(env);
+
+	env->insn_processed++;
+	frame->insns_subtotal++;
+	env->subprog_info[frame->subprogno].insns_self++;
+}
+
+static void account_processed_insns(struct bpf_verifier_env *env,
+				    struct bpf_func_state *callee,
+				    struct bpf_func_state *caller)
+{
+	u32 insns;
+
+	if (!callee)
+		return;
+
+	insns = callee->insns_subtotal;
+
+	env->subprog_info[callee->subprogno].insns_total += insns;
+	if (caller)
+		caller->insns_subtotal += insns;
+	callee->insns_subtotal = 0;
+}
+
+static void account_current_path(struct bpf_verifier_env *env)
+{
+	struct bpf_verifier_state *state = env->cur_state;
+	int frame;
+
+	for (frame = state->curframe; frame >= 0; frame--)
+		account_processed_insns(env, state->frame[frame],
+					frame ? state->frame[frame - 1] : NULL);
+}
+
 /* Are we currently verifying the callback for a rbtree helper that must
  * be called with lock held? If so, no need to complain about unreleased
  * lock
@@ -9813,6 +9851,7 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
 		verbose(env, "to caller at %d:\n", *insn_idx);
 		print_verifier_state(env, state, caller->frameno, true);
 	}
+	account_processed_insns(env, callee, caller);
 	/* clear everything in the callee. In case of exceptional exits using
 	 * bpf_throw, this will be done by copy_verifier_state for extra frames. */
 	free_func_state(callee);
@@ -17368,7 +17407,9 @@ static int do_check(struct bpf_verifier_env *env)
 		insn = &insns[env->insn_idx];
 		insn_aux = &env->insn_aux_data[env->insn_idx];
 
-		if (++env->insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) {
+		account_processed_insn(env);
+
+		if (env->insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) {
 			verbose(env,
 				"BPF program is too large. Processed %d insn\n",
 				env->insn_processed);
@@ -17509,6 +17550,7 @@ static int do_check(struct bpf_verifier_env *env)
 					    "speculation barrier after jump instruction may not have the desired effect"))
 				return -EFAULT;
 process_bpf_exit:
+			account_current_path(env);
 			mark_verifier_state_scratched(env);
 			err = bpf_update_branch_counts(env, env->cur_state);
 			if (err)
@@ -18415,6 +18457,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
 	struct bpf_prog_aux *aux = env->prog->aux;
 	struct bpf_verifier_state *state;
 	struct bpf_reg_state *regs;
+	u32 insn_processed = env->insn_processed;
 	int ret, i;
 
 	env->prev_linfo = NULL;
@@ -18553,9 +18596,19 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
 
 	ret = do_check(env);
 out:
+	account_current_path(env);
 	if (!ret && pop_log)
 		bpf_vlog_reset(&env->log, 0);
 	free_states(env);
+
+	/*
+	 * The override is needed to account for async subprograms, which
+	 * are verified with their own set of stack frames and thus are
+	 * not accounted as callees by account_current_path().
+	 * Accumulate their total counts as total counts of the main or
+	 * global subprog hosting the async call.
+	 */
+	env->subprog_info[subprog].insns_total = env->insn_processed - insn_processed;
 	return ret;
 }
 
@@ -18584,7 +18637,6 @@ static int do_check_subprogs(struct bpf_verifier_env *env)
 	struct bpf_prog_aux *aux = env->prog->aux;
 	struct bpf_func_info_aux *sub_aux;
 	int i, ret, new_cnt;
-	u32 insn_processed;
 
 	if (!aux->func_info)
 		return 0;
@@ -18599,8 +18651,6 @@ static int do_check_subprogs(struct bpf_verifier_env *env)
 		if (!bpf_subprog_is_global(env, i))
 			continue;
 
-		insn_processed = env->insn_processed;
-
 		sub_aux = subprog_aux(env, i);
 		if (!sub_aux->called || sub_aux->verified)
 			continue;
@@ -18608,7 +18658,6 @@ static int do_check_subprogs(struct bpf_verifier_env *env)
 		env->insn_idx = env->subprog_info[i].start;
 		WARN_ON_ONCE(env->insn_idx == 0);
 		ret = do_check_common(env, i);
-		env->subprog_info[i].insn_processed = env->insn_processed - insn_processed;
 		if (ret) {
 			return ret;
 		} else if (env->log.level & BPF_LOG_LEVEL) {
@@ -18635,12 +18684,10 @@ static int do_check_subprogs(struct bpf_verifier_env *env)
 
 static int do_check_main(struct bpf_verifier_env *env)
 {
-	u32 insn_processed = env->insn_processed;
 	int ret;
 
 	env->insn_idx = 0;
 	ret = do_check_common(env, 0);
-	env->subprog_info[0].insn_processed = env->insn_processed - insn_processed;
 	if (!ret)
 		env->prog->aux->stack_depth = env->subprog_info[0].stack_depth;
 	return ret;
@@ -18655,15 +18702,20 @@ static void print_verification_stats(struct bpf_verifier_env *env)
 	if (env->log.level & BPF_LOG_STATS) {
 		verbose(env, "verification time %lld usec\n",
 			div_u64(env->verification_time, 1000));
-		verbose(env, "stack depth %d", env->subprog_info[0].stack_depth);
-		for (i = 1; i < subprog_cnt; i++)
-			verbose(env, "+%d", env->subprog_info[i].stack_depth);
-		verbose(env, " max %d\n", env->max_stack_depth);
-		verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
-		for (i = 1; i < subprog_cnt; i++)
-			if (bpf_subprog_is_global(env, i))
-				verbose(env, "+%d", env->subprog_info[i].insn_processed);
-		verbose(env, "\n");
+		verbose(env, "stack depth max %d\n", env->max_stack_depth);
+		for (i = 0; i < subprog_cnt; i++) {
+			const char *name = env->subprog_info[i].name;
+			const char *kind;
+
+			if (!name || !name[0])
+				name = "<unknown>";
+			kind = i == 0 ? "main" :
+			       bpf_subprog_is_global(env, i) ? "global" : "static";
+			verbose(env, "subprog %d (%s) %s insns_self %d insns_total %d stack %d\n",
+				i, name, kind, env->subprog_info[i].insns_self,
+				env->subprog_info[i].insns_total,
+				env->subprog_info[i].stack_depth);
+		}
 	}
 	verbose(env, "processed %d insns (limit %d) max_states_per_insn %d "
 		"total_states %d peak_states %d mark_read %d\n",
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 5b265af3b1d57..8113fea7ba86c 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -102,6 +102,7 @@
 #include "verifier_stack_arg_order.skel.h"
 #include "verifier_stack_ptr.skel.h"
 #include "verifier_store_release.skel.h"
+#include "verifier_subprog_insn_stats.skel.h"
 #include "verifier_subprog_precision.skel.h"
 #include "verifier_subprog_topo.skel.h"
 #include "verifier_subreg.skel.h"
@@ -262,6 +263,7 @@ void test_verifier_stack_arg(void)            { RUN(verifier_stack_arg); }
 void test_verifier_stack_arg_order(void)      { RUN(verifier_stack_arg_order); }
 void test_verifier_stack_ptr(void)            { RUN(verifier_stack_ptr); }
 void test_verifier_store_release(void)        { RUN(verifier_store_release); }
+void test_verifier_subprog_insn_stats(void)   { RUN(verifier_subprog_insn_stats); }
 void test_verifier_subprog_precision(void)    { RUN(verifier_subprog_precision); }
 void test_verifier_subprog_topo(void)        { RUN(verifier_subprog_topo); }
 void test_verifier_subreg(void)               { RUN(verifier_subreg); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_basic_stack.c b/tools/testing/selftests/bpf/progs/verifier_basic_stack.c
index d3df7a9f1d8c8..0eb495ce85c1c 100644
--- a/tools/testing/selftests/bpf/progs/verifier_basic_stack.c
+++ b/tools/testing/selftests/bpf/progs/verifier_basic_stack.c
@@ -27,7 +27,8 @@ __naked void stack_out_of_bounds(void)
 
 SEC("socket")
 __description("uninitialized stack1")
-__success __log_level(4) __msg("stack depth 8")
+__success __log_level(4)
+__msg("subprog 0 (uninitialized_stack1) main {{.*}} stack 8")
 __failure_unpriv __msg_unpriv("invalid read from stack")
 __naked void uninitialized_stack1(void)
 {
@@ -45,7 +46,8 @@ __naked void uninitialized_stack1(void)
 
 SEC("socket")
 __description("uninitialized stack2")
-__success __log_level(4) __msg("stack depth 8")
+__success __log_level(4)
+__msg("subprog 0 (uninitialized_stack2) main insns_self {{[0-9]+}} insns_total {{[0-9]+}} stack 8")
 __failure_unpriv __msg_unpriv("invalid read from stack")
 __naked void uninitialized_stack2(void)
 {
diff --git a/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c b/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c
index 83707faea0499..4cfaa6b4ab405 100644
--- a/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c
+++ b/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c
@@ -10,7 +10,8 @@
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 8")
+__log_level(4)
+__msg("subprog 0 (simple) main insns_self {{[0-9]+}} insns_total {{[0-9]+}} stack 8")
 __xlated("4: r5 = 5")
 __xlated("5: r0 = ")
 __xlated("6: r0 = &(void __percpu *)(r0)")
@@ -96,7 +97,8 @@ __naked void canary_zero_spills(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 16")
+__log_level(4)
+__msg("subprog 0 (wrong_reg_in_pattern1) main {{.*}} stack 16")
 __xlated("1: *(u64 *)(r10 -16) = r1")
 __xlated("...")
 __xlated("3: r0 = &(void __percpu *)(r0)")
@@ -598,7 +600,8 @@ __naked static void subprogs_use_independent_offsets_aux(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 8")
+__log_level(4)
+__msg("subprog 0 (helper_call_does_not_prevent_bpf_fastcall) main {{.*}} stack 8")
 __xlated("2: r0 = &(void __percpu *)(r0)")
 __success
 __naked void helper_call_does_not_prevent_bpf_fastcall(void)
@@ -620,7 +623,8 @@ __naked void helper_call_does_not_prevent_bpf_fastcall(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 24")
+__log_level(4)
+__msg("subprog 0 (may_goto_interaction_x86_64) main {{.*}} stack 24")
 /* may_goto counter at -24 */
 __xlated("0: *(u64 *)(r10 -24) =")
 /* may_goto timestamp at -16 */
@@ -661,7 +665,8 @@ __naked void may_goto_interaction_x86_64(void)
 SEC("raw_tp")
 __arch_arm64
 __arch_riscv64
-__log_level(4) __msg("stack depth 24")
+__log_level(4)
+__msg("subprog 0 (may_goto_interaction) main {{.*}} stack 24")
 /* may_goto counter at -24 */
 __xlated("0: *(u64 *)(r10 -24) =")
 /* may_goto timestamp at -16 */
@@ -708,7 +713,9 @@ __naked static void dummy_loop_callback(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 32+0")
+__log_level(4)
+__msg("subprog 0 (bpf_loop_interaction1) main {{.*}} stack 32")
+__msg("subprog 1 (dummy_loop_callback) static {{.*}} stack 0")
 __xlated("2: r1 = 1")
 __xlated("3: r0 =")
 __xlated("4: r0 = &(void __percpu *)(r0)")
@@ -756,7 +763,9 @@ __naked int bpf_loop_interaction1(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 40+0")
+__log_level(4)
+__msg("subprog 0 (bpf_loop_interaction2) main {{.*}} stack 40")
+__msg("subprog 1 (dummy_loop_callback) static {{.*}} stack 0")
 /* call bpf_get_smp_processor_id */
 __xlated("2: r1 = 42")
 __xlated("3: r0 =")
@@ -800,7 +809,10 @@ __naked int bpf_loop_interaction2(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 512+0 max 512")
+__log_level(4)
+__msg("stack depth max 512")
+__msg("subprog 0 (cumulative_stack_depth) main {{.*}} stack 512")
+__msg("subprog 1 (cumulative_stack_depth_subprog) static {{.*}} stack 0")
 /* just to print xlated version when debugging */
 __xlated("r0 = &(void __percpu *)(r0)")
 __success
diff --git a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
index 67dc352addfd7..7b65eea97ebc2 100644
--- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
+++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
@@ -52,7 +52,10 @@ __msg("('global_calls_good_only') is global and assumed valid.")
 /* eventually global_good() is transitively validated as well */
 __msg("Validating global_good() func")
 __msg("('global_good') is safe for any args that match its prototype")
-__msg("insns processed {{[0-9]+\\+[0-9]+\\+[0-9]+$}}")
+__msg("subprog 0 (chained_global_func_calls_success) main insns_self 7 insns_total 7 stack")
+__msg("subprog {{[0-9]+}} (global_calls_good_only) global insns_self 2 insns_total 2 stack")
+__msg("subprog {{[0-9]+}} (global_good) global insns_self 5 insns_total 5 stack")
+__msg("processed 14 insns")
 int chained_global_func_calls_success(void)
 {
 	int sum = 0;
diff --git a/tools/testing/selftests/bpf/progs/verifier_private_stack.c b/tools/testing/selftests/bpf/progs/verifier_private_stack.c
index bb8206e10880c..ea0a7e73331da 100644
--- a/tools/testing/selftests/bpf/progs/verifier_private_stack.c
+++ b/tools/testing/selftests/bpf/progs/verifier_private_stack.c
@@ -86,7 +86,9 @@ __naked static void cumulative_stack_depth_subprog(void)
 SEC("kprobe")
 __description("Private stack, subtree > MAX_BPF_STACK")
 __success
-__log_level(4) __msg("stack depth 512+32 max 512")
+__log_level(4) __msg("stack depth max 512")
+__msg("subprog 0 (private_stack_nested_1) main {{.*}} stack 512")
+__msg("subprog 1 (cumulative_stack_depth_subprog) static {{.*}} stack 32")
 __arch_x86_64
 /* private stack fp for the main prog */
 __jited("	movabsq	$0x{{.*}}, %r9")
@@ -331,7 +333,11 @@ SEC("fentry/bpf_fentry_test9")
 __description("Private stack, async callback, potential nesting")
 __success __retval(0)
 __load_if_JITed()
-__log_level(4) __msg("stack depth 8+0+256+0 max 272")
+__log_level(4) __msg("stack depth max 272")
+__msg("subprog 0 (private_stack_async_callback_2) main {{.*}} stack 8")
+__msg("subprog 1 (timer_cb1) static {{.*}} stack 0")
+__msg("subprog 2 (subprog1) static {{.*}} stack 256")
+__msg("subprog 3 (subprog2) static {{.*}} stack 0")
 __arch_x86_64
 __jited("	subq	$0x100, %rsp")
 __arch_arm64
@@ -355,7 +361,10 @@ int private_stack_async_callback_2(void)
 SEC("fentry/bpf_fentry_test9")
 __description("private stack, max stack depth is private stack")
 __success
-__log_level(4) __msg("stack depth 8+256+0 max 256")
+__log_level(4) __msg("stack depth max 256")
+__msg("subprog 0 (private_stack_max_depth) main {{.*}} stack 8")
+__msg("subprog 1 (subprog1) static insns_self {{[0-9]+}} insns_total {{[0-9]+}} stack 256")
+__msg("subprog 2 (subprog2) static insns_self {{[0-9]+}} insns_total {{[0-9]+}} stack 0")
 int private_stack_max_depth(void)
 {
 	int x = 0;
diff --git a/tools/testing/selftests/bpf/progs/verifier_subprog_insn_stats.c b/tools/testing/selftests/bpf/progs/verifier_subprog_insn_stats.c
new file mode 100644
index 0000000000000..8f6082fdb5c84
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_subprog_insn_stats.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+struct timer_value {
+	struct bpf_timer timer;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 1);
+	__type(key, __u32);
+	__type(value, struct timer_value);
+} timer_map SEC(".maps");
+
+SEC("?raw_tp")
+__success __log_level(4)
+__msg("subprog 0 (stats_main_only) main insns_self 2 insns_total 2 stack 0")
+__msg("processed 2 insns")
+__naked int stats_main_only(void)
+{
+	asm volatile (
+		"r0 = 0;"
+		"exit;"
+	);
+}
+
+__naked __noinline __used
+static int stats_chain_leaf(void)
+{
+	asm volatile (
+		"r0 = 0;"
+		"exit;"
+	);
+}
+
+__naked __noinline __used
+static int stats_chain_parent(void)
+{
+	asm volatile (
+		"call stats_chain_leaf;"
+		"exit;"
+	);
+}
+
+SEC("?raw_tp")
+__success __log_level(4)
+/*
+ * self: 2 + 2 + 2 = 6
+ * totals: leaf 2, parent 2 + 2 = 4, main 2 + 4 = 6
+ */
+__msg("subprog 0 (stats_static_chain) main insns_self 2 insns_total 6 stack 0")
+__msg("subprog {{[0-9]+}} (stats_chain_parent) static insns_self 2 insns_total 4 stack 0")
+__msg("subprog {{[0-9]+}} (stats_chain_leaf) static insns_self 2 insns_total 2 stack 0")
+__msg("processed 6 insns")
+__naked int stats_static_chain(void)
+{
+	asm volatile (
+		"call stats_chain_parent;"
+		"exit;"
+	);
+}
+
+__naked __noinline __used
+static int stats_shared_leaf(void)
+{
+	asm volatile (
+		"r0 = 0;"
+		"exit;"
+	);
+}
+
+__naked __noinline __used
+int stats_global_root(void)
+{
+	asm volatile (
+		"call stats_shared_leaf;"
+		"exit;"
+	);
+}
+
+SEC("?raw_tp")
+__success __log_level(4)
+/*
+ * stats_shared_leaf is explored once under each independent root.
+ * self: main 3 + leaf 4 + global 2 = 9
+ * root totals: main 5 + global 4 = 9
+ */
+__msg("subprog 0 (stats_shared_roots) main insns_self 3 insns_total 5 stack 0")
+__msg("subprog {{[0-9]+}} (stats_shared_leaf) static insns_self 4 insns_total 4 stack 0")
+__msg("subprog {{[0-9]+}} (stats_global_root) global insns_self 2 insns_total 4 stack 0")
+__msg("processed 9 insns")
+__naked int stats_shared_roots(void)
+{
+	asm volatile (
+		"call stats_shared_leaf;"
+		"call stats_global_root;"
+		"exit;"
+	);
+}
+
+__noinline __used
+static int stats_async_leaf(void *map, __u32 *key, struct bpf_timer *timer)
+{
+	return 0;
+}
+
+__noinline __used
+static __u64 stats_async_schedule(struct bpf_map *map, __u32 *key,
+				  struct timer_value *value, void *ctx)
+{
+	asm volatile (
+		"r1 = %[timer];"
+		"r2 = %[stats_async_leaf];"
+		"call %[bpf_timer_set_callback];"
+		:
+		: [timer] "r" (value),
+		  __imm_ptr(stats_async_leaf),
+		  __imm(bpf_timer_set_callback)
+		: __clobber_common
+	);
+	return 0;
+}
+
+SEC("?raw_tp")
+__success __log_level(4)
+/*
+ * self: 9 + 7 + 2 = 18
+ * totals: leaf 2, scheduler 7, main root 18
+ */
+__msg("subprog 0 (stats_async_direct) main insns_self 9 insns_total 18 stack 0")
+__msg("subprog {{[0-9]+}} (stats_async_schedule) static insns_self 7 insns_total 7 stack 0")
+__msg("subprog {{[0-9]+}} (stats_async_leaf) static insns_self 2 insns_total 2 stack 0")
+__msg("processed 18 insns")
+__naked int stats_async_direct(void)
+{
+	asm volatile (
+		"r1 = %[timer_map] ll;"
+		"r2 = %[stats_async_schedule];"
+		"r3 = 0;"
+		"r4 = 0;"
+		"call %[bpf_for_each_map_elem];"
+		"r0 = 0;"
+		"exit;"
+		:
+		: __imm_addr(timer_map),
+		  __imm_ptr(stats_async_schedule),
+		  __imm(bpf_for_each_map_elem)
+		: __clobber_common
+	);
+}
+
+__noinline __used
+static int stats_async_nested_leaf(void *map, __u32 *key, struct bpf_timer *timer)
+{
+	return 0;
+}
+
+__noinline __used
+static int stats_async_outer(void *map, __u32 *key, struct bpf_timer *timer)
+{
+	asm volatile (
+		"r1 = %[timer];"
+		"r2 = %[stats_async_nested_leaf];"
+		"call %[bpf_timer_set_callback];"
+		:
+		: [timer] "r" (timer),
+		  __imm_ptr(stats_async_nested_leaf),
+		  __imm(bpf_timer_set_callback)
+		: __clobber_common
+	);
+	return 0;
+}
+
+__noinline __used
+static __u64 stats_async_nested_schedule(struct bpf_map *map, __u32 *key,
+					 struct timer_value *value, void *ctx)
+{
+	asm volatile (
+		"r1 = %[timer];"
+		"r2 = %[stats_async_outer];"
+		"call %[bpf_timer_set_callback];"
+		:
+		: [timer] "r" (value),
+		  __imm_ptr(stats_async_outer),
+		  __imm(bpf_timer_set_callback)
+		: __clobber_common
+	);
+	return 0;
+}
+
+SEC("?raw_tp")
+__success __log_level(4)
+/*
+ * self: 9 + 7 + 7 + 2 = 25
+ * totals: leaf 2, outer 7, scheduler 7, main root 25
+ */
+__msg("subprog 0 (stats_async_nested) main insns_self 9 insns_total 25 stack 0")
+__msg("subprog {{[0-9]+}} (stats_async_nested_schedule) static insns_self 7 insns_total 7 stack 0")
+__msg("subprog {{[0-9]+}} (stats_async_outer) static insns_self 7 insns_total 7 stack 0")
+__msg("subprog {{[0-9]+}} (stats_async_nested_leaf) static insns_self 2 insns_total 2 stack 0")
+__msg("processed 25 insns")
+__naked int stats_async_nested(void)
+{
+	asm volatile (
+		"r1 = %[timer_map] ll;"
+		"r2 = %[stats_async_nested_schedule];"
+		"r3 = 0;"
+		"r4 = 0;"
+		"call %[bpf_for_each_map_elem];"
+		"r0 = 0;"
+		"exit;"
+		:
+		: __imm_addr(timer_map),
+		  __imm_ptr(stats_async_nested_schedule),
+		  __imm(bpf_for_each_map_elem)
+		: __clobber_common
+	);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_var_off.c b/tools/testing/selftests/bpf/progs/verifier_var_off.c
index 24cd0a7636730..a63e336750918 100644
--- a/tools/testing/selftests/bpf/progs/verifier_var_off.c
+++ b/tools/testing/selftests/bpf/progs/verifier_var_off.c
@@ -198,7 +198,8 @@ __success
 /* Check that the maximum stack depth is correctly maintained according to the
  * maximum possible variable offset.
  */
-__log_level(4) __msg("stack depth 16")
+__log_level(4)
+__msg("subprog 0 (stack_write_priv_vs_unpriv) main {{.*}} stack 16")
 __failure_unpriv
 /* Variable stack access is rejected for unprivileged.
  */
@@ -238,7 +239,8 @@ __success
 /* Check that the maximum stack depth is correctly maintained according to the
  * maximum possible variable offset.
  */
-__log_level(4) __msg("stack depth 16")
+__log_level(4)
+__msg("subprog 0 (stack_write_followed_by_read) main {{.*}} stack 16")
 __failure_unpriv
 __msg_unpriv("R2 variable stack access prohibited for !root")
 __retval(0)
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index a8ae03c57bbae..bffb7360434c5 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -1560,7 +1560,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
 
 	opts.expected_attach_type = test->expected_attach_type;
 	if (expected_ret == VERBOSE_ACCEPT)
-		opts.log_level = 2;
+		opts.log_level = 2 | 4;
 	else if (verbose)
 		opts.log_level = verif_log_level | 4; /* force stats */
 	else
diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c
index 8cd626e045515..eb6e3baef412a 100644
--- a/tools/testing/selftests/bpf/verifier/calls.c
+++ b/tools/testing/selftests/bpf/verifier/calls.c
@@ -1091,7 +1091,17 @@
 	/* stack_main=32, stack_A=256, stack_B=64
 	 * and max(main+A, main+A+B) < 512
 	 */
-	.result = ACCEPT,
+	.result = VERBOSE_ACCEPT,
+	.errstr = "stack depth max 352\t"
+		  "subprog 0 (<unknown>) main insns_self \t"
+		  " insns_total \t"
+		  " stack 32\t"
+		  "subprog 1 (<unknown>) static insns_self \t"
+		  " insns_total \t"
+		  " stack 256\t"
+		  "subprog 2 (<unknown>) static insns_self \t"
+		  " insns_total \t"
+		  " stack 64",
 },
 {
 	"calls: stack depth check using three frames. test2",
diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
index c9c257784ee3d..5c0ec3edce720 100644
--- a/tools/testing/selftests/bpf/veristat.c
+++ b/tools/testing/selftests/bpf/veristat.c
@@ -993,13 +993,15 @@ static void free_verif_stats(struct verif_stats *stats, size_t stat_cnt)
 
 static char verif_log_buf[64 * 1024];
 
-#define MAX_PARSED_LOG_LINES 100
+/* Keep room for all 256 subprogram records and trailing statistics. */
+#define MAX_PARSED_LOG_LINES 300
 
 static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *s)
 {
 	const char *cur;
-	int pos, lines, sub_stack, cnt = 0;
-	char *state = NULL, *token, stack[512];
+	long sub_stack;
+	int pos, lines, cnt = 0;
+	char *state = NULL, *token, stack[512] = {};
 
 	buf[buf_sz - 1] = '\0';
 
@@ -1025,11 +1027,24 @@ static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *
 				&s->stats[MARK_READ_MAX_LEN]))
 			continue;
 
+		/*
+		 * New kernels emit one "subprog <id> (<name>) <kind>" record
+		 * per subprogram with the stack depth at the end, while old
+		 * kernels emit a single "stack depth <a+...+n> max <max>"
+		 * line. Match both formats so veristat works against either
+		 * kernel.
+		 */
+		if (sscanf(cur, "stack depth max %ld", &s->stats[MAX_STACK]) == 1)
+			continue;
+		if (sscanf(cur, "subprog %*d %*s %*s insns_self %*d insns_total %*d stack %ld", &sub_stack) == 1) {
+			s->stats[STACK] += sub_stack;
+			continue;
+		}
 		if (2 == sscanf(cur, "stack depth %511s max %ld", stack, &s->stats[MAX_STACK]))
 			continue;
 	}
 	while ((token = strtok_r(cnt++ ? NULL : stack, "+", &state))) {
-		if (sscanf(token, "%d", &sub_stack) == 0)
+		if (sscanf(token, "%ld", &sub_stack) == 0)
 			break;
 		s->stats[STACK] += sub_stack;
 	}

3/2 2026/08/12 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 12496
							output: 32
							thoughts: 509
						

Thoughts:
**BPF Verifier Instruction Count Tracking**

Okay, so this patch is clearly focused on improving the visibility into BPF verification, specifically by tracking instruction counts on a per-subprogram basis. The changes are concentrated in `kernel/bpf/verifier.c` and the associated header file. It looks like they're adding new fields: `insns_subtotal` within `struct bpf_func_state`, and `insns_total` and `insns_self` to `struct bpf_subprog_info`. The code updates existing accounting functions like `account_processed_insn`, `account_processed_insns`, and `account_current_path` to keep these counts accurate. And naturally, `print_verification_stats` is being modified to report this new, more granular data. That's a solid improvement for debugging and performance analysis.

But, the question is, *does this affect verification logic itself?* It's critical to make sure the core behavior remains unchanged. Let me see... yes, I was right to check that! Okay, let's see how the instruction complexity limit is handled. Ah, here we go...

```c
-		if (++env->insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) {
+		account_processed_insn(env);
+
+		if (env->insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) {
```

Okay, `account_processed_insn` is called first, which still increments `env->insn_processed` before the check against `BPF_COMPLEXITY_LIMIT_INSNS`. So the limit *itself* remains unchanged. The new fields, `insns_subtotal`, `insns_total`, and `insns_self`, appear to be purely for statistics in `print_verification_stats`.

Let's quickly confirm. Let's look at `account_processed_insn` function definition.

```c
static void account_processed_insn(struct bpf_verifier_env *env)
{
	struct bpf_func_state *frame = cur_func(env);

	env->insn_processed++;
	frame->insns_subtotal++;
	env->subprog_info[frame->subprogno].insns_self++;
}
```

Yup, it increments `env->insn_processed` and then updates all the tracking variables. And `account_processed_insns` increments the `insns_total`. I need to be 100% sure this isn't used to reject programs. Quick search for `insns_total` and `insns_self` in the verifier code is necessary. Just to be sure.



4/2 2026/08/12 22:38 tool grepper
0m Args:
{
  "Expression": "insns_total",
  "PathPrefix": "kernel/bpf/verifier.c"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=9731=static void account_processed_insns(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-9741-\nkernel/bpf/verifier.c:9742:\tenv-\u003esubprog_info[callee-\u003esubprogno].insns_total += insns;\nkernel/bpf/verifier.c-9743-\tif (caller)\n--\nkernel/bpf/verifier.c=18453=static int do_check_common(struct bpf_verifier_env *env, int subprog)\n--\nkernel/bpf/verifier.c-18610-\t */\nkernel/bpf/verifier.c:18611:\tenv-\u003esubprog_info[subprog].insns_total = env-\u003einsn_processed - insn_processed;\nkernel/bpf/verifier.c-18612-\treturn ret;\n--\nkernel/bpf/verifier.c=18697=static void print_verification_stats(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-18713-\t\t\t       bpf_subprog_is_global(env, i) ? \"global\" : \"static\";\nkernel/bpf/verifier.c:18714:\t\t\tverbose(env, \"subprog %d (%s) %s insns_self %d insns_total %d stack %d\\n\",\nkernel/bpf/verifier.c-18715-\t\t\t\ti, name, kind, env-\u003esubprog_info[i].insns_self,\nkernel/bpf/verifier.c:18716:\t\t\t\tenv-\u003esubprog_info[i].insns_total,\nkernel/bpf/verifier.c-18717-\t\t\t\tenv-\u003esubprog_info[i].stack_depth);\n"
}

5/2 2026/08/12 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14109
							output: 33
							thoughts: 40
						

Thoughts:
Okay, here's my interpretation of those thoughts, summarized from my perspective as someone well-versed in this code:

**Analyzing Instruction Counting Metrics**

Alright, let's see. The code I'm looking at only seems to be using `insns_total` and `insns_self` for logging purposes, specifically within `print_verification_stats`.  That's a bit limiting, and it doesn't give me the level of detail I'm usually after. Before I dig deeper, I need to see what's happening with `insns_subtotal`. I'm guessing this field might be the key to understanding the deeper structure. Hopefully it's calculated in a way that gives a more useful breakdown than what I'm seeing from the other two. I'm going to track down the usage of this variable next.



6/2 2026/08/12 22:38 tool grepper
0m Args:
{
  "Expression": "insns_subtotal",
  "PathPrefix": "kernel/bpf/verifier.c"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=1592=static int copy_func_state(struct bpf_func_state *dst,\n--\nkernel/bpf/verifier.c-1596-\t/* Instruction accounting is path-local, not part of verifier state. */\nkernel/bpf/verifier.c:1597:\tdst-\u003einsns_subtotal = 0;\nkernel/bpf/verifier.c-1598-\treturn copy_stack_state(dst, src);\n--\nkernel/bpf/verifier.c=9722=static void account_processed_insn(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-9726-\tenv-\u003einsn_processed++;\nkernel/bpf/verifier.c:9727:\tframe-\u003einsns_subtotal++;\nkernel/bpf/verifier.c-9728-\tenv-\u003esubprog_info[frame-\u003esubprogno].insns_self++;\n--\nkernel/bpf/verifier.c=9731=static void account_processed_insns(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-9739-\nkernel/bpf/verifier.c:9740:\tinsns = callee-\u003einsns_subtotal;\nkernel/bpf/verifier.c-9741-\n--\nkernel/bpf/verifier.c-9743-\tif (caller)\nkernel/bpf/verifier.c:9744:\t\tcaller-\u003einsns_subtotal += insns;\nkernel/bpf/verifier.c:9745:\tcallee-\u003einsns_subtotal = 0;\nkernel/bpf/verifier.c-9746-}\n"
}

7/2 2026/08/12 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14801
							output: 77
							thoughts: 577
						

Thoughts:
**My Assessment of the BPF Verifier Change**

Okay, so I've examined this patch and it's pretty straightforward. From what I can tell, the primary purpose of these changes is purely for statistical tracking within the BPF verifier. It appears that the new fields are being introduced to enhance logging, specifically when `BPF_LOG_STATS` is enabled. The core of the verification logic itself isn't being altered in a way that would change how programs are accepted or rejected, nor is there any modification to the generated code.

The addition of `account_processed_insn` and the associated fields, such as `insns_subtotal` and `insns_self`, are simply incrementing and tracking specific counts; functionally, `env->insn_processed` is being updated in the same manner. This patch just provides more detailed insights into the verification process.

I've also quickly scanned the `print_verification_stats` function to ensure there aren't any potential issues related to memory access or logging that could cause a crash. It appears to be a safe `verbose()` call, merely outputting the collected statistics.

Given the nature of the changes, which is limited to logging and statistical accumulation, I can confidently conclude that fuzzing this specific patch would not be beneficial. It won't reveal any new vulnerabilities in the program verification. Therefore, my assessment is `WorthFuzzing=false`.



8/2 2026/08/12 22:38 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch only adds statistics tracking for the number of instructions processed per subprogram during BPF verification and outputs them in the verifier log. It does not alter the verification logic, execution flow, or the generated BPF code.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds statistics tracking for the number of instructions processed per subprogram during BPF verification and outputs them in the verifier log. It does not alter the verification logic, execution flow, or the generated BPF code.",
  "WorthFuzzing": false
}

9/1 2026/08/12 22:38 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)