perf probe: Fix to avoid crashing if DW_AT_decl_file is NULL
authorMasami Hiramatsu (Google) <mhiramat@kernel.org>
Tue, 1 Nov 2022 13:48:30 +0000 (22:48 +0900)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 3 Nov 2022 12:24:46 +0000 (09:24 -0300)
Since clang generates DWARF5 which sets DW_AT_decl_file as 0,
dwarf_decl_file() thinks that is invalid and returns NULL.  In that case
'perf probe' SIGSEGVs because it doesn't expect a NULL decl_file.

This adds a dwarf_decl_file() return value check to avoid such SEGV with
clang generated DWARF5 info.

Without this, 'perf probe' crashes:

  $ perf probe -k $BIN_PATH/vmlinux -s $SRC_PATH -L vfs_read:10
  Segmentation fault
  $

With this, it just warns about it:

  $ perf probe -k $BIN_PATH/vmlinux -s $SRC_PATH -L vfs_read:10
  Debuginfo analysis failed.
    Error: Failed to show lines.
  $

Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt (VMware) <rostedt@goodmis.org>
Link: https://lore.kernel.org/r/166731051077.2100653.15626653369345128302.stgit@devnote3
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/dwarf-aux.c
tools/perf/util/probe-finder.c

index 609ca16..406b7bd 100644 (file)
@@ -137,7 +137,7 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, Dwarf_Addr addr,
        }
 
 out:
-       return *lineno ?: -ENOENT;
+       return (*lineno && *fname) ? *lineno : -ENOENT;
 }
 
 static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data);
@@ -874,6 +874,11 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
                cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL);
                dwarf_decl_line(rt_die, &decl);
                decf = dwarf_decl_file(rt_die);
+               if (!decf) {
+                       pr_debug2("Failed to get the declared file name of %s\n",
+                                 dwarf_diename(rt_die));
+                       return -EINVAL;
+               }
        } else
                cu_die = rt_die;
        if (!cu_die) {
index 50d861a..1aa8fcc 100644 (file)
@@ -1063,6 +1063,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
        struct dwarf_callback_param *param = data;
        struct probe_finder *pf = param->data;
        struct perf_probe_point *pp = &pf->pev->point;
+       const char *fname;
 
        /* Check tag and diename */
        if (!die_is_func_def(sp_die) ||
@@ -1070,12 +1071,17 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
                return DWARF_CB_OK;
 
        /* Check declared file */
-       if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die)))
+       fname = dwarf_decl_file(sp_die);
+       if (!fname) {
+               pr_warning("A function DIE doesn't have decl_line. Maybe broken DWARF?\n");
+               return DWARF_CB_OK;
+       }
+       if (pp->file && fname && strtailcmp(pp->file, fname))
                return DWARF_CB_OK;
 
        pr_debug("Matched function: %s [%lx]\n", dwarf_diename(sp_die),
                 (unsigned long)dwarf_dieoffset(sp_die));
-       pf->fname = dwarf_decl_file(sp_die);
+       pf->fname = fname;
        if (pp->line) { /* Function relative line */
                dwarf_decl_line(sp_die, &pf->lno);
                pf->lno += pp->line;
@@ -1134,6 +1140,7 @@ struct pubname_callback_param {
 static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
 {
        struct pubname_callback_param *param = data;
+       const char *fname;
 
        if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) {
                if (dwarf_tag(param->sp_die) != DW_TAG_subprogram)
@@ -1143,9 +1150,11 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
                        if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die))
                                return DWARF_CB_OK;
 
-                       if (param->file &&
-                           strtailcmp(param->file, dwarf_decl_file(param->sp_die)))
-                               return DWARF_CB_OK;
+                       if (param->file) {
+                               fname = dwarf_decl_file(param->sp_die);
+                               if (!fname || strtailcmp(param->file, fname))
+                                       return DWARF_CB_OK;
+                       }
 
                        param->found = 1;
                        return DWARF_CB_ABORT;
@@ -1779,7 +1788,7 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, u64 addr,
                }
                /* Verify the lineno and baseline are in a same file */
                tmp = dwarf_decl_file(&spdie);
-               if (!tmp || strcmp(tmp, fname) != 0)
+               if (!tmp || (fname && strcmp(tmp, fname) != 0))
                        lineno = 0;
        }
 
@@ -1889,10 +1898,14 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
        struct dwarf_callback_param *param = data;
        struct line_finder *lf = param->data;
        struct line_range *lr = lf->lr;
+       const char *fname;
 
        /* Check declared file */
-       if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die)))
-               return DWARF_CB_OK;
+       if (lr->file) {
+               fname = dwarf_decl_file(sp_die);
+               if (!fname || strtailcmp(lr->file, fname))
+                       return DWARF_CB_OK;
+       }
 
        if (die_match_name(sp_die, lr->function) && die_is_func_def(sp_die)) {
                lf->fname = dwarf_decl_file(sp_die);