On Fri, Jul 22, 2022 at 10:58:06AM -0700, Joanne Koong wrote: > When a data slice is obtained from a dynptr (through the bpf_dynptr_data API), > the ref obj id of the dynptr must be found and then associated with the data > slice. > > The ref obj id of the dynptr must be found *before* the caller saved regs are > reset. Without this fix, the ref obj id tracking is not correct for > dynptrs that are at an offset from the frame pointer. > > Please also note that the data slice's ref obj id must be assigned after the > ret types are parsed, since RET_PTR_TO_ALLOC_MEM-type return regs get > zero-marked. > > Fixes: 34d4ef5775f776ec4b0d53a02d588bf3195cada6 ("bpf: Add dynptr data slices"); > Signed-off-by: Joanne Koong <joannelkoong@xxxxxxxxx> > --- Hi Joanne, Overall this looks great, thanks. Just a couple small comments / questions. > kernel/bpf/verifier.c | 62 ++++++++++++++++++++----------------------- > 1 file changed, 29 insertions(+), 33 deletions(-) > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index c59c3df0fea6..29987b2ea26f 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -5830,7 +5830,8 @@ static u32 stack_slot_get_id(struct bpf_verifier_env *env, struct bpf_reg_state > > static int check_func_arg(struct bpf_verifier_env *env, u32 arg, > struct bpf_call_arg_meta *meta, > - const struct bpf_func_proto *fn) > + const struct bpf_func_proto *fn, > + int func_id) Can we get the func_id from meta instead of adding another argument? It looks like the func_id is stored there before we call check_func_arg. > { > u32 regno = BPF_REG_1 + arg; > struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; > @@ -6040,23 +6041,33 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, > } > > meta->uninit_dynptr_regno = regno; > - } else if (!is_dynptr_reg_valid_init(env, reg, arg_type)) { > - const char *err_extra = ""; > + } else { > + if (!is_dynptr_reg_valid_init(env, reg, arg_type)) { > + const char *err_extra = ""; > > - switch (arg_type & DYNPTR_TYPE_FLAG_MASK) { > - case DYNPTR_TYPE_LOCAL: > - err_extra = "local "; > - break; > - case DYNPTR_TYPE_RINGBUF: > - err_extra = "ringbuf "; > - break; > - default: > - break; > - } > + switch (arg_type & DYNPTR_TYPE_FLAG_MASK) { > + case DYNPTR_TYPE_LOCAL: > + err_extra = "local "; > + break; > + case DYNPTR_TYPE_RINGBUF: > + err_extra = "ringbuf "; > + break; > + default: > + break; > + } > > - verbose(env, "Expected an initialized %sdynptr as arg #%d\n", > - err_extra, arg + 1); > - return -EINVAL; > + verbose(env, "Expected an initialized %sdynptr as arg #%d\n", > + err_extra, arg + 1); > + return -EINVAL; > + } > + if (func_id == BPF_FUNC_dynptr_data) { > + if (meta->ref_obj_id) { > + verbose(env, "verifier internal error: multiple refcounted args in BPF_FUNC_dynptr_data"); > + return -EFAULT; > + } > + /* Find the id of the dynptr we're tracking the reference of */ > + meta->ref_obj_id = stack_slot_get_id(env, reg); > + } > } > break; > case ARG_CONST_ALLOC_SIZE_OR_ZERO: > @@ -7227,7 +7238,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn > meta.func_id = func_id; > /* check args */ > for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) { > - err = check_func_arg(env, i, &meta, fn); > + err = check_func_arg(env, i, &meta, fn, func_id); > if (err) > return err; > } > @@ -7457,7 +7468,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn > if (type_may_be_null(regs[BPF_REG_0].type)) > regs[BPF_REG_0].id = ++env->id_gen; > > - if (is_ptr_cast_function(func_id)) { > + if (is_ptr_cast_function(func_id) || func_id == BPF_FUNC_dynptr_data) { Just a nit and my two cents, but IMO, is_ptr_cast_function() feels like a bit of an unclear function name. It's only used for this specific if statement, so maybe we should change that function name to something like is_meta_stored_ref() and just add BPF_FUNC_dynptr_data to that list? > /* For release_reference() */ > regs[BPF_REG_0].ref_obj_id = meta.ref_obj_id; > } else if (is_acquire_function(func_id, meta.map_ptr)) { > @@ -7469,21 +7480,6 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn > regs[BPF_REG_0].id = id; > /* For release_reference() */ > regs[BPF_REG_0].ref_obj_id = id; > - } else if (func_id == BPF_FUNC_dynptr_data) { > - int dynptr_id = 0, i; > - > - /* Find the id of the dynptr we're acquiring a reference to */ > - for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) { > - if (arg_type_is_dynptr(fn->arg_type[i])) { > - if (dynptr_id) { > - verbose(env, "verifier internal error: multiple dynptr args in func\n"); > - return -EFAULT; > - } > - dynptr_id = stack_slot_get_id(env, ®s[BPF_REG_1 + i]); > - } > - } > - /* For release_reference() */ > - regs[BPF_REG_0].ref_obj_id = dynptr_id; > } > > do_refine_retval_range(regs, fn->ret_type, func_id, &meta); > -- > 2.30.2 > Looks good otherwise, as mentioned above. Thanks, David