Re: [PATCH bpf-next 1/2] bpf: handle fake register spill to stack with BPF_ST_MEM instruction

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

 



On Fri, Dec 8, 2023 at 6:15 PM Andrii Nakryiko
<andrii.nakryiko@xxxxxxxxx> wrote:
>
> On Fri, Dec 8, 2023 at 6:01 PM Eduard Zingerman <eddyz87@xxxxxxxxx> wrote:
> >
> > On Fri, 2023-12-08 at 17:09 -0800, Andrii Nakryiko wrote:
> > > When verifier validates BPF_ST_MEM instruction that stores known
> > > constant to stack (e.g., *(u64 *)(r10 - 8) = 123), it effectively spills
> > > a fake register with a constant (but initially imprecise) value to
> > > a stack slot. Because read-side logic treats it as a proper register
> > > fill from stack slot, we need to mark such stack slot initialization as
> > > INSN_F_STACK_ACCESS instruction to stop precision backtracking from
> > > missing it.
> > >
> > > Fixes: 41f6f64e6999 ("bpf: support non-r10 register spill/fill to/from stack in precision tracking")
> > > Signed-off-by: Andrii Nakryiko <andrii@xxxxxxxxxx>
> > > ---
> > >  kernel/bpf/verifier.c | 1 -
> > >  1 file changed, 1 deletion(-)
> > >
> > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > index fb690539d5f6..727a59e4a647 100644
> > > --- a/kernel/bpf/verifier.c
> > > +++ b/kernel/bpf/verifier.c
> > > @@ -4498,7 +4498,6 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
> > >               __mark_reg_known(&fake_reg, insn->imm);
> > >               fake_reg.type = SCALAR_VALUE;
> > >               save_register_state(env, state, spi, &fake_reg, size);
> > > -             insn_flags = 0; /* not a register spill */
> > >       } else if (reg && is_spillable_regtype(reg->type)) {
> > >               /* register containing pointer is being spilled into stack */
> > >               if (size != BPF_REG_SIZE) {
> >
> > So, the problem is that for some 'r5 = r10; *(u64 *)(r5 - 8) = 123'
> > backtracking won't reset precision mark for -8, right?
>
> no, the problem is that we won't stop at the right instruction. Let's
> say we have this sequence
>
> 1: *(u64 *)(r10 - 8) = 123;
> 2: r1 = *(u64 *)(r10 - 8);
> 3: if r1 == 123 goto +10;
> ...
>
> At 3: we want to set r1 to precise. We go back, see that at 2: we set
> r1 from fp-8 slot, so instead of looking for r1, we start looking for
> what set fp-8 now. So we go to 1:, and because it actually is not
> marked as INSN_F_STACK_ACCESS, we skip it, and keep looking further
> for what set fp-8. At some point we can go to parent state that didn't
> even have fp-8 stack slot allocated (or we can get out and then see
> that we haven't cleared all stack slot bits in our masks). So this
> patch makes it so that 1: is marked as setting fp-8 slot, and
> precision propagation will clear fp-8 from the mask.
>
> Now, the subtle thing here is that this doesn't happen with STACK_ZERO
> or STACK_MISC. Let's look at STACK_MISC/STACK_INVALID case.
>
> 1: *(u8 *)(r10 -1) = 123; /* now fp-8=m??????? */
> 2: r1 = *(u64 *)(r10 - 8); /* STACK_MISC read, r1 is set to unknown scalar */
> 3: if r1 == 123 goto +10;

small correction, with STACK_MISC conditional jump won't mark_precise,
we'd need some other instruction to trigger precision, but hopefully
the point is still clear

>
> Let's do analysis again. At 3: we mark r1 as precise, go back to 2:.
> Here 2: instruction is not marked as INSN_F_STACK_ACCESS because it
> wasn't stack fill due to STACK_MISC (that's handled in
> check_read_fixed_off logic). So mark_chain_precision() stops here
> because that instruction is resetting r1, so we clear r1 from the
> mask, but this instruction isn't STACK_ACCESS, so we don't look for
> fp-8 here.
>
> I think check_stack_write_fixed_off() can always set
> INSN_F_STACK_ACCESS, actually, maybe that would be easier to follow.
> Even when we write STACK_ZERO/STACK_MISC. It's only
> check_stack_read_fixed_off() that has to be careful and drop
> INSN_F_STACK_ACCESS if we didn't really fill the register state from
> the stack slot.
>
>
> > That's not a tragedy we just get more precision marks than needed,
> > however, I think that same logic applies to the MISC/ZERO case below.
>
> See above, MISC/ZERO is fine as is due to check_stack_read_fixed_off()
> not setting STACK_ACCESS bit, but I can also send a version that
> unconditionally sets INSNS_F_STACK_ACCESS in
> check_stack_write_fixed_off().
>
> > I'll look through the tests in the morning.
>
> Thanks, no rush!





[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