|
|
|
@ -1640,52 +1640,173 @@ static int add_subprog(struct bpf_verifier_env *env, int off)
|
|
|
|
|
return env->subprog_cnt - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define MAX_KFUNC_DESCS 256
|
|
|
|
|
#define MAX_KFUNC_BTFS 256
|
|
|
|
|
|
|
|
|
|
struct bpf_kfunc_desc {
|
|
|
|
|
struct btf_func_model func_model;
|
|
|
|
|
u32 func_id;
|
|
|
|
|
s32 imm;
|
|
|
|
|
u16 offset;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct bpf_kfunc_btf {
|
|
|
|
|
struct btf *btf;
|
|
|
|
|
struct module *module;
|
|
|
|
|
u16 offset;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MAX_KFUNC_DESCS 256
|
|
|
|
|
struct bpf_kfunc_desc_tab {
|
|
|
|
|
struct bpf_kfunc_desc descs[MAX_KFUNC_DESCS];
|
|
|
|
|
u32 nr_descs;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int kfunc_desc_cmp_by_id(const void *a, const void *b)
|
|
|
|
|
struct bpf_kfunc_btf_tab {
|
|
|
|
|
struct bpf_kfunc_btf descs[MAX_KFUNC_BTFS];
|
|
|
|
|
u32 nr_descs;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int kfunc_desc_cmp_by_id_off(const void *a, const void *b)
|
|
|
|
|
{
|
|
|
|
|
const struct bpf_kfunc_desc *d0 = a;
|
|
|
|
|
const struct bpf_kfunc_desc *d1 = b;
|
|
|
|
|
|
|
|
|
|
/* func_id is not greater than BTF_MAX_TYPE */
|
|
|
|
|
return d0->func_id - d1->func_id;
|
|
|
|
|
return d0->func_id - d1->func_id ?: d0->offset - d1->offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int kfunc_btf_cmp_by_off(const void *a, const void *b)
|
|
|
|
|
{
|
|
|
|
|
const struct bpf_kfunc_btf *d0 = a;
|
|
|
|
|
const struct bpf_kfunc_btf *d1 = b;
|
|
|
|
|
|
|
|
|
|
return d0->offset - d1->offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct bpf_kfunc_desc *
|
|
|
|
|
find_kfunc_desc(const struct bpf_prog *prog, u32 func_id)
|
|
|
|
|
find_kfunc_desc(const struct bpf_prog *prog, u32 func_id, u16 offset)
|
|
|
|
|
{
|
|
|
|
|
struct bpf_kfunc_desc desc = {
|
|
|
|
|
.func_id = func_id,
|
|
|
|
|
.offset = offset,
|
|
|
|
|
};
|
|
|
|
|
struct bpf_kfunc_desc_tab *tab;
|
|
|
|
|
|
|
|
|
|
tab = prog->aux->kfunc_tab;
|
|
|
|
|
return bsearch(&desc, tab->descs, tab->nr_descs,
|
|
|
|
|
sizeof(tab->descs[0]), kfunc_desc_cmp_by_id);
|
|
|
|
|
sizeof(tab->descs[0]), kfunc_desc_cmp_by_id_off);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id)
|
|
|
|
|
static struct btf *__find_kfunc_desc_btf(struct bpf_verifier_env *env,
|
|
|
|
|
s16 offset, struct module **btf_modp)
|
|
|
|
|
{
|
|
|
|
|
struct bpf_kfunc_btf kf_btf = { .offset = offset };
|
|
|
|
|
struct bpf_kfunc_btf_tab *tab;
|
|
|
|
|
struct bpf_kfunc_btf *b;
|
|
|
|
|
struct module *mod;
|
|
|
|
|
struct btf *btf;
|
|
|
|
|
int btf_fd;
|
|
|
|
|
|
|
|
|
|
tab = env->prog->aux->kfunc_btf_tab;
|
|
|
|
|
b = bsearch(&kf_btf, tab->descs, tab->nr_descs,
|
|
|
|
|
sizeof(tab->descs[0]), kfunc_btf_cmp_by_off);
|
|
|
|
|
if (!b) {
|
|
|
|
|
if (tab->nr_descs == MAX_KFUNC_BTFS) {
|
|
|
|
|
verbose(env, "too many different module BTFs\n");
|
|
|
|
|
return ERR_PTR(-E2BIG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bpfptr_is_null(env->fd_array)) {
|
|
|
|
|
verbose(env, "kfunc offset > 0 without fd_array is invalid\n");
|
|
|
|
|
return ERR_PTR(-EPROTO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (copy_from_bpfptr_offset(&btf_fd, env->fd_array,
|
|
|
|
|
offset * sizeof(btf_fd),
|
|
|
|
|
sizeof(btf_fd)))
|
|
|
|
|
return ERR_PTR(-EFAULT);
|
|
|
|
|
|
|
|
|
|
btf = btf_get_by_fd(btf_fd);
|
|
|
|
|
if (IS_ERR(btf))
|
|
|
|
|
return btf;
|
|
|
|
|
|
|
|
|
|
if (!btf_is_module(btf)) {
|
|
|
|
|
verbose(env, "BTF fd for kfunc is not a module BTF\n");
|
|
|
|
|
btf_put(btf);
|
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mod = btf_try_get_module(btf);
|
|
|
|
|
if (!mod) {
|
|
|
|
|
btf_put(btf);
|
|
|
|
|
return ERR_PTR(-ENXIO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b = &tab->descs[tab->nr_descs++];
|
|
|
|
|
b->btf = btf;
|
|
|
|
|
b->module = mod;
|
|
|
|
|
b->offset = offset;
|
|
|
|
|
|
|
|
|
|
sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]),
|
|
|
|
|
kfunc_btf_cmp_by_off, NULL);
|
|
|
|
|
}
|
|
|
|
|
if (btf_modp)
|
|
|
|
|
*btf_modp = b->module;
|
|
|
|
|
return b->btf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void bpf_free_kfunc_btf_tab(struct bpf_kfunc_btf_tab *tab)
|
|
|
|
|
{
|
|
|
|
|
if (!tab)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
while (tab->nr_descs--) {
|
|
|
|
|
module_put(tab->descs[tab->nr_descs].module);
|
|
|
|
|
btf_put(tab->descs[tab->nr_descs].btf);
|
|
|
|
|
}
|
|
|
|
|
kfree(tab);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env,
|
|
|
|
|
u32 func_id, s16 offset,
|
|
|
|
|
struct module **btf_modp)
|
|
|
|
|
{
|
|
|
|
|
struct btf *kfunc_btf;
|
|
|
|
|
|
|
|
|
|
if (offset) {
|
|
|
|
|
if (offset < 0) {
|
|
|
|
|
/* In the future, this can be allowed to increase limit
|
|
|
|
|
* of fd index into fd_array, interpreted as u16.
|
|
|
|
|
*/
|
|
|
|
|
verbose(env, "negative offset disallowed for kernel module function call\n");
|
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kfunc_btf = __find_kfunc_desc_btf(env, offset, btf_modp);
|
|
|
|
|
if (IS_ERR_OR_NULL(kfunc_btf)) {
|
|
|
|
|
verbose(env, "cannot find module BTF for func_id %u\n", func_id);
|
|
|
|
|
return kfunc_btf ?: ERR_PTR(-ENOENT);
|
|
|
|
|
}
|
|
|
|
|
return kfunc_btf;
|
|
|
|
|
}
|
|
|
|
|
return btf_vmlinux ?: ERR_PTR(-ENOENT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset)
|
|
|
|
|
{
|
|
|
|
|
const struct btf_type *func, *func_proto;
|
|
|
|
|
struct bpf_kfunc_btf_tab *btf_tab;
|
|
|
|
|
struct bpf_kfunc_desc_tab *tab;
|
|
|
|
|
struct bpf_prog_aux *prog_aux;
|
|
|
|
|
struct bpf_kfunc_desc *desc;
|
|
|
|
|
const char *func_name;
|
|
|
|
|
struct btf *desc_btf;
|
|
|
|
|
unsigned long addr;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
prog_aux = env->prog->aux;
|
|
|
|
|
tab = prog_aux->kfunc_tab;
|
|
|
|
|
btf_tab = prog_aux->kfunc_btf_tab;
|
|
|
|
|
if (!tab) {
|
|
|
|
|
if (!btf_vmlinux) {
|
|
|
|
|
verbose(env, "calling kernel function is not supported without CONFIG_DEBUG_INFO_BTF\n");
|
|
|
|
@ -1713,7 +1834,20 @@ static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id)
|
|
|
|
|
prog_aux->kfunc_tab = tab;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (find_kfunc_desc(env->prog, func_id))
|
|
|
|
|
if (!btf_tab && offset) {
|
|
|
|
|
btf_tab = kzalloc(sizeof(*btf_tab), GFP_KERNEL);
|
|
|
|
|
if (!btf_tab)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
prog_aux->kfunc_btf_tab = btf_tab;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
desc_btf = find_kfunc_desc_btf(env, func_id, offset, NULL);
|
|
|
|
|
if (IS_ERR(desc_btf)) {
|
|
|
|
|
verbose(env, "failed to find BTF for kernel function\n");
|
|
|
|
|
return PTR_ERR(desc_btf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (find_kfunc_desc(env->prog, func_id, offset))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (tab->nr_descs == MAX_KFUNC_DESCS) {
|
|
|
|
@ -1721,20 +1855,20 @@ static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id)
|
|
|
|
|
return -E2BIG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func = btf_type_by_id(btf_vmlinux, func_id);
|
|
|
|
|
func = btf_type_by_id(desc_btf, func_id);
|
|
|
|
|
if (!func || !btf_type_is_func(func)) {
|
|
|
|
|
verbose(env, "kernel btf_id %u is not a function\n",
|
|
|
|
|
func_id);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
func_proto = btf_type_by_id(btf_vmlinux, func->type);
|
|
|
|
|
func_proto = btf_type_by_id(desc_btf, func->type);
|
|
|
|
|
if (!func_proto || !btf_type_is_func_proto(func_proto)) {
|
|
|
|
|
verbose(env, "kernel function btf_id %u does not have a valid func_proto\n",
|
|
|
|
|
func_id);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func_name = btf_name_by_offset(btf_vmlinux, func->name_off);
|
|
|
|
|
func_name = btf_name_by_offset(desc_btf, func->name_off);
|
|
|
|
|
addr = kallsyms_lookup_name(func_name);
|
|
|
|
|
if (!addr) {
|
|
|
|
|
verbose(env, "cannot find address for kernel function %s\n",
|
|
|
|
@ -1745,12 +1879,13 @@ static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id)
|
|
|
|
|
desc = &tab->descs[tab->nr_descs++];
|
|
|
|
|
desc->func_id = func_id;
|
|
|
|
|
desc->imm = BPF_CALL_IMM(addr);
|
|
|
|
|
err = btf_distill_func_proto(&env->log, btf_vmlinux,
|
|
|
|
|
desc->offset = offset;
|
|
|
|
|
err = btf_distill_func_proto(&env->log, desc_btf,
|
|
|
|
|
func_proto, func_name,
|
|
|
|
|
&desc->func_model);
|
|
|
|
|
if (!err)
|
|
|
|
|
sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]),
|
|
|
|
|
kfunc_desc_cmp_by_id, NULL);
|
|
|
|
|
kfunc_desc_cmp_by_id_off, NULL);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1829,7 +1964,7 @@ static int add_subprog_and_kfunc(struct bpf_verifier_env *env)
|
|
|
|
|
} else if (bpf_pseudo_call(insn)) {
|
|
|
|
|
ret = add_subprog(env, i + insn->imm + 1);
|
|
|
|
|
} else {
|
|
|
|
|
ret = add_kfunc_call(env, insn->imm);
|
|
|
|
|
ret = add_kfunc_call(env, insn->imm, insn->off);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
@ -2166,12 +2301,17 @@ static int get_prev_insn_idx(struct bpf_verifier_state *st, int i,
|
|
|
|
|
static const char *disasm_kfunc_name(void *data, const struct bpf_insn *insn)
|
|
|
|
|
{
|
|
|
|
|
const struct btf_type *func;
|
|
|
|
|
struct btf *desc_btf;
|
|
|
|
|
|
|
|
|
|
if (insn->src_reg != BPF_PSEUDO_KFUNC_CALL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
func = btf_type_by_id(btf_vmlinux, insn->imm);
|
|
|
|
|
return btf_name_by_offset(btf_vmlinux, func->name_off);
|
|
|
|
|
desc_btf = find_kfunc_desc_btf(data, insn->imm, insn->off, NULL);
|
|
|
|
|
if (IS_ERR(desc_btf))
|
|
|
|
|
return "<error>";
|
|
|
|
|
|
|
|
|
|
func = btf_type_by_id(desc_btf, insn->imm);
|
|
|
|
|
return btf_name_by_offset(desc_btf, func->name_off);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* For given verifier state backtrack_insn() is called from the last insn to
|
|
|
|
@ -6530,23 +6670,29 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
|
|
|
|
struct bpf_reg_state *regs = cur_regs(env);
|
|
|
|
|
const char *func_name, *ptr_type_name;
|
|
|
|
|
u32 i, nargs, func_id, ptr_type_id;
|
|
|
|
|
struct module *btf_mod = NULL;
|
|
|
|
|
const struct btf_param *args;
|
|
|
|
|
struct btf *desc_btf;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
desc_btf = find_kfunc_desc_btf(env, insn->imm, insn->off, &btf_mod);
|
|
|
|
|
if (IS_ERR(desc_btf))
|
|
|
|
|
return PTR_ERR(desc_btf);
|
|
|
|
|
|
|
|
|
|
func_id = insn->imm;
|
|
|
|
|
func = btf_type_by_id(btf_vmlinux, func_id);
|
|
|
|
|
func_name = btf_name_by_offset(btf_vmlinux, func->name_off);
|
|
|
|
|
func_proto = btf_type_by_id(btf_vmlinux, func->type);
|
|
|
|
|
func = btf_type_by_id(desc_btf, func_id);
|
|
|
|
|
func_name = btf_name_by_offset(desc_btf, func->name_off);
|
|
|
|
|
func_proto = btf_type_by_id(desc_btf, func->type);
|
|
|
|
|
|
|
|
|
|
if (!env->ops->check_kfunc_call ||
|
|
|
|
|
!env->ops->check_kfunc_call(func_id)) {
|
|
|
|
|
!env->ops->check_kfunc_call(func_id, btf_mod)) {
|
|
|
|
|
verbose(env, "calling kernel function %s is not allowed\n",
|
|
|
|
|
func_name);
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check the arguments */
|
|
|
|
|
err = btf_check_kfunc_arg_match(env, btf_vmlinux, func_id, regs);
|
|
|
|
|
err = btf_check_kfunc_arg_match(env, desc_btf, func_id, regs);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
@ -6554,15 +6700,15 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
|
|
|
|
mark_reg_not_init(env, regs, caller_saved[i]);
|
|
|
|
|
|
|
|
|
|
/* Check return type */
|
|
|
|
|
t = btf_type_skip_modifiers(btf_vmlinux, func_proto->type, NULL);
|
|
|
|
|
t = btf_type_skip_modifiers(desc_btf, func_proto->type, NULL);
|
|
|
|
|
if (btf_type_is_scalar(t)) {
|
|
|
|
|
mark_reg_unknown(env, regs, BPF_REG_0);
|
|
|
|
|
mark_btf_func_reg_size(env, BPF_REG_0, t->size);
|
|
|
|
|
} else if (btf_type_is_ptr(t)) {
|
|
|
|
|
ptr_type = btf_type_skip_modifiers(btf_vmlinux, t->type,
|
|
|
|
|
ptr_type = btf_type_skip_modifiers(desc_btf, t->type,
|
|
|
|
|
&ptr_type_id);
|
|
|
|
|
if (!btf_type_is_struct(ptr_type)) {
|
|
|
|
|
ptr_type_name = btf_name_by_offset(btf_vmlinux,
|
|
|
|
|
ptr_type_name = btf_name_by_offset(desc_btf,
|
|
|
|
|
ptr_type->name_off);
|
|
|
|
|
verbose(env, "kernel function %s returns pointer type %s %s is not supported\n",
|
|
|
|
|
func_name, btf_type_str(ptr_type),
|
|
|
|
@ -6570,7 +6716,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
mark_reg_known_zero(env, regs, BPF_REG_0);
|
|
|
|
|
regs[BPF_REG_0].btf = btf_vmlinux;
|
|
|
|
|
regs[BPF_REG_0].btf = desc_btf;
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_BTF_ID;
|
|
|
|
|
regs[BPF_REG_0].btf_id = ptr_type_id;
|
|
|
|
|
mark_btf_func_reg_size(env, BPF_REG_0, sizeof(void *));
|
|
|
|
@ -6581,7 +6727,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
|
|
|
|
for (i = 0; i < nargs; i++) {
|
|
|
|
|
u32 regno = i + 1;
|
|
|
|
|
|
|
|
|
|
t = btf_type_skip_modifiers(btf_vmlinux, args[i].type, NULL);
|
|
|
|
|
t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
|
|
|
|
|
if (btf_type_is_ptr(t))
|
|
|
|
|
mark_btf_func_reg_size(env, regno, sizeof(void *));
|
|
|
|
|
else
|
|
|
|
@ -11121,7 +11267,8 @@ static int do_check(struct bpf_verifier_env *env)
|
|
|
|
|
env->jmps_processed++;
|
|
|
|
|
if (opcode == BPF_CALL) {
|
|
|
|
|
if (BPF_SRC(insn->code) != BPF_K ||
|
|
|
|
|
insn->off != 0 ||
|
|
|
|
|
(insn->src_reg != BPF_PSEUDO_KFUNC_CALL
|
|
|
|
|
&& insn->off != 0) ||
|
|
|
|
|
(insn->src_reg != BPF_REG_0 &&
|
|
|
|
|
insn->src_reg != BPF_PSEUDO_CALL &&
|
|
|
|
|
insn->src_reg != BPF_PSEUDO_KFUNC_CALL) ||
|
|
|
|
@ -12477,6 +12624,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
|
|
|
|
|
func[i]->aux->stack_depth = env->subprog_info[i].stack_depth;
|
|
|
|
|
func[i]->jit_requested = 1;
|
|
|
|
|
func[i]->aux->kfunc_tab = prog->aux->kfunc_tab;
|
|
|
|
|
func[i]->aux->kfunc_btf_tab = prog->aux->kfunc_btf_tab;
|
|
|
|
|
func[i]->aux->linfo = prog->aux->linfo;
|
|
|
|
|
func[i]->aux->nr_linfo = prog->aux->nr_linfo;
|
|
|
|
|
func[i]->aux->jited_linfo = prog->aux->jited_linfo;
|
|
|
|
@ -12665,7 +12813,7 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env,
|
|
|
|
|
/* insn->imm has the btf func_id. Replace it with
|
|
|
|
|
* an address (relative to __bpf_base_call).
|
|
|
|
|
*/
|
|
|
|
|
desc = find_kfunc_desc(env->prog, insn->imm);
|
|
|
|
|
desc = find_kfunc_desc(env->prog, insn->imm, insn->off);
|
|
|
|
|
if (!desc) {
|
|
|
|
|
verbose(env, "verifier internal error: kernel function descriptor not found for func_id %u\n",
|
|
|
|
|
insn->imm);
|
|
|
|
|