btf_find_struct_member() traverses into nested anonymous structures and unions to find a struct member. However, get_bitoffset_of_field() in trace_probe.c checked btf_type_kflag(type) using the outer parent type instead of the actual anonymous structure/union that directly contains the found member. If the parent structure and anonymous structure have mismatched kflags (e.g., the parent has kflag=0 while the anonymous structure has kflag=1 because it contains bitfields), the bitfield size encoded in the upper 8 bits of member->offset is erroneously treated as part of the byte/bit offset, corrupting the resolved offset and failing to set last_bitsize. Similarly, btf_find_struct_member() pushed anonymous member offsets onto anon_stack without masking BTF_MEMBER_BIT_OFFSET() when kflag is set. To fix this problem, update btf_find_struct_member() to return actual containing structure/union type via member_type, use appropriate __btf_member_bit_offset() to get bit offset, and use member_type for btf_type_kflag() in get_bitoffset_of_field(). Link: https://lore.kernel.org/all/178827250904.123716.17452648791331881284.stgit@devnote2/
Unified · Split
| @@ -61,16 +61,17 @@ struct btf_anon_stack { · 60 unmodified lines | |||
| 61 | 61 | ||
| 62 | /* | 62 | /* |
| 63 | * Find a member of data structure/union by name and return it. | 63 | * Find a member of data structure/union by name and return it. |
| 64 | * Return NULL if not found, or -EINVAL if parameter is invalid. | 64 | * Return NULL if not found, or ERR_PTR(-EINVAL) if parameter is invalid. |
| 65 | * If the member is an member of anonymous union/structure, the offset | 65 | * If the member is a member of an anonymous union/structure, the bit offset |
| 66 | * of that anonymous union/structure is stored into @anon_offset. Caller | 66 | * of that anonymous union/structure is stored into @anon_offset. |
| 67 | * can calculate the correct offset from the root data structure by | 67 | * If @member_type is non-NULL, the actual containing structure/union type |
| 68 | * adding anon_offset to the member's offset. | 68 | * of the found member is stored into @member_type. |
| 69 | */ | 69 | */ |
| 70 | const struct btf_member *btf_find_struct_member(struct btf *btf, | 70 | const struct btf_member *btf_find_struct_member(struct btf *btf, |
| 71 | const struct btf_type *type, | 71 | const struct btf_type *type, |
| 72 | const char *member_name, | 72 | const char *member_name, |
| 73 | u32 *anon_offset) | 73 | u32 *anon_offset, |
| 74 | const struct btf_type **member_type) | ||
| 74 | { | 75 | { |
| 75 | struct btf_anon_stack *anon_stack; | 76 | struct btf_anon_stack *anon_stack; |
| 76 | const struct btf_member *member; | 77 | const struct btf_member *member; |
| @@ -94,14 +95,16 @@ retry: · 17 unmodified lines | |||
| 94 | if (mtype && btf_type_is_struct(mtype) && | 95 | if (mtype && btf_type_is_struct(mtype) && |
| 95 | top < BTF_ANON_STACK_MAX) { | 96 | top < BTF_ANON_STACK_MAX) { |
| 96 | anon_stack[top].tid = tid; | 97 | anon_stack[top].tid = tid; |
| 97 | anon_stack[top++].offset = | 98 | anon_stack[top++].offset = cur_offset + |
| 98 | cur_offset + member->offset; | 99 | __btf_member_bit_offset(type, member); |
| 99 | } | 100 | } |
| 100 | } else { | 101 | } else { |
| 101 | name = btf_name_by_offset(btf, member->name_off); | 102 | name = btf_name_by_offset(btf, member->name_off); |
| 102 | if (name && !strcmp(member_name, name)) { | 103 | if (name && !strcmp(member_name, name)) { |
| 103 | if (anon_offset) | 104 | if (anon_offset) |
| 104 | *anon_offset = cur_offset; | 105 | *anon_offset = cur_offset; |
| 106 | if (member_type) | ||
| 107 | *member_type = type; | ||
| 105 | goto out; | 108 | goto out; |
| 106 | } | 109 | } |
| 107 | } | 110 | } |
| @@ -8,4 +8,5 @@ const struct btf_param *btf_get_func_param(const struct btf_type *func_proto, · 7 unmodified lines | |||
| 8 | const struct btf_member *btf_find_struct_member(struct btf *btf, | 8 | const struct btf_member *btf_find_struct_member(struct btf *btf, |
| 9 | const struct btf_type *type, | 9 | const struct btf_type *type, |
| 10 | const char *member_name, | 10 | const char *member_name, |
| 11 | u32 *anon_offset); | 11 | u32 *anon_offset, |
| 12 | const struct btf_type **member_type); | ||
| @@ -625,6 +625,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty · 624 unmodified lines | |||
| 625 | { | 625 | { |
| 626 | const struct btf_type *type = *ptype; | 626 | const struct btf_type *type = *ptype; |
| 627 | const struct btf_member *field; | 627 | const struct btf_member *field; |
| 628 | const struct btf_type *mtype; | ||
| 628 | struct btf *btf = ctx_btf(ctx); | 629 | struct btf *btf = ctx_btf(ctx); |
| 629 | char *fieldname = *pfieldname; | 630 | char *fieldname = *pfieldname; |
| 630 | int bitoffs = 0; | 631 | int bitoffs = 0; |
| @@ -640,7 +641,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty · 9 unmodified lines | |||
| 640 | 641 | ||
| 641 | anon_offs = 0; | 642 | anon_offs = 0; |
| 642 | field = btf_find_struct_member(btf, type, fieldname, | 643 | field = btf_find_struct_member(btf, type, fieldname, |
| 643 | &anon_offs); | 644 | &anon_offs, &mtype); |
| 644 | if (IS_ERR(field)) { | 645 | if (IS_ERR(field)) { |
| 645 | trace_probe_log_err(ctx->offset, BAD_BTF_TID); | 646 | trace_probe_log_err(ctx->offset, BAD_BTF_TID); |
| 646 | return PTR_ERR(field); | 647 | return PTR_ERR(field); |
| @@ -653,7 +654,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty · 6 unmodified lines | |||
| 653 | bitoffs += anon_offs; | 654 | bitoffs += anon_offs; |
| 654 | 655 | ||
| 655 | /* Accumulate the bit-offsets of the dot-connected fields */ | 656 | /* Accumulate the bit-offsets of the dot-connected fields */ |
| 656 | if (btf_type_kflag(type)) { | 657 | if (btf_type_kflag(mtype)) { |
| 657 | bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset); | 658 | bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset); |
| 658 | ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset); | 659 | ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset); |
| 659 | } else { | 660 | } else { |