> On Jan 7, 2020, at 11:25 PM, Alexei Starovoitov <ast@xxxxxxxxxx> wrote: > > New llvm and old llvm with libbpf help produce BTF that distinguish global and > static functions. Unlike arguments of static function the arguments of global > functions cannot be removed or optimized away by llvm. The compiler has to use > exactly the arguments specified in a function prototype. The argument type > information allows the verifier validate each global function independently. > For now only supported argument types are pointer to context and scalars. In > the future pointers to structures, sizes, pointer to packet data can be > supported as well. Consider the following example: [...] > The type information and static/global kind is preserved after the verification > hence in the above example global function f2() and f3() can be replaced later > by equivalent functions with the same types that are loaded and verified later > without affecting safety of this main() program. Such replacement (re-linking) > of global functions is a subject of future patches. > > Signed-off-by: Alexei Starovoitov <ast@xxxxxxxxxx> > --- > include/linux/bpf.h | 7 +- > include/linux/bpf_verifier.h | 7 +- > include/uapi/linux/btf.h | 6 + > kernel/bpf/btf.c | 147 +++++++++++++++++----- > kernel/bpf/verifier.c | 228 +++++++++++++++++++++++++++-------- > 5 files changed, 317 insertions(+), 78 deletions(-) > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h > index b14e51d56a82..ceb5b6c13abc 100644 > --- a/include/linux/bpf.h > +++ b/include/linux/bpf.h > @@ -558,6 +558,7 @@ static inline void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, > #endif > > struct bpf_func_info_aux { > + u32 linkage; How about we use u16 or even u8 for linkage? We are using BTF_INFO_VLEN() which is 16-bit long. Maybe we should save some bits for future extensions? > bool unreliable; > }; > > @@ -1006,7 +1007,11 @@ int btf_distill_func_proto(struct bpf_verifier_log *log, > const char *func_name, > struct btf_func_model *m); > > -int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog); > +struct bpf_reg_state; > +int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, > + struct bpf_reg_state *regs); > +int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog, > + struct bpf_reg_state *reg); > > struct bpf_prog *bpf_prog_by_id(u32 id); > [...] > @@ -3535,11 +3537,12 @@ btf_get_prog_ctx_type(struct bpf_verifier_log *log, struct btf *btf, > static int btf_translate_to_vmlinux(struct bpf_verifier_log *log, > struct btf *btf, > const struct btf_type *t, > - enum bpf_prog_type prog_type) > + enum bpf_prog_type prog_type, > + int arg) > { > const struct btf_member *prog_ctx_type, *kern_ctx_type; > > - prog_ctx_type = btf_get_prog_ctx_type(log, btf, t, prog_type); > + prog_ctx_type = btf_get_prog_ctx_type(log, btf, t, prog_type, arg); > if (!prog_ctx_type) > return -ENOENT; > kern_ctx_type = prog_ctx_type + 1; > @@ -3700,7 +3703,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, > info->btf_id = t->type; > > if (tgt_prog) { > - ret = btf_translate_to_vmlinux(log, btf, t, tgt_prog->type); > + ret = btf_translate_to_vmlinux(log, btf, t, tgt_prog->type, arg); > if (ret > 0) { > info->btf_id = ret; > return true; > @@ -4043,11 +4046,10 @@ int btf_distill_func_proto(struct bpf_verifier_log *log, > return 0; > } > > -int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog) > +/* Compare BTF of a function with given bpf_reg_state */ > +int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, > + struct bpf_reg_state *reg) I think we need more comments for the retval of btf_check_func_arg_match(). > { > - struct bpf_verifier_state *st = env->cur_state; > - struct bpf_func_state *func = st->frame[st->curframe]; > - struct bpf_reg_state *reg = func->regs; > struct bpf_verifier_log *log = &env->log; > struct bpf_prog *prog = env->prog; > struct btf *btf = prog->aux->btf; [...] > + > +/* Convert BTF of a function into bpf_reg_state if possible */ > +int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog, > + struct bpf_reg_state *reg) > +{ > + struct bpf_verifier_log *log = &env->log; > + struct bpf_prog *prog = env->prog; > + struct btf *btf = prog->aux->btf; > + const struct btf_param *args; > + const struct btf_type *t; > + u32 i, nargs, btf_id; > + const char *tname; > + > + if (!prog->aux->func_info || > + prog->aux->func_info_aux[subprog].linkage != BTF_FUNC_GLOBAL) { > + bpf_log(log, "Verifier bug\n"); IIUC, this should never happen? Maybe we need more details in the log, and maybe also WARN_ONCE()? > + return -EFAULT; > + } > + > + btf_id = prog->aux->func_info[subprog].type_id; > + if (!btf_id) { > + bpf_log(log, "Global functions need valid BTF\n"); > + return -EINVAL; > + } > + > + t = btf_type_by_id(btf, btf_id); > + if (!t || !btf_type_is_func(t)) { > + bpf_log(log, "BTF of func#%d doesn't point to KIND_FUNC\n", > + subprog); > + return -EINVAL; > + } > + tname = btf_name_by_offset(btf, t->name_off); > + > + if (log->level & BPF_LOG_LEVEL) > + bpf_log(log, "Validating %s() func#%d...\n", > + tname, subprog); > + > + if (prog->aux->func_info_aux[subprog].unreliable) { > + bpf_log(log, "Verifier bug in function %s()\n", tname); > + return -EFAULT; Why -EFAULT instead of -EINVAL? I think we treat them the same? I guess this to highlight verifier bug vs. libbpf/llvm bug? How about we use WARN_ONCE() for all verifier bug? > + } > + > + t = btf_type_by_id(btf, t->type); > + if (!t || !btf_type_is_func_proto(t)) { > + bpf_log(log, "Invalid type of function %s()\n", tname); > + return -EINVAL; > + } > + args = (const struct btf_param *)(t + 1); > + nargs = btf_type_vlen(t); > + if (nargs > 5) { > + bpf_log(log, "Global function %s() with %d > 5 args. Buggy llvm.\n", > + tname, nargs); > + return -EINVAL; > + } > + /* check that function returns int */ > + t = btf_type_by_id(btf, t->type); > + while (btf_type_is_modifier(t)) > + t = btf_type_by_id(btf, t->type); > + if (!btf_type_is_int(t) && !btf_type_is_enum(t)) { > + bpf_log(log, > + "Global function %s() doesn't return scalar. Only those are supported.\n", > + tname); > + return -EINVAL; > + } > + /* Convert BTF function arguments into verifier types. > + * Only PTR_TO_CTX and SCALAR are supported atm. > + */ > + for (i = 0; i < nargs; i++) { > + t = btf_type_by_id(btf, args[i].type); > + while (btf_type_is_modifier(t)) > + t = btf_type_by_id(btf, t->type); > + if (btf_type_is_int(t) || btf_type_is_enum(t)) { > + reg[i + 1].type = SCALAR_VALUE; > + continue; > + } > + if (btf_type_is_ptr(t) && > + btf_get_prog_ctx_type(log, btf, t, prog->type, i)) { > + reg[i + 1].type = PTR_TO_CTX; > + continue; > + } > + bpf_log(log, "Arg#%d type %s in %s() is not supported yet.\n", > + i, btf_kind_str[BTF_INFO_KIND(t->info)], tname); > + return -EINVAL; > + } > return 0; > } [...]