Re: [PATCH bpf v1 1/2] bpf: Suppress warning for non-zero off raw_tp arg NULL check

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

 



On Wed, 4 Dec 2024 at 17:37, Alexei Starovoitov
<alexei.starovoitov@xxxxxxxxx> wrote:
>
> On Tue, Dec 3, 2024 at 6:42 PM Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx> wrote:
> >
> > The fixed commit began marking raw_tp arguments as PTR_MAYBE_NULL to
> > avoid dead code elimination in the verifier, since raw_tp arguments
> > may actually be NULL at runtime. However, to preserve compatibility,
> > it simulated the raw_tp accesses as if the NULL marking was not present.
> >
> > One of the behaviors permitted by this simulation is offset modification
> > for NULL pointers. Typically, this pattern is rejected by the verifier,
> > and users make workarounds to prevent the compiler from producing such
> > patterns. However, now that it is allowed, when the compiler emits such
> > code, the offset modification is allowed and a PTR_MAYBE_NULL raw_tp arg
> > with non-zero off can be formed.
> >
> > The failing example program had the following pseudo-code:
> >
> > r0 = 1024;
> > r1 = ...; // r1 = trusted_or_null_(id=1)
> > r3 = r1;  // r3 = trusted_or_null_(id=1) r1 = trusted_or_null_(id=1)
> > r3 += r0; // r3 = trusted_or_null_(id=1, off=1024)
> > if r1 == 0 goto pc+X;
> >
> > At this point, while mark_ptr_or_null_reg will see PTR_MAYBE_NULL and
> > off == 0 for r1, it will notice non-zero off for r3, and the
> > WARN_ON_ONCE will fire, as the condition checks excluding register types
> > do not include raw_tp argument type.
> >
> > This is a pattern produced by LLVM, therefore it is hard to suppress it
> > everywhere in BPF programs.
> >
> > The right "generic" fix for this issue in general, will be permitting
> > offset modification for PTR_MAYBE_NULL pointers everywhere, and
> > enforcing that the instruction operand of a conditional jump has the
> > offset as zero. It's other copies may still have non-zero offset, and
> > that is fine. But this is more involved and will take longer to
> > integrate.
> >
> > Hence, for now, when we notice raw_tp args with off != 0 when unmarking
> > NULL modifier, simply allocate such pointer a fresh id and remove them
> > from the "id" set being currently operated on, and leave them as is
> > without removing PTR_MAYBE_NULL marking.
> >
> > Dereferencing such pointers will still work as the fixed commit allowed
> > it for raw_tp args.
> >
> > This will mean that still, all registers with a given id and off = 0
> > will be unmarked, even if a register with off != 0 is NULL checked, but
> > this shouldn't introducing any incorrectness. Just that any register
> > with off != 0 excludes itself from the marking exercise by reassigning
> > itself a new id.
> >
> > Fixes: cb4158ce8ec8 ("bpf: Mark raw_tp arguments with PTR_MAYBE_NULL")
> > Reported-by: Manu Bretelle <chantra@xxxxxxxx>
> > Signed-off-by: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx>
> > ---
> >  kernel/bpf/verifier.c | 44 ++++++++++++++++++++++++++++++++++++++-----
> >  1 file changed, 39 insertions(+), 5 deletions(-)
> >
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 1c4ebb326785..37504095a0bc 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -15335,7 +15335,8 @@ static int reg_set_min_max(struct bpf_verifier_env *env,
> >         return err;
> >  }
> >
> > -static void mark_ptr_or_null_reg(struct bpf_func_state *state,
> > +static void mark_ptr_or_null_reg(struct bpf_verifier_env *env,
> > +                                struct bpf_func_state *state,
> >                                  struct bpf_reg_state *reg, u32 id,
> >                                  bool is_null)
> >  {
> > @@ -15352,6 +15353,38 @@ static void mark_ptr_or_null_reg(struct bpf_func_state *state,
> >                  */
> >                 if (WARN_ON_ONCE(reg->smin_value || reg->smax_value || !tnum_equals_const(reg->var_off, 0)))
> >                         return;
> > +               /* Unlike the MEM_ALLOC and NON_OWN_REF cases explicitly tested
> > +                * below, where verifier will set off != 0, we allow users to
> > +                * modify offset of PTR_MAYBE_NULL raw_tp args to preserve
> > +                * compatibility since they were not marked NULL in older
> > +                * kernels. This however means we may see a non-zero offset
> > +                * register when marking them non-NULL in verifier state.
> > +                * This can happen for the operand of the instruction:
> > +                *
> > +                * r1 = trusted_or_null_(id=1);
> > +                * if r1 == 0 goto X;
> > +                *
> > +                * or a copy when LLVM produces code like below:
> > +                *
> > +                * r1 = trusted_or_null_(id=1);
> > +                * r3 = r1; // r3 = trusted_or_null(id=1)
> > +                * r3 += K; // r3 = trusted_or_null_(id=1, off=K)
> > +                * if r1 == 0 goto X; // see r3.off != 0 when unmarking _or_null
> > +                *
> > +                * The right fix would be more generic: lift the restriction on
> > +                * modifying reg->off for PTR_MAYBE_NULL pointers, and only
> > +                * enforce it for the instruction operand of a NULL check, while
> > +                * allowing non-zero off for other registers, but this is future
> > +                * work.
> > +                */
>
> I think the comment is too verbose.
> Especially considering that we're going to remove this hack in bpf-next.
>
> I can trim it to bare minimum while applying if you're ok ?

No objections.





[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