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!

> 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