When BTF parsing encounters an unrecognized kind (> BTF_KIND_MAX), look for __BTF_KIND_<num> typedef which points at the associated kind struct; it tells us if there is metadata and how much. This all allows us to proceed with BTF parsing rather than bailing when hitting a kind we do not support. Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx> --- kernel/bpf/btf.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 1a59cc7..ce00a4c5 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -222,6 +222,14 @@ struct btf_id_dtor_kfunc_tab { struct btf_id_dtor_kfunc dtors[]; }; +struct btf_kind_desc { + u16 kind; + u16 nr_meta; + u32 meta_size; +}; + +#define NR_BTF_KINDS_POSSIBLE 0x20 + struct btf { void *data; struct btf_type **types; @@ -246,6 +254,7 @@ struct btf { u32 start_str_off; /* first string offset (0 for base BTF) */ char name[MODULE_NAME_LEN]; bool kernel_btf; + struct btf_kind_desc unrecognized_kinds[NR_BTF_KINDS_POSSIBLE - NR_BTF_KINDS]; }; enum verifier_phase { @@ -4873,7 +4882,7 @@ static s32 btf_check_meta(struct btf_verifier_env *env, u32 meta_left) { u32 saved_meta_left = meta_left; - s32 var_meta_size; + s32 var_meta_size = 0; if (meta_left < sizeof(*t)) { btf_verifier_log(env, "[%u] meta_left:%u meta_needed:%zu", @@ -4888,12 +4897,80 @@ static s32 btf_check_meta(struct btf_verifier_env *env, return -EINVAL; } - if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX || - BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) { + if (BTF_INFO_KIND(t->info) == BTF_KIND_UNKN || + BTF_INFO_KIND(t->info) >= NR_BTF_KINDS_POSSIBLE) { btf_verifier_log(env, "[%u] Invalid kind:%u", env->log_type_id, BTF_INFO_KIND(t->info)); return -EINVAL; } + if (BTF_INFO_KIND(t->info) <= BTF_KIND_MAX) { + var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left); + if (var_meta_size < 0) + return var_meta_size; + } else { + struct btf_kind_desc *unrec_kind; + u8 kind = BTF_INFO_KIND(t->info); + struct btf *btf = env->btf; + + unrec_kind = &btf->unrecognized_kinds[kind - NR_BTF_KINDS]; + + if (unrec_kind->kind != kind) { + const struct btf_member *m; + const struct btf_type *kt; + const struct btf_array *a; + char name[64]; + s32 id; + + /* BTF may encode info about unrecognized kinds; check for this here. */ + snprintf(name, sizeof(name), BTF_KIND_PFX "%u", kind); + id = btf_find_by_name_kind(btf, name, BTF_KIND_TYPEDEF); + if (id > 0) { + kt = btf_type_by_id(btf, id); + if (kt) + kt = btf_type_by_id(btf, kt->type); + } + if (id < 0 || !kt) { + btf_verifier_log_type(env, t, "[%u] invalid kind:%u", + env->log_type_id, kind); + return -EINVAL; + } + switch (btf_type_vlen(kt)) { + case 1: + /* no metadata */ + unrec_kind->kind = kind; + unrec_kind->nr_meta = 0; + unrec_kind->meta_size = 0; + break; + case 2: + m = btf_members(kt); + kt = btf_type_by_id(btf, (++m)->type); + if (btf_kind(kt) != BTF_KIND_ARRAY) { + btf_verifier_log_type(env, t, "[%u] invalid metadata:%u", + env->log_type_id, kind); + return -EINVAL; + } + a = btf_array(kt); + kt = btf_type_by_id(btf, a->type); + if (!kt) { + btf_verifier_log_type(env, t, "[%u] invalid metadata:%u", + env->log_type_id, kind); + return -EINVAL; + } + unrec_kind->kind = kind; + unrec_kind->nr_meta = a->nelems; + unrec_kind->meta_size = kt->size; + break; + default: + btf_verifier_log_type(env, t, "[%u] invalid metadata:%u", + env->log_type_id, kind); + return -EINVAL; + } + } + if (!unrec_kind->nr_meta) + var_meta_size = btf_type_vlen(t) * unrec_kind->meta_size; + else + var_meta_size = unrec_kind->nr_meta * unrec_kind->meta_size; + } if (!btf_name_offset_valid(env->btf, t->name_off)) { btf_verifier_log(env, "[%u] Invalid name_offset:%u", @@ -4901,10 +4978,6 @@ static s32 btf_check_meta(struct btf_verifier_env *env, return -EINVAL; } - var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left); - if (var_meta_size < 0) - return var_meta_size; - meta_left -= var_meta_size; return saved_meta_left - meta_left; -- 1.8.3.1