BPF List
 help / color / mirror / Atom feed
* [PATCH bpf v1 1/2] bpf: account for current allocated stack depth in widen_imprecise_scalars()
@ 2025-11-14  2:57 Eduard Zingerman
  2025-11-14  2:57 ` [PATCH bpf v1 2/2] selftests/bpf: widen_imprecise_scalars() and different stack depth Eduard Zingerman
  2025-11-14 17:57 ` [PATCH bpf v1 1/2] bpf: account for current allocated stack depth in widen_imprecise_scalars() patchwork-bot+netdevbpf
  0 siblings, 2 replies; 3+ messages in thread
From: Eduard Zingerman @ 2025-11-14  2:57 UTC (permalink / raw)
  To: bpf, ast, andrii
  Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87,
	Emil Tsalapatis

The usage pattern for widen_imprecise_scalars() looks as follows:

    prev_st = find_prev_entry(env, ...);
    queued_st = push_stack(...);
    widen_imprecise_scalars(env, prev_st, queued_st);

Where prev_st is an ancestor of the queued_st in the explored states
tree. This ancestor is not guaranteed to have same allocated stack
depth as queued_st. E.g. in the following case:

    def main():
      for i in 1..2:
        foo(i)        // same callsite, differnt param

    def foo(i):
      if i == 1:
        use 128 bytes of stack
      iterator based loop

Here, for a second 'foo' call prev_st->allocated_stack is 128,
while queued_st->allocated_stack is much smaller.
widen_imprecise_scalars() needs to take this into account and avoid
accessing bpf_verifier_state->frame[*]->stack out of bounds.

