On Mon, Dec 07, 2020 at 09:31:40PM -0800, John Fastabend wrote: > Brendan Jackman wrote: > > The BPF_FETCH field can be set in bpf_insn.imm, for BPF_ATOMIC > > instructions, in order to have the previous value of the > > atomically-modified memory location loaded into the src register > > after an atomic op is carried out. > > > > Suggested-by: Yonghong Song <y...@fb.com> > > Signed-off-by: Brendan Jackman <jackm...@google.com> > > --- > > I like Yonghong suggestion > > #define BPF_ATOMIC_FETCH_ADD(SIZE, DST, SRC, OFF) \ > BPF_ATOMIC(SIZE, DST, SRC, OFF, BPF_ADD | BPF_FETCH) > > otherwise LGTM. One observation to consider below. > > Acked-by: John Fastabend <john.fastab...@gmail.com> > > > arch/x86/net/bpf_jit_comp.c | 4 ++++ > > include/linux/filter.h | 1 + > > include/uapi/linux/bpf.h | 3 +++ > > kernel/bpf/core.c | 13 +++++++++++++ > > kernel/bpf/disasm.c | 7 +++++++ > > kernel/bpf/verifier.c | 33 ++++++++++++++++++++++++--------- > > tools/include/linux/filter.h | 11 +++++++++++ > > tools/include/uapi/linux/bpf.h | 3 +++ > > 8 files changed, 66 insertions(+), 9 deletions(-) > > [...] > > > @@ -3652,8 +3656,20 @@ static int check_atomic(struct bpf_verifier_env > > *env, int insn_idx, struct bpf_i > > return err; > > > > /* check whether we can write into the same memory */ > > - return check_mem_access(env, insn_idx, insn->dst_reg, insn->off, > > - BPF_SIZE(insn->code), BPF_WRITE, -1, true); > > + err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off, > > + BPF_SIZE(insn->code), BPF_WRITE, -1, true); > > + if (err) > > + return err; > > + > > + if (!(insn->imm & BPF_FETCH)) > > + return 0; > > + > > + /* check and record load of old value into src reg */ > > + err = check_reg_arg(env, insn->src_reg, DST_OP); > > This will mark the reg unknown. I think this is fine here. Might be nice > to carry bounds through though if possible
Ah, I hadn't thought of this. I think if I move this check_reg_arg to be before the first check_mem_access, and then (when BPF_FETCH) set the val_regno arg to load_reg, then the bounds from memory would get propagated back to the register: if (insn->imm & BPF_FETCH) { if (insn->imm == BPF_CMPXCHG) load_reg = BPF_REG_0; else load_reg = insn->src_reg; err = check_reg_arg(env, load_reg, DST_OP); if (err) return err; } else { load_reg = -1; } /* check wether we can read the memory */ err = check_mem_access(env, insn_index, insn->dst_reg, insn->off BPF_SIZE(insn->code), BPF_READ, load_reg, // <-- true); Is that the kind of thing you had in mind? > > + if (err) > > + return err; > > + > > + return 0; > > } > >