Re: [PATCH v6 bpf-next 1/4] bpf: Introduce may_goto instruction

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

 



On Wed, Mar 6, 2024 at 10:33 AM Andrii Nakryiko
<andrii.nakryiko@xxxxxxxxx> wrote:
>
> On Tue, Mar 5, 2024 at 7:19 PM Alexei Starovoitov
> <alexei.starovoitov@xxxxxxxxx> wrote:
> >
> > From: Alexei Starovoitov <ast@xxxxxxxxxx>
> >
> > Introduce may_goto instruction that from the verifier pov is similar to
> > open coded iterators bpf_for()/bpf_repeat() and bpf_loop() helper, but it
> > doesn't iterate any objects.
> > In assembly 'may_goto' is a nop most of the time until bpf runtime has to
> > terminate the program for whatever reason. In the current implementation
> > may_goto has a hidden counter, but other mechanisms can be used.
> > For programs written in C the later patch introduces 'cond_break' macro
> > that combines 'may_goto' with 'break' statement and has similar semantics:
> > cond_break is a nop until bpf runtime has to break out of this loop.
> > It can be used in any normal "for" or "while" loop, like
> >
> >   for (i = zero; i < cnt; cond_break, i++) {
> >
> > The verifier recognizes that may_goto is used in the program, reserves
> > additional 8 bytes of stack, initializes them in subprog prologue, and
> > replaces may_goto instruction with:
> > aux_reg = *(u64 *)(fp - 40)
> > if aux_reg == 0 goto pc+off
> > aux_reg -= 1
> > *(u64 *)(fp - 40) = aux_reg
> >
> > may_goto instruction can be used by LLVM to implement __builtin_memcpy,
> > __builtin_strcmp.
> >
> > may_goto is not a full substitute for bpf_for() macro.
> > bpf_for() doesn't have induction variable that verifiers sees,
> > so 'i' in bpf_for(i, 0, 100) is seen as imprecise and bounded.
> >
> > But when the code is written as:
> > for (i = 0; i < 100; cond_break, i++)
> > the verifier see 'i' as precise constant zero,
> > hence cond_break (aka may_goto) doesn't help to converge the loop.
> > A static or global variable can be used as a workaround:
> > static int zero = 0;
> > for (i = zero; i < 100; cond_break, i++) // works!
> >
> > may_goto works well with arena pointers that don't need to be bounds
> > checked on access. Load/store from arena returns imprecise unbounded
> > scalar and loops with may_goto pass the verifier.
> >
> > Reserve new opcode BPF_JMP | BPF_JCOND for may_goto insn.
> > JCOND stands for conditional pseudo jump.
> > Since goto_or_nop insn was proposed, it may use the same opcode.
> > may_goto vs goto_or_nop can be distinguished by src_reg:
> > code = BPF_JMP | BPF_JCOND
> > src_reg = 0 - may_goto
> > src_reg = 1 - goto_or_nop
> >
> > Acked-by: Andrii Nakryiko <andrii@xxxxxxxxxx>
> > Signed-off-by: Alexei Starovoitov <ast@xxxxxxxxxx>
> > ---
> >  include/linux/bpf_verifier.h   |   2 +
> >  include/uapi/linux/bpf.h       |   5 +
> >  kernel/bpf/core.c              |   1 +
> >  kernel/bpf/disasm.c            |   4 +
> >  kernel/bpf/verifier.c          | 163 +++++++++++++++++++++++++++------
> >  tools/include/uapi/linux/bpf.h |   5 +
> >  6 files changed, 150 insertions(+), 30 deletions(-)
> >
>
> [...]
>
> > @@ -14871,11 +14882,36 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
> >         int err;
> >
> >         /* Only conditional jumps are expected to reach here. */
> > -       if (opcode == BPF_JA || opcode > BPF_JSLE) {
> > +       if (opcode == BPF_JA || opcode > BPF_JCOND) {
> >                 verbose(env, "invalid BPF_JMP/JMP32 opcode %x\n", opcode);
> >                 return -EINVAL;
> >         }
> >
> > +       if (opcode == BPF_JCOND) {
> > +               struct bpf_verifier_state *cur_st = env->cur_state, *queued_st, *prev_st;
> > +               int idx = *insn_idx;
> > +
> > +               if (insn->code != (BPF_JMP | BPF_JCOND) ||
> > +                   insn->src_reg != BPF_MAY_GOTO ||
> > +                   insn->dst_reg || insn->imm || insn->off == 0) {
>
> Why rejecting insn->off == 0? It's still a legal instruction, even if
> not super useful in practice. insn->off == -1 is also weird, but legal
> (and not rejected in this case).

Legal, but nonsensical.
s390 hits it due to macro endian issue, so it's better to
reject early instead of wasting 1m verifier steps.





[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