Re: [PATCH bpf] bpf: fix tracking of stack size for var-off access

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Nov 21, 2023 at 1:46 AM Andrii Nakryiko
<andrii.nakryiko@xxxxxxxxx> wrote:
>
>
> It *feels* like this stack depth update *and* growing allocated stack
> slots should happen somewhere in check_stack_access_within_bounds() or
> right after it. It shouldn't matter whether we read or write to the
> stack slot: either way that slot becomes part of the verifier state
> that we should take into account during state comparison. Eduard not
> so long ago added a change that allows reading STACK_INVALID slots, so
> it's completely valid to read something that was never written to (and
> so grow_stack_state() wasn't called for that slot, as it is
> implemented right now). So I think we should fix that.
>

Agree. The following cases are currently confusing to me.

The verifier accepts the following program, which goes from #4 to #8
and directly read the stack at runtime without any previous write:
func#0 @0
0: R1=ctx() R10=fp0
0: (bf) r6 = r10                      ; R6_w=fp0 R10=fp0
1: (85) call bpf_get_prandom_u32#7    ; R0_w=scalar()
2: (bf) r3 = r0                       ; R0_w=scalar(id=1) R3_w=scalar(id=1)
3: (bf) r8 = r0                       ; R0_w=scalar(id=1) R8_w=scalar(id=1)
4: (4e) if w0 & w3 goto pc+3          ; R0_w=scalar(id=1) R3_w=scalar(id=1)
5: (63) *(u32 *)(r6 -196) = r3        ; R3_w=scalar(id=1) R6_w=fp0
fp-200=mmmm????
6: (18) r7 = 0x19                     ; R7=25
8: (61) r7 = *(u32 *)(r6 -200)        ; R6=fp0
R7_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))
fp-200=mmmm????
9: (95) exit

from 4 to 8: safe
verification time 358 usec
stack depth 200
processed 10 insns (limit 1000000) max_states_per_insn 0 total_states
1 peak_states 1 mark_read 1

The state is pruned, because of this:
static bool stacksafe(...)
         ....
         if (env->allow_uninit_stack &&
             old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_MISC)
             continue;

Yet, the sample direct read would be rejected:

func#0 @0
0: R1=ctx() R10=fp0
0: (bf) r6 = r10                      ; R6_w=fp0 R10=fp0
1: (61) r7 = *(u32 *)(r6 -200)
invalid read from stack R6 off=-200 size=4

Eduard, you added support for reading uninit slots, should we also add something
like the following:

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8c2d31aa3d31..aa861d2da240 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6446,7 +6446,7 @@ static int check_stack_slot_within_bounds(int off,
 {
        int min_valid_off;

-       if (t == BPF_WRITE)
+       if (t == BPF_WRITE || env->allow_uninit_stack)
                min_valid_off = -MAX_BPF_STACK;
        else
                min_valid_off = -state->allocated_stack;





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux