Re: [PATCH v5 bpf-next 17/23] bpf: generalize reg_set_min_max() to handle two sets of two registers

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

 



On Mon, Oct 30, 2023 at 7:02 PM Alexei Starovoitov
<alexei.starovoitov@xxxxxxxxx> wrote:
>
> On Fri, Oct 27, 2023 at 11:13:40AM -0700, Andrii Nakryiko wrote:
> >  static void reg_set_min_max(struct bpf_reg_state *true_reg1,
> > +                         struct bpf_reg_state *true_reg2,
> >                           struct bpf_reg_state *false_reg1,
> > -                         u64 val, u32 val32,
> > +                         struct bpf_reg_state *false_reg2,
> >                           u8 opcode, bool is_jmp32)
> >  {
> > -     struct tnum false_32off = tnum_subreg(false_reg1->var_off);
> > -     struct tnum false_64off = false_reg1->var_off;
> > -     struct tnum true_32off = tnum_subreg(true_reg1->var_off);
> > -     struct tnum true_64off = true_reg1->var_off;
> > -     s64 sval = (s64)val;
> > -     s32 sval32 = (s32)val32;
> > -
> > -     /* If the dst_reg is a pointer, we can't learn anything about its
> > -      * variable offset from the compare (unless src_reg were a pointer into
> > -      * the same object, but we don't bother with that.
> > -      * Since false_reg1 and true_reg1 have the same type by construction, we
> > -      * only need to check one of them for pointerness.
> > +     struct tnum false_32off, false_64off;
> > +     struct tnum true_32off, true_64off;
> > +     u64 val;
> > +     u32 val32;
> > +     s64 sval;
> > +     s32 sval32;
> > +
> > +     /* If either register is a pointer, we can't learn anything about its
> > +      * variable offset from the compare (unless they were a pointer into
> > +      * the same object, but we don't bother with that).
> >        */
> > -     if (__is_pointer_value(false, false_reg1))
>
> The removal of the above check, but not the comment was surprising and concerning,
> so I did a bit of git-archaeology.
> It was added in commit f1174f77b50c ("bpf/verifier: rework value tracking")
> back in 2017 !
> and in that commit reg_set_min_max() was always called with reg == scalar.
> It looked like premature check. Then I spotted a comment in that commit:
>   * this is only legit if both are scalars (or pointers to the same
>   * object, I suppose, but we don't support that right now), because
>   * otherwise the different base pointers mean the offsets aren't
>   * comparable.
> so the intent back then was to generalize reg_set_min_max() to be used with pointers too,
> but we never got around to do that and the comment now reads:

Yeah, it shouldn't be too hard to "generalize" to pointer vs pointer,
if we ensure they point to exactly the same thing (I haven't thought
much about how), because beyond that it's still basically SCALAR
offsets. But I figured it's out of scope for these changes :)

>   * this is only legit if both are scalars (or pointers to the same
>   * object, I suppose, see the PTR_MAYBE_NULL related if block below),
>   * because otherwise the different base pointers mean the offsets aren't
>   * comparable.
>
> So please remove is_pointer check and remove the comment,

So I'm a bit confused. I did remove __is_pointer_value() check, but I
still need to guard against having pointers, which is why I have:

if (false_reg1->type != SCALAR_VALUE || false_reg2->type != SCALAR_VALUE).
    return;

I think I need this check, because reg_set_min_max() can be called
from check_cond_jmp_op() with pointer regs, and we shouldn't try to
adjust them. Or am I missing something? And the comment I have here
now:

+       /* If either register is a pointer, we can't learn anything about its
+        * variable offset from the compare (unless they were a pointer into
+        * the same object, but we don't bother with that).
         */

is trying to explain that we don't really adjust two pointers.

> and fixup the comment in check_cond_jmp_op() where reg_set_min_max().

I have this locally for now, please let me know if this is fine or you
had something else in mind:

-/* Adjusts the register min/max values in the case that the dst_reg is the
- * variable register that we are working on, and src_reg is a constant or we're
- * simply doing a BPF_K check.
- * In JEQ/JNE cases we also adjust the var_off values.
+/* Adjusts the register min/max values in the case that the dst_reg and
+ * src_reg are both SCALAR_VALUE registers (or we are simply doing a BPF_K
+ * check, in which case we havea fake SCALAR_VALUE representing insn->imm).
+ * Technically we can do similar adjustments for pointers to the same object,
+ * but we don't support that right now.
  */
 static void reg_set_min_max(struct bpf_reg_state *true_reg1,
                            struct bpf_reg_state *true_reg2,
@@ -14884,13 +14885,6 @@ static int check_cond_jmp_op(struct
bpf_verifier_env *env,
                return -EFAULT;
        other_branch_regs = other_branch->frame[other_branch->curframe]->regs;

-       /* detect if we are comparing against a constant value so we can adjust
-        * our min/max values for our dst register.
-        * this is only legit if both are scalars (or pointers to the same
-        * object, I suppose, see the PTR_MAYBE_NULL related if block below),
-        * because otherwise the different base pointers mean the offsets aren't
-        * comparable.
-        */
        if (BPF_SRC(insn->code) == BPF_X) {
                reg_set_min_max(&other_branch_regs[insn->dst_reg],
                                &other_branch_regs[insn->src_reg],





[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