History

tracing/probes: Fix BTF kflag check for anonymous struct member access

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/

Masami Hiramatsu (Google) committed 2 months ago

Commit 47e93041 parent f36d94a3 files · +16 −11Browse files

Showing the changes to kernel (3 of 3 files). Show all

Unified · Split

3 files
  1. kernel/trace/trace_btf.c +11 −8
  2. kernel/trace/trace_btf.h +2 −1
  3. kernel/trace/trace_probe.c +3 −2
kernel/trace/trace_btf.c+11−8
@@ -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 offset65 * 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. Caller66 * of that anonymous union/structure is stored into @anon_offset.
67 * can calculate the correct offset from the root data structure by67 * 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 */
70const struct btf_member *btf_find_struct_member(struct btf *btf,70const 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 }
kernel/trace/trace_btf.h+2−1
@@ -8,4 +8,5 @@ const struct btf_param *btf_get_func_param(const struct btf_type *func_proto, · 7 unmodified lines
8const struct btf_member *btf_find_struct_member(struct btf *btf,8const 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);
kernel/trace/trace_probe.c+3−2
@@ -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 {