> On 26 Feb 2025, at 6:35 PM, Masami Hiramatsu (Google) <mhira...@kernel.org> 
> wrote:
> 
> On Tue, 25 Feb 2025 18:00:42 +0530
> Athira Rajeev <atraj...@linux.ibm.com> wrote:
> 
>> Perf probe on vfs_fstatat fails as below on a powerpc system
>> 
>> ./perf probe -nf --max-probes=512 -a 'vfs_fstatat $params'
>> Segmentation fault (core dumped)
>> 
>> This is observed while running perftool-testsuite_probe testcase.
>> 
>> While running with verbose, its observed that segfault happens
>> at:
>> 
>>   synthesize_probe_trace_arg ()
>>   synthesize_probe_trace_command ()
>>   probe_file.add_event ()
>>   apply_perf_probe_events ()
>>   __cmd_probe ()
>>   cmd_probe ()
>>   run_builtin ()
>>   handle_internal_command ()
>>   main ()
>> 
>> Code in synthesize_probe_trace_arg() access a null value and results in
>> segfault. Data structure which is null:
>> struct probe_trace_arg arg->value
>> 
>> We are hitting a case where arg->value is null in probe point:
>> "vfs_fstatat $params". This is happening since 'commit e896474fe485
>> ("getname_maybe_null() - the third variant of pathname copy-in")'
>> Before the commit, probe point for vfs_fstatat was getting added only
>> for one location:
>> 
>> Writing event: p:probe/vfs_fstatat _text+6345404 dfd=%gpr3:s32 
>> filename=%gpr4:x64 stat=%gpr5:x64 flags=%gpr6:s32
>> 
>> With this change, vfs_fstatat code is inlined for other locations in the
>> code:
>> Probe point found: __do_sys_lstat64+48
>> Probe point found: __do_sys_stat64+48
>> Probe point found: __do_sys_newlstat+48
>> Probe point found: __do_sys_newstat+48
>> Probe point found: vfs_fstatat+0
>> 
>> When trying to find matching dwarf information entry (DIE)
>> from the debuginfo, the code incorrectly picks DIE which is
>> not referring to vfs_fstatat. Snippet from dwarf entry in vmlinux
>> debuginfo file.
>> 
>> The main abstract die is:
>> <1><4214883>: Abbrev Number: 147 (DW_TAG_subprogram)
>>    <4214885>   DW_AT_external    : 1
>>    <4214885>   DW_AT_name        : (indirect string, offset: 0x17b9f3): 
>> vfs_fstatat
>> 
>> With formal parameters:
>> <2><4214896>: Abbrev Number: 51 (DW_TAG_formal_parameter)
>>    <4214897>   DW_AT_name        : dfd
>> <2><42148a3>: Abbrev Number: 23 (DW_TAG_formal_parameter)
>>    <42148a4>   DW_AT_name        : (indirect string, offset: 0x8fda9): 
>> filename
>> <2><42148b0>: Abbrev Number: 23 (DW_TAG_formal_parameter)
>>    <42148b1>   DW_AT_name        : (indirect string, offset: 0x16bd9c): stat
>> <2><42148bd>: Abbrev Number: 23 (DW_TAG_formal_parameter)
>>    <42148be>   DW_AT_name        : (indirect string, offset: 0x39832b): flags
>> 
>> While collecting variables/parameters for a probe point, the function
>> copy_variables_cb() also looks at dwarf debug entries based on the
>> instruction address. Snippet
>> 
>>        if (dwarf_haspc(die_mem, vf->pf->addr))
>>                return DIE_FIND_CB_CONTINUE;
>>        else
>>                return DIE_FIND_CB_SIBLING;
>> 
>> But incase of inlined function instance for vfs_fstatat, there are two
>> entries which has the instruction address entry point as same.
>> 
>> Instance 1: which is for vfs_fstatat and DW_AT_abstract_origin points to
>> 0x4214883 (reference above for main abstract die)
>> 
>> <3><42131fa>: Abbrev Number: 59 (DW_TAG_inlined_subroutine)
>>    <42131fb>   DW_AT_abstract_origin: <0x4214883>
>>    <42131ff>   DW_AT_entry_pc    : 0xc00000000062b1e0
>> 
>> Instance 2: which is not for vfs_fstatat but for getname
>> 
>> <5><4213270>: Abbrev Number: 39 (DW_TAG_inlined_subroutine)
>>    <4213271>   DW_AT_abstract_origin: <0x4215b6b>
>>    <4213275>   DW_AT_entry_pc    : 0xc00000000062b1e0
>> 
>> But the copy_variables_cb() continues to add parameters from second
>> instance also based on the dwarf_haspc() check. This results in
>> formal parameters for getname also appended to params. But while
>> filling in the args->value for these parameters, since these args
>> are not part of dwarf with offset "42131fa". Hence value will be
>> null. This incorrect args results in segfault when value field is
>> accessed.
>> 
>> Save the dwarf dieoffset of the actual DW_TAG_subprogram as part of
>> "struct probe_finder". In copy_variables_cb(), include check to make
>> sure the DW_AT_abstract_origin points to the correct entry if the
>> dwarf_haspc() matches the instruction address.
>> 
> 
> Good catch! and this looks good to me :-)
> 
> Acked-by: Masami Hiramatsu (Google) <mhira...@kernel.org>
> 
> Thank you!

