A segmentation fault was observed in `libdw` when running `perf kmem`
with `--page stat` on some workloads. The crash occurred deep inside
`libdw` (specifically in `dwarf_child` and `dwarf_diename`) when
processing DWARF information.
The root cause was improper error handling of `dwarf_getfuncs` in
`die_find_realfunc` and `die_find_tailfunc`.
`dwarf_getfuncs` returns:
- `0` on success (when all functions have been processed).
- A positive offset if the callback aborts early (e.g., via
`DWARF_CB_ABORT` when a match is found).
- `-1` on error.
The original code used `if (!dwarf_getfuncs(...)) return NULL;`. On
error (`-1`), `!-1` evaluates to `0` (false), bypassing the error
check. Execution then proceeded as if a match was found, returning
uninitialized stack memory (`die_mem`) to the caller
(`cu_walk_functions_at`). When `cu_walk_functions_at` passed this
uninitialized memory to `libdw` via `dwarf_diename`, it caused a
segmentation fault.
Fix this by correcting the error check to `if (dwarf_getfuncs(...) <= 0)`.
Fixes: e0d153c69040 ("perf-probe: Move dwarf library routines to dwarf-aux.{c, h}")
Fixes: d4c537e6bf86 ("perf probe: Ignore tail calls to probed functions")
Assisted-by: Gemini-CLI:Google Gemini 3
Signed-off-by: Ian Rogers <irogers@google.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Zecheng Li <zli94@ncsu.edu>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
}
return ret;
-
}
/**
ad.addr = addr;
ad.die_mem = die_mem;
/* dwarf_getscopes can't find subprogram. */
- if (!dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0))
+ if (dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0) <= 0)
return NULL;
else
return die_mem;
ad.addr = addr;
ad.die_mem = die_mem;
/* dwarf_getscopes can't find subprogram. */
- if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0))
+ if (dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0) <= 0)
return NULL;
else
return die_mem;