bpf: Show more useful info in stack depth stats

Stack depth statistics list captured depths in subprogram-number order,
while per-verification instruction counts are reported separately. Since
libbpf determines subprogram numbers, it is hard to associate either
statistic with its subprogram name or see where verifier work is spent.

Now that self and inclusive instruction counts are available for every
subprogram, keep the combined maximum stack depth on its own line and print
one uniform record for each subprogram. Represent the main program as
subprog 0, then classify each record as main, global, or static before
reporting insns_self, insns_total, and stack depth.

The aggregate processed count is the sum of all self counts, while each
total shows verifier work rooted at that subprogram.

When no subprogram name is available, print <unknown>. Keep the existing
aggregate "processed ... insns" record unchanged for compatibility.

Suggested-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://patch.msgid.link/20260812221925.3358041-4-memxor@gmail.com
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
This commit is contained in:
Kumar Kartikeya Dwivedi
2026-08-13 00:19:20 +02:00
committed by Eduard Zingerman
parent 6137fb7c5f
commit c2e6c7de88
6 changed files with 58 additions and 25 deletions

View File

@@ -18693,15 +18693,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].insns_total);
for (i = 1; i < subprog_cnt; i++)
if (bpf_subprog_is_global(env, i))
verbose(env, "+%d", env->subprog_info[i].insns_total);
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",

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)