Hi Masami

Thanks for checking the patch.

Namhyung,
Thanks for having this patch in tmp.perf-tools-next.

James had sent reviewed-by for the first version
https://lore.kernel.org/linux-perf-users/964f60e0-fe88-48c8-87c3-a6ea2f3ca...@linaro.org/

Since I added a change in V2 to directly use the die offset, I didn’t carry his 
reviewed-by in V2 to make sure he is fine with V2.
Please add reviewed-by from James if he is ok with V2 :) 

Thanks
Athira
> 
>> Signed-off-by: Athira Rajeev <atraj...@linux.ibm.com>
>> ---
>> Changelog:
>> v1 -> v2:
>> Instead of saving the Dwarf_Die as part of "struct probe_finder",
>> save the dwarf dieoffset. We only need offset while comparing to see
>> if DW_AT_abstract_origin points to correct entry
>> Suggested in review by Namhyung.
>> 
>> tools/perf/util/probe-finder.c | 21 ++++++++++++++++++---
>> tools/perf/util/probe-finder.h |  1 +
>> 2 files changed, 19 insertions(+), 3 deletions(-)
>> 
>> diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
>> index 1e769b68da37..3cc7c40f5097 100644
>> --- a/tools/perf/util/probe-finder.c
>> +++ b/tools/perf/util/probe-finder.c
>> @@ -973,6 +973,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void 
>> *data)
>> pr_debug("Matched function: %s [%lx]\n", dwarf_diename(sp_die),
>>  (unsigned long)dwarf_dieoffset(sp_die));
>> pf->fname = fname;
>> + pf->abstrace_dieoffset = dwarf_dieoffset(sp_die);
>> if (pp->line) { /* Function relative line */
>> dwarf_decl_line(sp_die, &pf->lno);
>> pf->lno += pp->line;
>> @@ -1179,6 +1180,8 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void 
>> *data)
>> struct local_vars_finder *vf = data;
>> struct probe_finder *pf = vf->pf;
>> int tag;
>> + Dwarf_Attribute attr;
>> + Dwarf_Die var_die;
>> 
>> tag = dwarf_tag(die_mem);
>> if (tag == DW_TAG_formal_parameter ||
>> @@ -1196,10 +1199,22 @@ static int copy_variables_cb(Dwarf_Die *die_mem, 
>> void *data)
>> }
>> }
>> 
>> - if (dwarf_haspc(die_mem, vf->pf->addr))
>> + if (dwarf_haspc(die_mem, vf->pf->addr)) {
>> + /*
>> +  * when DW_AT_entry_pc contains instruction address,
>> +  * also check if the DW_AT_abstract_origin of die_mem
>> +  * points to correct die.
>> +  */
>> + if (dwarf_attr(die_mem, DW_AT_abstract_origin, &attr)) {
>> + dwarf_formref_die(&attr, &var_die);
>> + if (pf->abstrace_dieoffset != dwarf_dieoffset(&var_die))
>> + goto out;
>> + }
>> return DIE_FIND_CB_CONTINUE;
>> - else
>> - return DIE_FIND_CB_SIBLING;
>> + }
>> +
>> +out:
>> + return DIE_FIND_CB_SIBLING;
>> }
>> 
>> static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf,
>> diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
>> index dcf6cc1e1cbe..ecd6d937c592 100644
>> --- a/tools/perf/util/probe-finder.h
>> +++ b/tools/perf/util/probe-finder.h
>> @@ -63,6 +63,7 @@ struct probe_finder {
>> const char *fname; /* Real file name */
>> Dwarf_Die cu_die; /* Current CU */
>> Dwarf_Die sp_die;
>> + Dwarf_Off abstrace_dieoffset;
>> struct intlist *lcache; /* Line cache for lazy match */
>> 
>> /* For variable searching */
>> -- 
>> 2.43.5
>> 
> 
> 
> -- 
> Masami Hiramatsu (Google) <mhira...@kernel.org>



Reply via email to