BPF List
 help / color / mirror / Atom feed
* [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs
@ 2026-04-28 14:55 Paul Chaignon
  2026-04-28 14:55 ` [PATCH bpf-next 2/2] selftests/bpf: Test insns processed breakdown Paul Chaignon
  2026-04-28 23:08 ` [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs sashiko-bot
  0 siblings, 2 replies; 10+ messages in thread
From: Paul Chaignon @ 2026-04-28 14:55 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi

When using global functions (i.e. subprogs), the verifier performs
function-by-function verification. In that case, the sum of the
instructions processed in each global function and in the main program
counts towards the 1 million instructions limit. Only that sum is
reported in the verifier logs.

While starting to use global functions in Cilium (finally!), we found it
can be useful to have the breakdown per global function, to understand
exactly where the budget is currently spent. This patch implements this
breakdown, under BPF_LOG_STATS, as done for the stack depths.

Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
---
 include/linux/bpf_verifier.h |  1 +
 kernel/bpf/verifier.c        | 14 ++++++++++++++
 2 files changed, 15 insertions(+)

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 101ca6cc5424..976e2b2f40e8 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -779,6 +779,7 @@ 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;
 	/* 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 03f9e16c2abe..b48389b48eb6 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -18183,6 +18183,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
 	}
 
 	ret = do_check(env);
+
 out:
 	if (!ret && pop_log)
 		bpf_vlog_reset(&env->log, 0);
@@ -18215,6 +18216,7 @@ 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;
@@ -18229,6 +18231,8 @@ 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;
@@ -18236,6 +18240,7 @@ 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) {
@@ -18262,10 +18267,12 @@ 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;
@@ -18288,6 +18295,13 @@ static void print_verification_stats(struct bpf_verifier_env *env)
 				verbose(env, "+");
 		}
 		verbose(env, "\n");
+		verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
+		for (i = 1; i < env->subprog_cnt; i++) {
+			if (!bpf_subprog_is_global(env, i))
+				continue;
+			verbose(env, "+%d", env->subprog_info[i].insn_processed);
+		}
+		verbose(env, "\n");
 	}
 	verbose(env, "processed %d insns (limit %d) max_states_per_insn %d "
 		"total_states %d peak_states %d mark_read %d\n",
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH bpf-next 2/2] selftests/bpf: Test insns processed breakdown
  2026-04-28 14:55 [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs Paul Chaignon
@ 2026-04-28 14:55 ` Paul Chaignon
  2026-04-28 16:18   ` Kumar Kartikeya Dwivedi
  2026-04-28 23:14   ` sashiko-bot
  2026-04-28 23:08 ` [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs sashiko-bot
  1 sibling, 2 replies; 10+ messages in thread
From: Paul Chaignon @ 2026-04-28 14:55 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi

This patch covers in global subprog selftests the new verifier log with
the breakdown of instructions processed by global subprogs.

Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
---
 .../bpf/progs/verifier_global_subprogs.c      | 60 ++++++++++++-------
 1 file changed, 40 insertions(+), 20 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
index 1e08aff7532e..58faf5485667 100644
--- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
+++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
@@ -46,12 +46,13 @@ __noinline long global_dead(void)
 }
 
 SEC("?raw_tp")
-__success __log_level(2)
+__success __log_level(6)
 /* main prog is validated completely first */
 __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 7+2+5")
 int chained_global_func_calls_success(void)
 {
 	int sum = 0;
@@ -86,7 +87,8 @@ __noinline int global_unsupp(const int *mem)
 const volatile bool skip_unsupp_global = true;
 
 SEC("?raw_tp")
-__success
+__success __log_level(4)
+__msg("insns processed 5")
 int guarded_unsupp_global_called(void)
 {
 	if (!skip_unsupp_global)
@@ -142,8 +144,9 @@ int anon_user_mem_invalid(void *ctx)
 }
 
 SEC("?tracepoint")
-__success __log_level(2)
+__success __log_level(6)
 __msg("Func#1 ('subprog_user_anon_mem') is safe for any args that match its prototype")
+__msg("insns processed 6+5")
 int anon_user_mem_valid(void *ctx)
 {
 	user_struct_t t = { .x = 42 };
@@ -159,7 +162,8 @@ __noinline __weak int subprog_nonnull_ptr_good(int *p1 __arg_nonnull, int *p2 __
 int x = 47;
 
 SEC("?raw_tp")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 7+4")
 int arg_tag_nonnull_ptr_good(void *ctx)
 {
 	int y = 74;
@@ -186,21 +190,24 @@ __weak int raw_tp_u64_array(u64 *ctx __arg_ctx)
 }
 
 SEC("?raw_tp")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 12+5+2+2")
 int arg_tag_ctx_raw_tp(void *ctx)
 {
 	return subprog_ctx_tag(ctx) + raw_tp_canonical(ctx) + raw_tp_u64_array(ctx);
 }
 
 SEC("?raw_tp.w")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 12+5+2+2")
 int arg_tag_ctx_raw_tp_writable(void *ctx)
 {
 	return subprog_ctx_tag(ctx) + raw_tp_canonical(ctx) + raw_tp_u64_array(ctx);
 }
 
 SEC("?tp_btf/sys_enter")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 12+5+2+2")
 int arg_tag_ctx_raw_tp_btf(void *ctx)
 {
 	return subprog_ctx_tag(ctx) + raw_tp_canonical(ctx) + raw_tp_u64_array(ctx);
@@ -214,7 +221,8 @@ __weak int tp_whatever(struct whatever *ctx __arg_ctx)
 }
 
 SEC("?tp")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 7+5+2")
 int arg_tag_ctx_tp(void *ctx)
 {
 	return subprog_ctx_tag(ctx) + tp_whatever(ctx);
@@ -231,7 +239,8 @@ __weak int kprobe_subprog_typedef(bpf_user_pt_regs_t *ctx __arg_ctx)
 }
 
 SEC("?kprobe")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 12+5+2+2")
 int arg_tag_ctx_kprobe(void *ctx)
 {
 	return subprog_ctx_tag(ctx) +
@@ -266,7 +275,8 @@ __weak int perf_subprog_canonical(struct bpf_perf_event_data *ctx __arg_ctx)
 }
 
 SEC("?perf_event")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 15+5+2+2+2")
 int arg_tag_ctx_perf(void *ctx)
 {
 	return subprog_ctx_tag(ctx) +
@@ -286,7 +296,8 @@ __weak int iter_subprog_typed(struct bpf_iter__task *ctx __arg_ctx)
 }
 
 SEC("?iter/task")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 8+2+2")
 int arg_tag_ctx_iter_task(struct bpf_iter__task *ctx)
 {
 	return (iter_subprog_void(ctx) + iter_subprog_typed(ctx)) & 1;
@@ -305,7 +316,8 @@ __weak int tracing_subprog_u64(u64 *ctx __arg_ctx)
 int acc;
 
 SEC("?fentry/" SYS_PREFIX "sys_nanosleep")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 12+2+2")
 int BPF_PROG(arg_tag_ctx_fentry)
 {
 	acc += tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
@@ -313,7 +325,8 @@ int BPF_PROG(arg_tag_ctx_fentry)
 }
 
 SEC("?fexit/" SYS_PREFIX "sys_nanosleep")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 12+2+2")
 int BPF_PROG(arg_tag_ctx_fexit)
 {
 	acc += tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
@@ -321,14 +334,16 @@ int BPF_PROG(arg_tag_ctx_fexit)
 }
 
 SEC("?fmod_ret/" SYS_PREFIX "sys_nanosleep")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 7+2+2")
 int BPF_PROG(arg_tag_ctx_fmod_ret)
 {
 	return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
 }
 
 SEC("?lsm/bpf")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 11+2+2")
 int BPF_PROG(arg_tag_ctx_lsm)
 {
 	int ret;
@@ -339,7 +354,8 @@ int BPF_PROG(arg_tag_ctx_lsm)
 }
 
 SEC("?struct_ops/test_1")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 7+2+2")
 int BPF_PROG(arg_tag_ctx_struct_ops)
 {
 	return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
@@ -351,7 +367,8 @@ struct bpf_dummy_ops dummy_1 = {
 };
 
 SEC("?syscall")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 12+2+2+2")
 int arg_tag_ctx_syscall(void *ctx)
 {
 	return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx) + tp_whatever(ctx);
@@ -369,7 +386,8 @@ __weak int syscall_array_bpf_for(void *ctx __arg_ctx)
 }
 
 SEC("?syscall")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 2+58")
 int arg_tag_ctx_syscall_bpf_for(void *ctx)
 {
 	return syscall_array_bpf_for(ctx);
@@ -394,7 +412,8 @@ struct {
 };
 
 SEC("?syscall")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 5")
 int arg_tag_ctx_syscall_tailcall(void *ctx)
 {
 	bpf_tail_call(ctx, &syscall_prog_array, 0);
@@ -471,7 +490,8 @@ __weak int subprog_dynptr(struct bpf_dynptr *dptr)
 }
 
 SEC("?xdp")
-__success __log_level(2)
+__success __log_level(6)
+__msg("insns processed 8+27")
 int arg_tag_dynptr(struct xdp_md *ctx)
 {
 	struct bpf_dynptr dptr;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf-next 2/2] selftests/bpf: Test insns processed breakdown
  2026-04-28 14:55 ` [PATCH bpf-next 2/2] selftests/bpf: Test insns processed breakdown Paul Chaignon
@ 2026-04-28 16:18   ` Kumar Kartikeya Dwivedi
  2026-04-28 23:14   ` sashiko-bot
  1 sibling, 0 replies; 10+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-28 16:18 UTC (permalink / raw)
  To: Paul Chaignon
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman

On Tue, 28 Apr 2026 at 16:55, Paul Chaignon <paul.chaignon@gmail.com> wrote:
>
> This patch covers in global subprog selftests the new verifier log with
> the breakdown of instructions processed by global subprogs.
>
> Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
> ---

There are a few CI failures around the expected substring, PTAL.

pw-bot: cr

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs
  2026-04-28 14:55 [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs Paul Chaignon
  2026-04-28 14:55 ` [PATCH bpf-next 2/2] selftests/bpf: Test insns processed breakdown Paul Chaignon
@ 2026-04-28 23:08 ` sashiko-bot
  2026-04-28 23:55   ` Kumar Kartikeya Dwivedi
  1 sibling, 1 reply; 10+ messages in thread
From: sashiko-bot @ 2026-04-28 23:08 UTC (permalink / raw)
  To: Paul Chaignon; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Out-of-bounds read in `bpf_subprog_is_global()` when printing verification stats for programs with hidden subprograms.
--

commit 08128f883e58301e0f97b1d4a94656be63b8f790
Author: Paul Chaignon <paul.chaignon@gmail.com>

bpf: Print breakdown of insns processed by subprogs

This patch tracks the number of instructions processed by each global
function (subprog) during BPF verification. It then reports this
breakdown in the verifier logs under BPF_LOG_STATS.

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 03f9e16c2abe..b48389b48eb6 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c

[ ... ]

> @@ -18288,6 +18295,13 @@ static void print_verification_stats(struct bpf_verifier_env *env)
>  				verbose(env, "+");
>  		}
>  		verbose(env, "\n");
> +		verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
> +		for (i = 1; i < env->subprog_cnt; i++) {
> +			if (!bpf_subprog_is_global(env, i))

Could this introduce a regression due to an out-of-bounds read on the
func_info_aux array if there are hidden subprograms?

At load time, if BTF is present, env->prog->aux->func_info_aux is allocated
with exactly func_info_cnt elements, and env->subprog_cnt initially matches
func_info_cnt.

During verification, if a feature like bpf_throw is used without an explicit
callback, add_hidden_subprog() is called which increments env->subprog_cnt
but does not resize the func_info_aux array.

Since this loop iterates up to the updated env->subprog_cnt, when i reaches
the index of a hidden subprogram, bpf_subprog_is_global() will access
aux[i].linkage where i >= func_info_cnt:

kernel/bpf/verifier.c:bpf_subprog_is_global() {
	struct bpf_func_info_aux *aux = env->prog->aux->func_info_aux;

	return aux && aux[subprog].linkage == BTF_FUNC_GLOBAL;
}

Is it possible to skip hidden subprograms here, or limit the check to
func_info_cnt?

> +				continue;
> +			verbose(env, "+%d", env->subprog_info[i].insn_processed);
> +		}
> +		verbose(env, "\n");
>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/5c3514dbc879f22ba3f9eff8a2c730e13bd0572c.1777388107.git.paul.chaignon@gmail.com?part=1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf-next 2/2] selftests/bpf: Test insns processed breakdown
  2026-04-28 14:55 ` [PATCH bpf-next 2/2] selftests/bpf: Test insns processed breakdown Paul Chaignon
  2026-04-28 16:18   ` Kumar Kartikeya Dwivedi
@ 2026-04-28 23:14   ` sashiko-bot
  1 sibling, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-04-28 23:14 UTC (permalink / raw)
  To: Paul Chaignon; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Asserting exact compiler-generated instruction counts in BPF C selftests makes them extremely brittle across LLVM/Clang updates.
--

commit a64551619d1c16e765011f494b0528e642a29308
Author: Paul Chaignon <paul.chaignon@gmail.com>

selftests/bpf: Test insns processed breakdown

This patch updates the BPF global subprog selftests to check the new verifier
log breakdown of processed instructions by adding __msg() assertions with exact
instruction counts.

> diff --git a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
> index 1e08aff7532e..58faf5485667 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
> @@ -46,12 +46,13 @@ __noinline long global_dead(void)
>  }
>  
>  SEC("?raw_tp")
> -__success __log_level(2)
> +__success __log_level(6)
>  /* main prog is validated completely first */
>  __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 7+2+5")

Will these exact instruction counts make the tests brittle across different
LLVM/Clang versions?

Because C code compilation is highly sensitive to compiler versions, changes in
optimizations or register allocation will alter the number of emitted BPF
instructions. This could cause the selftests to fail unpredictably across
different local environments or when the CI compiler is updated.

Could the __regex macro be used here instead to verify the expected verifier
log format? For example, using __regex("insns processed [0-9]+(\\+[0-9]+)+")
would validate the breakdown output without relying on exact counts.

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/5c3514dbc879f22ba3f9eff8a2c730e13bd0572c.1777388107.git.paul.chaignon@gmail.com?part=2

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs
  2026-04-28 23:08 ` [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs sashiko-bot
@ 2026-04-28 23:55   ` Kumar Kartikeya Dwivedi
  2026-04-29  0:07     ` Kumar Kartikeya Dwivedi
  0 siblings, 1 reply; 10+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-28 23:55 UTC (permalink / raw)
  To: sashiko, Paul Chaignon; +Cc: bpf

On Wed Apr 29, 2026 at 1:08 AM CEST, sashiko-bot wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] Out-of-bounds read in `bpf_subprog_is_global()` when printing verification stats for programs with hidden subprograms.
> --
>
> commit 08128f883e58301e0f97b1d4a94656be63b8f790
> Author: Paul Chaignon <paul.chaignon@gmail.com>
>
> bpf: Print breakdown of insns processed by subprogs
>
> This patch tracks the number of instructions processed by each global
> function (subprog) during BPF verification. It then reports this
> breakdown in the verifier logs under BPF_LOG_STATS.
>
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 03f9e16c2abe..b48389b48eb6 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>
> [ ... ]
>
>> @@ -18288,6 +18295,13 @@ static void print_verification_stats(struct bpf_verifier_env *env)
>>  				verbose(env, "+");
>>  		}
>>  		verbose(env, "\n");
>> +		verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
>> +		for (i = 1; i < env->subprog_cnt; i++) {
>> +			if (!bpf_subprog_is_global(env, i))
>
> Could this introduce a regression due to an out-of-bounds read on the
> func_info_aux array if there are hidden subprograms?
>
> At load time, if BTF is present, env->prog->aux->func_info_aux is allocated
> with exactly func_info_cnt elements, and env->subprog_cnt initially matches
> func_info_cnt.
>
> During verification, if a feature like bpf_throw is used without an explicit
> callback, add_hidden_subprog() is called which increments env->subprog_cnt
> but does not resize the func_info_aux array.
>
> Since this loop iterates up to the updated env->subprog_cnt, when i reaches
> the index of a hidden subprogram, bpf_subprog_is_global() will access
> aux[i].linkage where i >= func_info_cnt:
>
> kernel/bpf/verifier.c:bpf_subprog_is_global() {
> 	struct bpf_func_info_aux *aux = env->prog->aux->func_info_aux;
>
> 	return aux && aux[subprog].linkage == BTF_FUNC_GLOBAL;
> }
>
> Is it possible to skip hidden subprograms here, or limit the check to
> func_info_cnt?

Don't see it happening, since hidden subprogs never get verified, so we won't
invoke bpf_subprog_is_global() with such a subprog index.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs
  2026-04-28 23:55   ` Kumar Kartikeya Dwivedi
@ 2026-04-29  0:07     ` Kumar Kartikeya Dwivedi
  2026-04-29 14:01       ` Paul Chaignon
  0 siblings, 1 reply; 10+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-29  0:07 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi, sashiko, Paul Chaignon; +Cc: bpf

On Wed Apr 29, 2026 at 1:55 AM CEST, Kumar Kartikeya Dwivedi wrote:
> On Wed Apr 29, 2026 at 1:08 AM CEST, sashiko-bot wrote:
>> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>> - [High] Out-of-bounds read in `bpf_subprog_is_global()` when printing verification stats for programs with hidden subprograms.
>> --
>>
>> commit 08128f883e58301e0f97b1d4a94656be63b8f790
>> Author: Paul Chaignon <paul.chaignon@gmail.com>
>>
>> bpf: Print breakdown of insns processed by subprogs
>>
>> This patch tracks the number of instructions processed by each global
>> function (subprog) during BPF verification. It then reports this
>> breakdown in the verifier logs under BPF_LOG_STATS.
>>
>>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>>> index 03f9e16c2abe..b48389b48eb6 100644
>>> --- a/kernel/bpf/verifier.c
>>> +++ b/kernel/bpf/verifier.c
>>
>> [ ... ]
>>
>>> @@ -18288,6 +18295,13 @@ static void print_verification_stats(struct bpf_verifier_env *env)
>>>  				verbose(env, "+");
>>>  		}
>>>  		verbose(env, "\n");
>>> +		verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
>>> +		for (i = 1; i < env->subprog_cnt; i++) {
>>> +			if (!bpf_subprog_is_global(env, i))
>>
>> Could this introduce a regression due to an out-of-bounds read on the
>> func_info_aux array if there are hidden subprograms?
>>
>> At load time, if BTF is present, env->prog->aux->func_info_aux is allocated
>> with exactly func_info_cnt elements, and env->subprog_cnt initially matches
>> func_info_cnt.
>>
>> During verification, if a feature like bpf_throw is used without an explicit
>> callback, add_hidden_subprog() is called which increments env->subprog_cnt
>> but does not resize the func_info_aux array.
>>
>> Since this loop iterates up to the updated env->subprog_cnt, when i reaches
>> the index of a hidden subprogram, bpf_subprog_is_global() will access
>> aux[i].linkage where i >= func_info_cnt:
>>
>> kernel/bpf/verifier.c:bpf_subprog_is_global() {
>> 	struct bpf_func_info_aux *aux = env->prog->aux->func_info_aux;
>>
>> 	return aux && aux[subprog].linkage == BTF_FUNC_GLOBAL;
>> }
>>
>> Is it possible to skip hidden subprograms here, or limit the check to
>> func_info_cnt?
>
> Don't see it happening, since hidden subprogs never get verified, so we won't
> invoke bpf_subprog_is_global() with such a subprog index.

Ah, no, stupid me. We get here after fixing up and adding the hidden subprog. So
we can still do OOB since subprog_cnt includes the hidden_subprog_cnt. How about
the following as a fix? I checked over other places where we iterate over all of
the subprogs and those look fine, so instead of changing bpf_subprog_is_global()
we can adjust this function to only consider real subprogs. Didn't compile test.

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index b48389b48eb6..b9266e3d46c7 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -18281,22 +18281,24 @@ static int do_check_main(struct bpf_verifier_env *env)

 static void print_verification_stats(struct bpf_verifier_env *env)
 {
+       /* Skip over hidden subprogs which are not verified. */
+       int subprog_cnt = env->subprog_cnt - env->hidden_subprog_cnt;
        int i;

        if (env->log.level & BPF_LOG_STATS) {
                verbose(env, "verification time %lld usec\n",
                        div_u64(env->verification_time, 1000));
                verbose(env, "stack depth ");
-               for (i = 0; i < env->subprog_cnt; i++) {
+               for (i = 0; i < subprog_cnt; i++) {
                        u32 depth = env->subprog_info[i].stack_depth;

                        verbose(env, "%d", depth);
-                       if (i + 1 < env->subprog_cnt)
+                       if (i + 1 < subprog_cnt)
                                verbose(env, "+");
                }
                verbose(env, "\n");
                verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
-               for (i = 1; i < env->subprog_cnt; i++) {
+               for (i = 1; i < subprog_cnt; i++) {
                        if (!bpf_subprog_is_global(env, i))
                                continue;
                        verbose(env, "+%d", env->subprog_info[i].insn_processed);

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs
  2026-04-29  0:07     ` Kumar Kartikeya Dwivedi
@ 2026-04-29 14:01       ` Paul Chaignon
  2026-04-29 15:53         ` Paul Chaignon
  0 siblings, 1 reply; 10+ messages in thread
From: Paul Chaignon @ 2026-04-29 14:01 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi; +Cc: sashiko, bpf

On Wed, Apr 29, 2026 at 02:07:33AM +0200, Kumar Kartikeya Dwivedi wrote:
> On Wed Apr 29, 2026 at 1:55 AM CEST, Kumar Kartikeya Dwivedi wrote:
> > On Wed Apr 29, 2026 at 1:08 AM CEST, sashiko-bot wrote:
> >> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> >> - [High] Out-of-bounds read in `bpf_subprog_is_global()` when printing verification stats for programs with hidden subprograms.
> >> --
> >>
> >> commit 08128f883e58301e0f97b1d4a94656be63b8f790
> >> Author: Paul Chaignon <paul.chaignon@gmail.com>
> >>
> >> bpf: Print breakdown of insns processed by subprogs
> >>
> >> This patch tracks the number of instructions processed by each global
> >> function (subprog) during BPF verification. It then reports this
> >> breakdown in the verifier logs under BPF_LOG_STATS.
> >>
> >>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> >>> index 03f9e16c2abe..b48389b48eb6 100644
> >>> --- a/kernel/bpf/verifier.c
> >>> +++ b/kernel/bpf/verifier.c
> >>
> >> [ ... ]
> >>
> >>> @@ -18288,6 +18295,13 @@ static void print_verification_stats(struct bpf_verifier_env *env)
> >>>  				verbose(env, "+");
> >>>  		}
> >>>  		verbose(env, "\n");
> >>> +		verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
> >>> +		for (i = 1; i < env->subprog_cnt; i++) {
> >>> +			if (!bpf_subprog_is_global(env, i))
> >>
> >> Could this introduce a regression due to an out-of-bounds read on the
> >> func_info_aux array if there are hidden subprograms?
> >>
> >> At load time, if BTF is present, env->prog->aux->func_info_aux is allocated
> >> with exactly func_info_cnt elements, and env->subprog_cnt initially matches
> >> func_info_cnt.
> >>
> >> During verification, if a feature like bpf_throw is used without an explicit
> >> callback, add_hidden_subprog() is called which increments env->subprog_cnt
> >> but does not resize the func_info_aux array.
> >>
> >> Since this loop iterates up to the updated env->subprog_cnt, when i reaches
> >> the index of a hidden subprogram, bpf_subprog_is_global() will access
> >> aux[i].linkage where i >= func_info_cnt:
> >>
> >> kernel/bpf/verifier.c:bpf_subprog_is_global() {
> >> 	struct bpf_func_info_aux *aux = env->prog->aux->func_info_aux;
> >>
> >> 	return aux && aux[subprog].linkage == BTF_FUNC_GLOBAL;
> >> }
> >>
> >> Is it possible to skip hidden subprograms here, or limit the check to
> >> func_info_cnt?
> >
> > Don't see it happening, since hidden subprogs never get verified, so we won't
> > invoke bpf_subprog_is_global() with such a subprog index.
> 
> Ah, no, stupid me. We get here after fixing up and adding the hidden subprog. So
> we can still do OOB since subprog_cnt includes the hidden_subprog_cnt. How about
> the following as a fix? I checked over other places where we iterate over all of
> the subprogs and those look fine, so instead of changing bpf_subprog_is_global()
> we can adjust this function to only consider real subprogs. Didn't compile test.

That's a nice find! I also doubted it initially as we have that pattern
everywhere.

It looks like this would be a fix for commit 335d1c5b5452 ("bpf:
Implement support for adding hidden subprogs") (or technically, the next
commit as 335d1c5b5452 didn't have any user). So maybe I can resend as a
first patch (with you as a co-author) and the following diff (took the
opportunity to simplify the logic on the assumption that we always have
the main "subprog").

Not sure if it would need to be sent to bpf instead of bpf-next. Maybe
keep the patchset on bpf-next, but add "Cc: stable@vger.kernel.org"?

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 03f9e16c2abe..8dfe7da76258 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -18274,19 +18274,15 @@ static int do_check_main(struct bpf_verifier_env *env)
 
 static void print_verification_stats(struct bpf_verifier_env *env)
 {
-       int i;
+       /* Skip over hidden subprogs which are not verified. */
+       int i, subprog_cnt = env->subprog_cnt - env->hidden_subprog_cnt;
 
        if (env->log.level & BPF_LOG_STATS) {
                verbose(env, "verification time %lld usec\n",
                        div_u64(env->verification_time, 1000));
-               verbose(env, "stack depth ");
-               for (i = 0; i < env->subprog_cnt; i++) {
-                       u32 depth = env->subprog_info[i].stack_depth;
-
-                       verbose(env, "%d", depth);
-                       if (i + 1 < env->subprog_cnt)
-                               verbose(env, "+");
-               }
+               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, "processed %d insns (limit %d) max_states_per_insn %d "


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs
  2026-04-29 14:01       ` Paul Chaignon
@ 2026-04-29 15:53         ` Paul Chaignon
  2026-04-29 22:17           ` Kumar Kartikeya Dwivedi
  0 siblings, 1 reply; 10+ messages in thread
From: Paul Chaignon @ 2026-04-29 15:53 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi; +Cc: sashiko, bpf

On Wed, Apr 29, 2026 at 04:01:25PM +0200, Paul Chaignon wrote:
> On Wed, Apr 29, 2026 at 02:07:33AM +0200, Kumar Kartikeya Dwivedi wrote:
> > On Wed Apr 29, 2026 at 1:55 AM CEST, Kumar Kartikeya Dwivedi wrote:
> > > On Wed Apr 29, 2026 at 1:08 AM CEST, sashiko-bot wrote:
> > >> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > >> - [High] Out-of-bounds read in `bpf_subprog_is_global()` when printing verification stats for programs with hidden subprograms.
> > >> --
> > >>
> > >> commit 08128f883e58301e0f97b1d4a94656be63b8f790
> > >> Author: Paul Chaignon <paul.chaignon@gmail.com>
> > >>
> > >> bpf: Print breakdown of insns processed by subprogs
> > >>
> > >> This patch tracks the number of instructions processed by each global
> > >> function (subprog) during BPF verification. It then reports this
> > >> breakdown in the verifier logs under BPF_LOG_STATS.
> > >>
> > >>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > >>> index 03f9e16c2abe..b48389b48eb6 100644
> > >>> --- a/kernel/bpf/verifier.c
> > >>> +++ b/kernel/bpf/verifier.c
> > >>
> > >> [ ... ]
> > >>
> > >>> @@ -18288,6 +18295,13 @@ static void print_verification_stats(struct bpf_verifier_env *env)
> > >>>  				verbose(env, "+");
> > >>>  		}
> > >>>  		verbose(env, "\n");
> > >>> +		verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
> > >>> +		for (i = 1; i < env->subprog_cnt; i++) {
> > >>> +			if (!bpf_subprog_is_global(env, i))
> > >>
> > >> Could this introduce a regression due to an out-of-bounds read on the
> > >> func_info_aux array if there are hidden subprograms?
> > >>
> > >> At load time, if BTF is present, env->prog->aux->func_info_aux is allocated
> > >> with exactly func_info_cnt elements, and env->subprog_cnt initially matches
> > >> func_info_cnt.
> > >>
> > >> During verification, if a feature like bpf_throw is used without an explicit
> > >> callback, add_hidden_subprog() is called which increments env->subprog_cnt
> > >> but does not resize the func_info_aux array.
> > >>
> > >> Since this loop iterates up to the updated env->subprog_cnt, when i reaches
> > >> the index of a hidden subprogram, bpf_subprog_is_global() will access
> > >> aux[i].linkage where i >= func_info_cnt:
> > >>
> > >> kernel/bpf/verifier.c:bpf_subprog_is_global() {
> > >> 	struct bpf_func_info_aux *aux = env->prog->aux->func_info_aux;
> > >>
> > >> 	return aux && aux[subprog].linkage == BTF_FUNC_GLOBAL;
> > >> }
> > >>
> > >> Is it possible to skip hidden subprograms here, or limit the check to
> > >> func_info_cnt?
> > >
> > > Don't see it happening, since hidden subprogs never get verified, so we won't
> > > invoke bpf_subprog_is_global() with such a subprog index.
> > 
> > Ah, no, stupid me. We get here after fixing up and adding the hidden subprog. So
> > we can still do OOB since subprog_cnt includes the hidden_subprog_cnt. How about
> > the following as a fix? I checked over other places where we iterate over all of
> > the subprogs and those look fine, so instead of changing bpf_subprog_is_global()
> > we can adjust this function to only consider real subprogs. Didn't compile test.
> 
> That's a nice find! I also doubted it initially as we have that pattern
> everywhere.
> 
> It looks like this would be a fix for commit 335d1c5b5452 ("bpf:
> Implement support for adding hidden subprogs") (or technically, the next
> commit as 335d1c5b5452 didn't have any user). So maybe I can resend as a
> first patch (with you as a co-author) and the following diff (took the
> opportunity to simplify the logic on the assumption that we always have
> the main "subprog").

Just saw that subprog_info is statically allocated (contrary to
func_info_aux) so we wouldn't get an OOB on the existing code, just
some garbage "+0" on the stack depths.

> 
> Not sure if it would need to be sent to bpf instead of bpf-next. Maybe
> keep the patchset on bpf-next, but add "Cc: stable@vger.kernel.org"?

[...]


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs
  2026-04-29 15:53         ` Paul Chaignon
@ 2026-04-29 22:17           ` Kumar Kartikeya Dwivedi
  0 siblings, 0 replies; 10+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-29 22:17 UTC (permalink / raw)
  To: Paul Chaignon; +Cc: sashiko, bpf

On Wed, 29 Apr 2026 at 17:53, Paul Chaignon <paul.chaignon@gmail.com> wrote:
>
> On Wed, Apr 29, 2026 at 04:01:25PM +0200, Paul Chaignon wrote:
> > On Wed, Apr 29, 2026 at 02:07:33AM +0200, Kumar Kartikeya Dwivedi wrote:
> > > On Wed Apr 29, 2026 at 1:55 AM CEST, Kumar Kartikeya Dwivedi wrote:
> > > > On Wed Apr 29, 2026 at 1:08 AM CEST, sashiko-bot wrote:
> > > >> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > > >> - [High] Out-of-bounds read in `bpf_subprog_is_global()` when printing verification stats for programs with hidden subprograms.
> > > >> --
> > > >>
> > > >> commit 08128f883e58301e0f97b1d4a94656be63b8f790
> > > >> Author: Paul Chaignon <paul.chaignon@gmail.com>
> > > >>
> > > >> bpf: Print breakdown of insns processed by subprogs
> > > >>
> > > >> This patch tracks the number of instructions processed by each global
> > > >> function (subprog) during BPF verification. It then reports this
> > > >> breakdown in the verifier logs under BPF_LOG_STATS.
> > > >>
> > > >>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > >>> index 03f9e16c2abe..b48389b48eb6 100644
> > > >>> --- a/kernel/bpf/verifier.c
> > > >>> +++ b/kernel/bpf/verifier.c
> > > >>
> > > >> [ ... ]
> > > >>
> > > >>> @@ -18288,6 +18295,13 @@ static void print_verification_stats(struct bpf_verifier_env *env)
> > > >>>                                 verbose(env, "+");
> > > >>>                 }
> > > >>>                 verbose(env, "\n");
> > > >>> +               verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
> > > >>> +               for (i = 1; i < env->subprog_cnt; i++) {
> > > >>> +                       if (!bpf_subprog_is_global(env, i))
> > > >>
> > > >> Could this introduce a regression due to an out-of-bounds read on the
> > > >> func_info_aux array if there are hidden subprograms?
> > > >>
> > > >> At load time, if BTF is present, env->prog->aux->func_info_aux is allocated
> > > >> with exactly func_info_cnt elements, and env->subprog_cnt initially matches
> > > >> func_info_cnt.
> > > >>
> > > >> During verification, if a feature like bpf_throw is used without an explicit
> > > >> callback, add_hidden_subprog() is called which increments env->subprog_cnt
> > > >> but does not resize the func_info_aux array.
> > > >>
> > > >> Since this loop iterates up to the updated env->subprog_cnt, when i reaches
> > > >> the index of a hidden subprogram, bpf_subprog_is_global() will access
> > > >> aux[i].linkage where i >= func_info_cnt:
> > > >>
> > > >> kernel/bpf/verifier.c:bpf_subprog_is_global() {
> > > >>  struct bpf_func_info_aux *aux = env->prog->aux->func_info_aux;
> > > >>
> > > >>  return aux && aux[subprog].linkage == BTF_FUNC_GLOBAL;
> > > >> }
> > > >>
> > > >> Is it possible to skip hidden subprograms here, or limit the check to
> > > >> func_info_cnt?
> > > >
> > > > Don't see it happening, since hidden subprogs never get verified, so we won't
> > > > invoke bpf_subprog_is_global() with such a subprog index.
> > >
> > > Ah, no, stupid me. We get here after fixing up and adding the hidden subprog. So
> > > we can still do OOB since subprog_cnt includes the hidden_subprog_cnt. How about
> > > the following as a fix? I checked over other places where we iterate over all of
> > > the subprogs and those look fine, so instead of changing bpf_subprog_is_global()
> > > we can adjust this function to only consider real subprogs. Didn't compile test.
> >
> > That's a nice find! I also doubted it initially as we have that pattern
> > everywhere.
> >
> > It looks like this would be a fix for commit 335d1c5b5452 ("bpf:
> > Implement support for adding hidden subprogs") (or technically, the next
> > commit as 335d1c5b5452 didn't have any user). So maybe I can resend as a
> > first patch (with you as a co-author) and the following diff (took the
> > opportunity to simplify the logic on the assumption that we always have
> > the main "subprog").
>
> Just saw that subprog_info is statically allocated (contrary to
> func_info_aux) so we wouldn't get an OOB on the existing code, just
> some garbage "+0" on the stack depths.

Yeah, I don't think it was buggy before this, e.g. adjust_btf_func()
explicitly avoids touching func_info for this reason.
Just feel free to fold the diff into your commit and resend, no need
to send a separate change or make me co author.

>
> >
> > Not sure if it would need to be sent to bpf instead of bpf-next. Maybe
> > keep the patchset on bpf-next, but add "Cc: stable@vger.kernel.org"?
>
> [...]
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-04-29 22:18 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-28 14:55 [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs Paul Chaignon
2026-04-28 14:55 ` [PATCH bpf-next 2/2] selftests/bpf: Test insns processed breakdown Paul Chaignon
2026-04-28 16:18   ` Kumar Kartikeya Dwivedi
2026-04-28 23:14   ` sashiko-bot
2026-04-28 23:08 ` [PATCH bpf-next 1/2] bpf: Print breakdown of insns processed by subprogs sashiko-bot
2026-04-28 23:55   ` Kumar Kartikeya Dwivedi
2026-04-29  0:07     ` Kumar Kartikeya Dwivedi
2026-04-29 14:01       ` Paul Chaignon
2026-04-29 15:53         ` Paul Chaignon
2026-04-29 22:17           ` Kumar Kartikeya Dwivedi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox