We've hit the 512 bytes limit on stack depth a few times in Cilium
recently. As a result, we started reporting in CI our current maximum
stack depth across all configurations for each BPF program.
Unfortunately, that is not trivial to compute in userspace. The
verifier reports the stack depths of individual subprogs at the end of
the logs. However the maximum combined stack depth also depends on the
callgraph of those subprogs (the max combined stack depth is the height
of the callgraph weighted by per-subprog stack depths). We can compute
a callgraph in userspace from the loaded instructions, but it often
doesn't match the verifier's own callgraph because of dead code
elimination. Our current approach relies on dumping the BPF_LOG_LEVEL2
logs, but this feels overkill considering the verifier already has the
information we need.
The patch lets the verifier dump the maximum combined stack depth in
the logs, on the same line as the per-subprog stack depths:
stack depth 16+256 max 272
The per-subprog stack depths and the new max stack depth are not
directly comparable. The former is sometimes updated during fixups,
while the latter is not. As a result, even with a single subprog, we
may end up with two slightly different values. The aim of the new max
value is to be closest to what is actually enforced by the verifier.
Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/d3d23a0410f87f116f3bbaa98a815dbae113bda2.1778700777.git.paul.chaignon@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
u32 prev_insn_processed, insn_processed;
/* number of jmps, calls, exits analyzed so far */
u32 prev_jmps_processed, jmps_processed;
+ /* maximum combined stack depth */
+ u32 max_stack_depth;
/* total verification time */
u64 verification_time;
/* maximum number of verifier states kept in 'branching' instructions */
}
if (subprog[idx].priv_stack_mode == PRIV_STACK_ADAPTIVE) {
+ if (subprog_depth > env->max_stack_depth)
+ env->max_stack_depth = subprog_depth;
if (subprog_depth > MAX_BPF_STACK) {
verbose(env, "stack size of subprog %d is %d. Too large\n",
idx, subprog_depth);
}
} else {
depth += subprog_depth;
+ if (depth > env->max_stack_depth)
+ env->max_stack_depth = depth;
if (depth > MAX_BPF_STACK) {
total = 0;
for (tmp = idx; tmp >= 0; tmp = dinfo[tmp].caller)
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, "\n");
+ 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))