Fixes: 2793a8b015f7 ("bpf: exact states comparison for iterator convergence checks")
Reported-by: Emil Tsalapatis <emil@etsalapatis.com>
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
 kernel/bpf/verifier.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8314518c8d93..fbe4bb91c564 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8866,7 +8866,7 @@ static int widen_imprecise_scalars(struct bpf_verifier_env *env,
 				   struct bpf_verifier_state *cur)
 {
 	struct bpf_func_state *fold, *fcur;
-	int i, fr;
+	int i, fr, num_slots;
 
 	reset_idmap_scratch(env);
 	for (fr = old->curframe; fr >= 0; fr--) {
@@ -8879,7 +8879,9 @@ static int widen_imprecise_scalars(struct bpf_verifier_env *env,
 					&fcur->regs[i],
 					&env->idmap_scratch);
 
-		for (i = 0; i < fold->allocated_stack / BPF_REG_SIZE; i++) {
+		num_slots = min(fold->allocated_stack / BPF_REG_SIZE,
+				fcur->allocated_stack / BPF_REG_SIZE);
+		for (i = 0; i < num_slots; i++) {
 			if (!is_spilled_reg(&fold->stack[i]) ||
 			    !is_spilled_reg(&fcur->stack[i]))
 				continue;
-- 
2.51.1


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

* [PATCH bpf v1 2/2] selftests/bpf: widen_imprecise_scalars() and different stack depth
  2025-11-14  2:57 [PATCH bpf v1 1/2] bpf: account for current allocated stack depth in widen_imprecise_scalars() Eduard Zingerman
@ 2025-11-14  2:57 ` Eduard Zingerman
  2025-11-14 17:57 ` [PATCH bpf v1 1/2] bpf: account for current allocated stack depth in widen_imprecise_scalars() patchwork-bot+netdevbpf
  1 sibling, 0 replies; 3+ messages in thread
From: Eduard Zingerman @ 2025-11-14  2:57 UTC (permalink / raw)
  To: bpf, ast, andrii; +Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87

A test case for a situation when widen_imprecise_scalars() is called
with old->allocated_stack > cur->allocated_stack. Test structure:

    def widening_stack_size_bug():
      r1 = 0
      for r6 in 0..1:
        iterator_with_diff_stack_depth(r1)
        r1 = 42

    def iterator_with_diff_stack_depth(r1):
      if r1 != 42:
        use 128 bytes of stack
      iterator based loop

iterator_with_diff_stack_depth() is verified with r1 == 0 first and
r1 == 42 next. Causing stack usage of 128 bytes on a first visit and 8
bytes on a second. Such arrangement triggered a KASAN error in
widen_imprecise_scalars().

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
 .../selftests/bpf/progs/iters_looping.c       | 53 +++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/tools/testing/selftests/bpf/progs/iters_looping.c b/tools/testing/selftests/bpf/progs/iters_looping.c
index 05fa5ce7fc59..d00fd570255a 100644
--- a/tools/testing/selftests/bpf/progs/iters_looping.c
+++ b/tools/testing/selftests/bpf/progs/iters_looping.c
@@ -161,3 +161,56 @@ int simplest_loop(void *ctx)
 
 	return 0;
 }
+
+__used
+static void iterator_with_diff_stack_depth(int x)
+{
+	struct bpf_iter_num iter;
+
+	asm volatile (
+		"if r1 == 42 goto 0f;"
+		"*(u64 *)(r10 - 128) = 0;"
+	"0:"
+		/* create iterator */
+		"r1 = %[iter];"
+		"r2 = 0;"
+		"r3 = 10;"
+		"call %[bpf_iter_num_new];"
+	"1:"
+		/* consume next item */
+		"r1 = %[iter];"
+		"call %[bpf_iter_num_next];"
+		"if r0 == 0 goto 2f;"
+		"goto 1b;"
+	"2:"
+		/* destroy iterator */
+		"r1 = %[iter];"
+		"call %[bpf_iter_num_destroy];"
+		:
+		: __imm_ptr(iter), ITER_HELPERS
+		: __clobber_common, "r6"
+	);
+}
+
+SEC("socket")
+__success
+__naked int widening_stack_size_bug(void *ctx)
+{
+	/*
+	 * Depending on iterator_with_diff_stack_depth() parameter value,
+	 * subprogram stack depth is either 8 or 128 bytes. Arrange values so
+	 * that it is 128 on a first call and 8 on a second. This triggered a
+	 * bug in verifier's widen_imprecise_scalars() logic.
+	 */
+	asm volatile (
+		"r6 = 0;"
+		"r1 = 0;"
+	"1:"
+		"call iterator_with_diff_stack_depth;"
+		"r1 = 42;"
+		"r6 += 1;"
+		"if r6 < 2 goto 1b;"
+		"r0 = 0;"
+		"exit;"
+		::: __clobber_all);
+}
-- 
2.51.1


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

* Re: [PATCH bpf v1 1/2] bpf: account for current allocated stack depth in widen_imprecise_scalars()
  2025-11-14  2:57 [PATCH bpf v1 1/2] bpf: account for current allocated stack depth in widen_imprecise_scalars() Eduard Zingerman
  2025-11-14  2:57 ` [PATCH bpf v1 2/2] selftests/bpf: widen_imprecise_scalars() and different stack depth Eduard Zingerman
@ 2025-11-14 17:57 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 3+ messages in thread
From: patchwork-bot+netdevbpf @ 2025-11-14 17:57 UTC (permalink / raw)
  To: Eduard Zingerman
  Cc: bpf, ast, andrii, daniel, martin.lau, kernel-team, yonghong.song,
	emil

Hello:

This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Thu, 13 Nov 2025 18:57:29 -0800 you wrote:
> The usage pattern for widen_imprecise_scalars() looks as follows:
> 
>     prev_st = find_prev_entry(env, ...);
>     queued_st = push_stack(...);
>     widen_imprecise_scalars(env, prev_st, queued_st);
> 
> Where prev_st is an ancestor of the queued_st in the explored states
> tree. This ancestor is not guaranteed to have same allocated stack
> depth as queued_st. E.g. in the following case:
> 
> [...]

Here is the summary with links:
  - [bpf,v1,1/2] bpf: account for current allocated stack depth in widen_imprecise_scalars()
    https://git.kernel.org/bpf/bpf/c/b0c8e6d3d866
  - [bpf,v1,2/2] selftests/bpf: widen_imprecise_scalars() and different stack depth
    https://git.kernel.org/bpf/bpf/c/6c762611fed7

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



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

end of thread, other threads:[~2025-11-14 17:57 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-14  2:57 [PATCH bpf v1 1/2] bpf: account for current allocated stack depth in widen_imprecise_scalars() Eduard Zingerman
2025-11-14  2:57 ` [PATCH bpf v1 2/2] selftests/bpf: widen_imprecise_scalars() and different stack depth Eduard Zingerman
2025-11-14 17:57 ` [PATCH bpf v1 1/2] bpf: account for current allocated stack depth in widen_imprecise_scalars() patchwork-bot+netdevbpf

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