mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-10 15:49:01 -04:00
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/
Fixes: c440adfbe3 ("tracing/probes: Support BTF based data structure field access")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260822095110.0772E1F000E9@smtp.kernel.org/
Assisted-by: Antigravity:gemini-3.7-flash
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Reviewed-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
@@ -61,16 +61,17 @@ struct btf_anon_stack {
|
||||
|
||||
/*
|
||||
* Find a member of data structure/union by name and return it.
|
||||
* Return NULL if not found, or -EINVAL if parameter is invalid.
|
||||
* If the member is an member of anonymous union/structure, the offset
|
||||
* of that anonymous union/structure is stored into @anon_offset. Caller
|
||||
* can calculate the correct offset from the root data structure by
|
||||
* adding anon_offset to the member's offset.
|
||||
* Return NULL if not found, or ERR_PTR(-EINVAL) if parameter is invalid.
|
||||
* If the member is a member of an anonymous union/structure, the bit offset
|
||||
* of that anonymous union/structure is stored into @anon_offset.
|
||||
* If @member_type is non-NULL, the actual containing structure/union type
|
||||
* of the found member is stored into @member_type.
|
||||
*/
|
||||
const struct btf_member *btf_find_struct_member(struct btf *btf,
|
||||
const struct btf_type *type,
|
||||
const char *member_name,
|
||||
u32 *anon_offset)
|
||||
u32 *anon_offset,
|
||||
const struct btf_type **member_type)
|
||||
{
|
||||
struct btf_anon_stack *anon_stack;
|
||||
const struct btf_member *member;
|
||||
@@ -94,14 +95,16 @@ const struct btf_member *btf_find_struct_member(struct btf *btf,
|
||||
if (mtype && btf_type_is_struct(mtype) &&
|
||||
top < BTF_ANON_STACK_MAX) {
|
||||
anon_stack[top].tid = tid;
|
||||
anon_stack[top++].offset =
|
||||
cur_offset + member->offset;
|
||||
anon_stack[top++].offset = cur_offset +
|
||||
__btf_member_bit_offset(type, member);
|
||||
}
|
||||
} else {
|
||||
name = btf_name_by_offset(btf, member->name_off);
|
||||
if (name && !strcmp(member_name, name)) {
|
||||
if (anon_offset)
|
||||
*anon_offset = cur_offset;
|
||||
if (member_type)
|
||||
*member_type = type;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ const struct btf_param *btf_get_func_param(const struct btf_type *func_proto,
|
||||
const struct btf_member *btf_find_struct_member(struct btf *btf,
|
||||
const struct btf_type *type,
|
||||
const char *member_name,
|
||||
u32 *anon_offset);
|
||||
u32 *anon_offset,
|
||||
const struct btf_type **member_type);
|
||||
|
||||
@@ -625,6 +625,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty
|
||||
{
|
||||
const struct btf_type *type = *ptype;
|
||||
const struct btf_member *field;
|
||||
const struct btf_type *mtype;
|
||||
struct btf *btf = ctx_btf(ctx);
|
||||
char *fieldname = *pfieldname;
|
||||
int bitoffs = 0;
|
||||
@@ -640,7 +641,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty
|
||||
|
||||
anon_offs = 0;
|
||||
field = btf_find_struct_member(btf, type, fieldname,
|
||||
&anon_offs);
|
||||
&anon_offs, &mtype);
|
||||
if (IS_ERR(field)) {
|
||||
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
|
||||
return PTR_ERR(field);
|
||||
@@ -653,7 +654,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty
|
||||
bitoffs += anon_offs;
|
||||
|
||||
/* Accumulate the bit-offsets of the dot-connected fields */
|
||||
if (btf_type_kflag(type)) {
|
||||
if (btf_type_kflag(mtype)) {
|
||||
bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset);
|
||||
ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user