bpf: account for current allocated stack depth in widen_imprecise_scalars()
Subsystems (4)
Eduard Zingerman
maintainerPATCH 1/2 bpf: account for current allocated stack depth in widen_imprecise_scalars()
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 loopHere, 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.
--- kernel/bpf/verifier.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
kernel/bpf/verifier.c +4 -2
@@ -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 ;
Signature · 1 line
2.51.1
Eduard Zingerman
maintainerPATCH 2/2 selftests/bpf: widen_imprecise_scalars() and different stack depth
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 loopiterator_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().
--- .../selftests/bpf/progs/iters_looping.c | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+)
tools/testing/selftests/bpf/progs/iters_looping.c +53 -0
@@ -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; + + asmvolatile ( +"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. +*/ + asmvolatile ( +"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); +}
Signature · 1 line
2.51.1
This series was applied to bpf/bpf.git (master) by Alexei Starovoitov <[email protected]>:
Hello:
This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <[email protected]>:
Quoted 11 lines · 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_stis an ancestor of thequeued_stin the explored states tree. This ancestor is not guaranteed to have same allocated stack depth asqueued_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!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html