This patch looks into unions when parsing BTF. While we would like to support adding a skb to bpf collections, the bpf graph node in sk_buff will happen to be in a union due to space constraint. Therefore, Signed-off-by: Amery Hung <amery.hung@xxxxxxxxxxxxx> --- kernel/bpf/btf.c | 74 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 37fb6143da79..25a5dc840ac3 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3305,7 +3305,7 @@ static int btf_find_struct(const struct btf *btf, const struct btf_type *t, u32 off, int sz, enum btf_field_type field_type, struct btf_field_info *info) { - if (!__btf_type_is_struct(t)) + if (!btf_type_is_struct(t)) return BTF_FIELD_IGNORE; if (t->size != sz) return BTF_FIELD_IGNORE; @@ -3497,6 +3497,24 @@ static int btf_get_field_type(const char *name, u32 field_mask, u32 *seen_mask, return type; } +static int btf_get_union_field_types(const struct btf *btf, const struct btf_type *u, + u32 field_mask, u32 *seen_mask, int *align, int *sz) +{ + int i, field_type, field_types = 0; + const struct btf_member *member; + const struct btf_type *t; + + for_each_member(i, u, member) { + t = btf_type_by_id(btf, member->type); + field_type = btf_get_field_type(__btf_name_by_offset(btf, t->name_off), + field_mask, seen_mask, align, sz); + if (field_type == 0 || field_type == BPF_KPTR_REF) + continue; + field_types = field_types | field_type; + } + return field_types; +} + #undef field_mask_test_name static int btf_find_struct_field(const struct btf *btf, @@ -3512,8 +3530,12 @@ static int btf_find_struct_field(const struct btf *btf, const struct btf_type *member_type = btf_type_by_id(btf, member->type); - field_type = btf_get_field_type(__btf_name_by_offset(btf, member_type->name_off), - field_mask, &seen_mask, &align, &sz); + field_type = BTF_INFO_KIND(member_type->info) == BTF_KIND_UNION ? + btf_get_union_field_types(btf, member_type, field_mask, + &seen_mask, &align, &sz) : + btf_get_field_type(__btf_name_by_offset(btf, member_type->name_off), + field_mask, &seen_mask, &align, &sz); + if (field_type == 0) continue; if (field_type < 0) @@ -3521,8 +3543,7 @@ static int btf_find_struct_field(const struct btf *btf, off = __btf_member_bit_offset(t, member); if (off % 8) - /* valid C code cannot generate such BTF */ - return -EINVAL; + continue; off /= 8; if (off % align) continue; @@ -3737,6 +3758,20 @@ static int btf_parse_kptr(const struct btf *btf, struct btf_field *field, return ret; } +static const struct btf_type * +btf_find_member_by_name(const struct btf *btf, const struct btf_type *t, + const char *member_name) +{ + const struct btf_member *member; + int i; + + for_each_member(i, t, member) { + if (!strcmp(member_name, __btf_name_by_offset(btf, member->name_off))) + return btf_type_by_id(btf, member->type); + } + return NULL; +} + static int btf_parse_graph_root(struct btf_field *field, struct btf_field_info *info, const char *node_type_name, @@ -3754,18 +3789,27 @@ static int btf_parse_graph_root(struct btf_field *field, * verify its type. */ for_each_member(i, t, member) { - if (strcmp(info->graph_root.node_name, - __btf_name_by_offset(btf, member->name_off))) + const struct btf_type *member_type = btf_type_by_id(btf, member->type); + + if (BTF_INFO_KIND(member_type->info) == BTF_KIND_UNION) { + member_type = btf_find_member_by_name(btf, member_type, + info->graph_root.node_name); + if (!member_type) + continue; + } else if (strcmp(info->graph_root.node_name, + __btf_name_by_offset(btf, member->name_off))) { continue; + } + /* Invalid BTF, two members with same name */ if (n) return -EINVAL; - n = btf_type_by_id(btf, member->type); + n = member_type; if (!__btf_type_is_struct(n)) return -EINVAL; if (strcmp(node_type_name, __btf_name_by_offset(btf, n->name_off))) return -EINVAL; - offset = __btf_member_bit_offset(n, member); + offset = __btf_member_bit_offset(member_type, member); if (offset % 8) return -EINVAL; offset /= 8; @@ -5440,7 +5484,7 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf) const struct btf_member *member; struct btf_struct_meta *type; struct btf_record *record; - const struct btf_type *t; + const struct btf_type *t, *member_type; int j, tab_cnt, id; id = btf_is_base_kernel ? @@ -5462,6 +5506,16 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf) cond_resched(); for_each_member(j, t, member) { + member_type = btf_type_by_id(btf, member->type); + if (BTF_INFO_KIND(member_type->info) == BTF_KIND_UNION) { + const struct btf_member *umember; + int k; + + for_each_member(k, member_type, umember) { + if (btf_id_set_contains(&aof.set, umember->type)) + goto parse; + } + } if (btf_id_set_contains(&aof.set, member->type)) goto parse; } -- 2.20.1