* [PATCH v3 0/6] perf DWARF: Fix libdw API contract violations and crashes
@ 2026-05-03 0:35 Ian Rogers
2026-05-03 0:35 ` [PATCH v3 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers
` (6 more replies)
0 siblings, 7 replies; 33+ messages in thread
From: Ian Rogers @ 2026-05-03 0:35 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li,
Masami Hiramatsu, linux-perf-users, linux-kernel
Cc: Ian Rogers
This patch series addresses a number of issues related to improper or
incomplete error handling when interacting with the `libdw` and
`libdwfl` APIs in the `perf` DWARF processing code.
The first patch fixes a real segmentation fault observed in `perf
kmem` (via `dwarf_child` and `dwarf_diename`) caused by
`dwarf_getfuncs` returning `-1` on error, which was not caught by the
previous `if (!dwarf_getfuncs(...))` check. This allowed uninitialized
stack memory to be returned and later dereferenced.
Following this discovery, a comprehensive audit of `libdw` API usage
was performed across `tools/perf/`. The remaining patches proactively
fix 28 additional instances of ignored return values or unchecked
pointers that could lead to similar uninitialized memory propagation,
crashes (e.g., via `strdup(NULL)` or `strcmp(NULL)`), or silent logic
failures.
v3:
Minor formatting fixes.
v2:
https://lore.kernel.org/lkml/20260502155656.478642-1-irogers@google.com/
v1:
https://lore.kernel.org/linux-perf-users/20260502064839.282422-1-irogers@google.com/
Ian Rogers (6):
perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at
perf dwarf-aux: Fix libdw API contract violations
perf libdw: Fix libdw API contract violations
perf probe-finder: Fix libdw API contract violations
perf annotate-data: Fix libdw API contract violations
perf debuginfo: Fix libdw API contract violations
tools/perf/util/annotate-data.c | 17 ++++---
tools/perf/util/debuginfo.c | 5 +-
tools/perf/util/dwarf-aux.c | 37 ++++++++------
tools/perf/util/libdw.c | 24 +++++++---
tools/perf/util/probe-finder.c | 85 ++++++++++++++++++++++-----------
5 files changed, 109 insertions(+), 59 deletions(-)
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply [flat|nested] 33+ messages in thread* [PATCH v3 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at 2026-05-03 0:35 [PATCH v3 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers @ 2026-05-03 0:35 ` Ian Rogers 2026-05-03 0:35 ` [PATCH v3 2/6] perf dwarf-aux: Fix libdw API contract violations Ian Rogers ` (5 subsequent siblings) 6 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-03 0:35 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers 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> --- tools/perf/util/dwarf-aux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 92db2fccc788..8e83cb2d565e 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -171,7 +171,6 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, } return ret; - } /** @@ -620,7 +619,7 @@ Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 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; @@ -647,6 +646,7 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) * die_find_realfunc - Search a non-inlined function at given address * @cu_die: a CU DIE which including @addr * @addr: target address + * @dbg: Dwarf session * @die_mem: a buffer for result DIE * * Search a non-inlined function DIE which includes @addr. Stores the @@ -659,7 +659,7 @@ Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 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; -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v3 2/6] perf dwarf-aux: Fix libdw API contract violations 2026-05-03 0:35 [PATCH v3 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 2026-05-03 0:35 ` [PATCH v3 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers @ 2026-05-03 0:35 ` Ian Rogers 2026-05-03 0:35 ` [PATCH v3 3/6] perf libdw: " Ian Rogers ` (4 subsequent siblings) 6 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-03 0:35 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwarf_decl_line`, `dwarf_getfuncs`, and `dwarf_lineaddr` to prevent using uninitialized stack variables or incorrectly reporting success on failure. Fixes: 57f95bf5f882 ("perf probe: Show correct statement line number by perf probe -l") Fixes: 3f4460a28fb2 ("perf probe: Filter out redundant inline-instances") Fixes: 75186a9b09e4 ("perf probe: Fix to show lines of sys_ functions correctly") Fixes: e0d153c69040 ("perf-probe: Move dwarf library routines to dwarf-aux.{c, h}") Fixes: 6243b9dc4c99 ("perf probe: Move dwarf specific functions to dwarf-aux.c") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/dwarf-aux.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 8e83cb2d565e..1168e69a9292 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -125,7 +125,8 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, Dwarf_Addr addr, && die_entrypc(&die_mem, &faddr) == 0 && faddr == addr) { *fname = die_get_decl_file(&die_mem); - dwarf_decl_line(&die_mem, lineno); + if (dwarf_decl_line(&die_mem, lineno) != 0) + return -ENOENT; goto out; } @@ -796,8 +797,7 @@ static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) /* Ignore redundant instances */ if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { - dwarf_decl_line(origin, &tmp); - if (die_get_call_lineno(inst) == tmp) { + if (dwarf_decl_line(origin, &tmp) == 0 && die_get_call_lineno(inst) == tmp) { tmp = die_get_decl_fileno(origin); if (die_get_call_fileno(inst) == tmp) return DIE_FIND_CB_CONTINUE; @@ -949,7 +949,11 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) /* Get the CU die */ if (dwarf_tag(rt_die) != DW_TAG_compile_unit) { cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL); - dwarf_decl_line(rt_die, &decl); + if (dwarf_decl_line(rt_die, &decl) != 0) { + pr_debug2("Failed to get the declared line number of %s\n", + dwarf_diename(rt_die)); + return -EINVAL; + } decf = die_get_decl_file(rt_die); if (!decf) { pr_debug2("Failed to get the declared file name of %s\n", @@ -1003,8 +1007,7 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) die_get_call_lineno(&die_mem) == lineno) goto found; - dwarf_decl_line(&die_mem, &inl); - if (inl != decl || + if (dwarf_decl_line(&die_mem, &inl) != 0 || inl != decl || decf != die_get_decl_file(&die_mem)) continue; } @@ -1035,8 +1038,10 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) .data = data, .retval = 0, }; - dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); - ret = param.retval; + if (dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0) < 0) + ret = -EINVAL; + else + ret = param.retval; } return ret; @@ -1940,10 +1945,12 @@ static bool die_get_postprologue_addr(unsigned long entrypc_idx, break; } - dwarf_lineaddr(line, postprologue_addr); - if (*postprologue_addr >= highpc) - dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), - postprologue_addr); + if (dwarf_lineaddr(line, postprologue_addr) != 0) + return false; + if (*postprologue_addr >= highpc) { + if (dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), postprologue_addr) != 0) + return false; + } return true; } -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v3 3/6] perf libdw: Fix libdw API contract violations 2026-05-03 0:35 [PATCH v3 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 2026-05-03 0:35 ` [PATCH v3 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers 2026-05-03 0:35 ` [PATCH v3 2/6] perf dwarf-aux: Fix libdw API contract violations Ian Rogers @ 2026-05-03 0:35 ` Ian Rogers 2026-05-03 0:35 ` [PATCH v3 4/6] perf probe-finder: " Ian Rogers ` (3 subsequent siblings) 6 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-03 0:35 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwfl_report_end` and `dwfl_module_addrdie`. Validate `dwarf_diename` result before passing to `new_inline_sym` (avoid potential `strdup(NULL)` crash) and check `die_get_call_lineno` for errors. Fixes: b7a2b011e962 ("perf powerpc: Unify the skip-callchain-idx libdw with that for addr2line") Fixes: 88c51002d06f ("perf addr2line: Add a libdw implementation") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/libdw.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 216977884103..e37f3b22699d 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -60,7 +60,11 @@ struct Dwfl *dso__libdw_dwfl(struct dso *dso) return NULL; } - dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL); + if (dwfl_report_end(dwfl, NULL, NULL) != 0) { + dwfl_end(dwfl); + return NULL; + } + dso__set_libdw(dso, dwfl); return dwfl; @@ -72,22 +76,27 @@ struct libdw_a2l_cb_args { struct inline_node *node; char *leaf_srcline; bool leaf_srcline_used; + int err; }; static int libdw_a2l_cb(Dwarf_Die *die, void *_args) { struct libdw_a2l_cb_args *args = _args; - struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); + const char *name = dwarf_diename(die); + struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, name ?: "unknown"); const char *call_fname = die_get_call_file(die); + int call_lineno = die_get_call_lineno(die); char *call_srcline = srcline__unknown; struct inline_list *ilist; - if (!inline_sym) - return -ENOMEM; + if (!inline_sym) { + args->err = -ENOMEM; + return DWARF_CB_ABORT; + } /* Assign caller information to the parent. */ - if (call_fname) - call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die)); + if (call_fname && call_lineno > 0) + call_srcline = srcline_from_fileline(call_fname, call_lineno); list_for_each_entry(ilist, &args->node->val, list) { if (args->leaf_srcline == ilist->srcline) @@ -163,7 +172,8 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, }; /* Walk from the parent down to the leaf. */ - cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); + if (cudie) + cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); if (!args.leaf_srcline_used) free(args.leaf_srcline); -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v3 4/6] perf probe-finder: Fix libdw API contract violations 2026-05-03 0:35 [PATCH v3 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (2 preceding siblings ...) 2026-05-03 0:35 ` [PATCH v3 3/6] perf libdw: " Ian Rogers @ 2026-05-03 0:35 ` Ian Rogers 2026-05-03 0:35 ` [PATCH v3 5/6] perf annotate-data: " Ian Rogers ` (2 subsequent siblings) 6 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-03 0:35 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwarf_formsdata`, `dwarf_entrypc`, `dwarf_highpc`, `dwarf_bytesize`, `dwarf_attr`, `dwarf_decl_line`, `dwarf_getfuncs`, and `dwarf_formref_die`. Validate `dwarf_diename` and `dwarf_diecu` results to prevent potential crashes. Fix C90 mixed declarations. Fixes: 66f69b219716 ("perf probe: Support DW_AT_const_value constant value") Fixes: 3d918a12a1b3 ("perf probe: Find fentry mcount fuzzed parameter location") Fixes: bcfc082150c6 ("perf probe: Remove redundant dwarf functions") Fixes: 221d061182b8 ("perf probe: Fix to search local variables in appropriate scope") Fixes: b55a87ade383 ("perf probe: Remove die() from probe-finder code") Fixes: 4c859351226c ("perf probe: Support glob wildcards for function name") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/probe-finder.c | 85 ++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 64328abeef8b..bdef340dfd55 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -93,7 +93,8 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (!tvar) return 0; - dwarf_formsdata(&attr, &snum); + if (dwarf_formsdata(&attr, &snum) != 0) + return -ENOENT; ret = asprintf(&tvar->value, "\\%ld", (long)snum); return ret < 0 ? -ENOMEM : 0; @@ -103,8 +104,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) return -EINVAL; /* Broken DIE ? */ if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { - ret = dwarf_entrypc(sp_die, &tmp); - if (ret) + if (dwarf_entrypc(sp_die, &tmp) != 0) return -ENOENT; if (probe_conf.show_location_range && @@ -115,8 +115,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, return -ENOENT; } - ret = dwarf_highpc(sp_die, &tmp); - if (ret) + if (dwarf_highpc(sp_die, &tmp) != 0) return -ENOENT; /* * This is fuzzed by fentry mcount. We try to find the @@ -138,15 +137,21 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, static_var: if (!tvar) return ret2; - /* Static variables on memory (not stack), make @varname */ - ret = strlen(dwarf_diename(vr_die)); - tvar->value = zalloc(ret + 2); - if (tvar->value == NULL) - return -ENOMEM; - snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); - tvar->ref = alloc_trace_arg_ref((long)offs); - if (tvar->ref == NULL) - return -ENOMEM; + { + /* Static variables on memory (not stack), make @varname */ + const char *name = dwarf_diename(vr_die); + + if (!name) + return -ENOENT; + ret = strlen(name); + tvar->value = zalloc(ret + 2); + if (tvar->value == NULL) + return -ENOMEM; + snprintf(tvar->value, ret + 2, "@%s", name); + tvar->ref = alloc_trace_arg_ref((long)offs); + if (tvar->ref == NULL) + return -ENOMEM; + } return ret2; } @@ -234,8 +239,8 @@ static int convert_variable_type(Dwarf_Die *vr_die, } if (die_get_real_type(vr_die, &type) == NULL) { - pr_warning("Failed to get a type information of %s.\n", - dwarf_diename(vr_die)); + const char *name = dwarf_diename(vr_die); + pr_warning("Failed to get a type information of %s.\n", name ? name : "<unknown>"); return -ENOENT; } @@ -291,7 +296,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, probe_type_is_available(PROBE_TYPE_X) ? 'x' : 'u'; ret = dwarf_bytesize(&type); - if (ret <= 0) + if (ret < 0) /* No size ... try to use default type */ return 0; ret = BYTES_TO_BITS(ret); @@ -357,7 +362,13 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, else *ref_ptr = ref; } - ref->offset += dwarf_bytesize(&type) * field->index; + { + int bsize = dwarf_bytesize(&type); + + if (bsize < 0) + return -EINVAL; + ref->offset += bsize * field->index; + } ref->user_access = user_access; goto next; } else if (tag == DW_TAG_pointer_type) { @@ -611,10 +622,16 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); /* Get the frame base attribute/ops from subprogram */ - dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); - ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); - if (ret <= 0 || nops == 0) { + if (dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr) == NULL) { pf->fb_ops = NULL; + } else { + ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); + if (ret <= 0 || nops == 0) + pf->fb_ops = NULL; + } + + if (pf->fb_ops == NULL) { + /* Not supported */ } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && (pf->cfi_eh != NULL || pf->cfi_dbg != NULL)) { if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 && @@ -667,8 +684,8 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) } } else { /* With the line number, find the nearest declared DIE */ - dwarf_decl_line(fn_die, &lno); - if (lno < fsp->line && fsp->diff > fsp->line - lno) { + if (dwarf_decl_line(fn_die, &lno) == 0 && lno < fsp->line && + fsp->diff > fsp->line - lno) { /* Keep a candidate and continue */ fsp->diff = fsp->line - lno; memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); @@ -1018,7 +1035,8 @@ static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; - dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); + if (dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0) < 0) + return -ENOENT; return _param.retval; } @@ -1207,7 +1225,8 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void *data) * points to correct die. */ if (dwarf_attr(die_mem, DW_AT_abstract_origin, &attr)) { - dwarf_formref_die(&attr, &var_die); + if (dwarf_formref_die(&attr, &var_die) == NULL) + goto out; if (pf->abstrace_dieoffset != dwarf_dieoffset(&var_die)) goto out; } @@ -1270,6 +1289,8 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) struct probe_trace_event *tev; struct perf_probe_arg *args = NULL; int ret, i; + const char *realname; + Dwarf_Die cu_die_mem; /* * For some reason (e.g. different column assigned to same address) @@ -1293,13 +1314,17 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) if (ret < 0) goto end; - tev->point.realname = strdup(dwarf_diename(sc_die)); + realname = dwarf_diename(sc_die); + tev->point.realname = strdup(realname ?: "unknown"); if (!tev->point.realname) { ret = -ENOMEM; goto end; } - tev->lang = dwarf_srclang(dwarf_diecu(sc_die, &pf->cu_die, NULL, NULL)); + if (dwarf_diecu(sc_die, &cu_die_mem, NULL, NULL) != NULL) + tev->lang = dwarf_srclang(&cu_die_mem); + else + tev->lang = DW_LANG_C; // Fallback pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); @@ -1794,7 +1819,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) if (die_match_name(sp_die, lr->function) && die_is_func_def(sp_die)) { lf->fname = die_get_decl_file(sp_die); - dwarf_decl_line(sp_die, &lr->offset); + if (dwarf_decl_line(sp_die, &lr->offset) != 0) + return DWARF_CB_OK; // Skip if no line info pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; if (lf->lno_s < 0) /* Overflow */ @@ -1818,7 +1844,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; - dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); + if (dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0) < 0) + return -ENOENT; return param.retval; } -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v3 5/6] perf annotate-data: Fix libdw API contract violations 2026-05-03 0:35 [PATCH v3 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (3 preceding siblings ...) 2026-05-03 0:35 ` [PATCH v3 4/6] perf probe-finder: " Ian Rogers @ 2026-05-03 0:35 ` Ian Rogers 2026-05-03 0:35 ` [PATCH v3 6/6] perf debuginfo: " Ian Rogers 2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 6 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-03 0:35 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwarf_aggregate_size` and `dwarf_formudata`. Validate `dwarf_diename` before `strdup` to prevent potential crashes. Fixes: 2bc3cf575a16 ("perf annotate-data: Improve debug message with location info") Fixes: 4a111cadac85 ("perf annotate-data: Add member field in the data type") Fixes: 8b1042c425f6 ("perf annotate-data: Set bitfield member offset and size properly") Fixes: fc044c53b99f ("perf annotate-data: Add dso->data_types tree") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/annotate-data.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 1eff0a27237d..e881a40a4885 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -74,7 +74,8 @@ void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) break; } - dwarf_aggregate_size(die, &size); + if (dwarf_aggregate_size(die, &size) != 0) + size = 0; strbuf_init(&sb, 32); die_get_typename_from_type(die, &sb); @@ -250,9 +251,10 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) if (dwarf_aggregate_size(&die_mem, &size) < 0) size = 0; - if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) - dwarf_formudata(&attr, &loc); - else { + if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) { + if (dwarf_formudata(&attr, &loc) != 0) + loc = 0; + } else { /* bitfield member */ if (dwarf_attr_integrate(die, DW_AT_data_bit_offset, &attr) && dwarf_formudata(&attr, &loc) == 0) @@ -273,7 +275,9 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) dwarf_diename(die), (long)bit_size) < 0) member->var_name = NULL; } else { - member->var_name = strdup(dwarf_diename(die)); + const char *name = dwarf_diename(die); + + member->var_name = strdup(name ?: "unknown"); } if (member->var_name == NULL) { @@ -370,7 +374,8 @@ static struct annotated_data_type *dso__findnew_data_type(struct dso *dso, if (dwarf_tag(type_die) == DW_TAG_typedef) die_get_real_type(type_die, type_die); - dwarf_aggregate_size(type_die, &size); + if (dwarf_aggregate_size(type_die, &size) != 0) + size = 0; /* Check existing nodes in dso->data_types tree */ key.self.type_name = type_name; -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v3 6/6] perf debuginfo: Fix libdw API contract violations 2026-05-03 0:35 [PATCH v3 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (4 preceding siblings ...) 2026-05-03 0:35 ` [PATCH v3 5/6] perf annotate-data: " Ian Rogers @ 2026-05-03 0:35 ` Ian Rogers 2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 6 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-03 0:35 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return value of `dwfl_report_end` during offline initialization. Validate `dwfl_module_relocation_info` result before passing to `strcmp` to avoid potential segmentation faults. Fixes: 6f1b6291cf73 ("perf tools: Add util/debuginfo.[ch] files") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/debuginfo.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c index 0e35c13abd04..49cb7f9b715d 100644 --- a/tools/perf/util/debuginfo.c +++ b/tools/perf/util/debuginfo.c @@ -62,7 +62,8 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); - dwfl_report_end(dbg->dwfl, NULL, NULL); + if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0) + goto error; return 0; error: @@ -167,7 +168,7 @@ int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, /* Search the relocation related .text section */ for (i = 0; i < n; i++) { p = dwfl_module_relocation_info(dbg->mod, i, &shndx); - if (strcmp(p, ".text") == 0) { + if (p && strcmp(p, ".text") == 0) { /* OK, get the section header */ scn = elf_getscn(elf, shndx); if (!scn) -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes 2026-05-03 0:35 [PATCH v3 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (5 preceding siblings ...) 2026-05-03 0:35 ` [PATCH v3 6/6] perf debuginfo: " Ian Rogers @ 2026-05-03 17:10 ` Ian Rogers 2026-05-03 17:10 ` [PATCH v4 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers ` (6 more replies) 6 siblings, 7 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-03 17:10 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers This patch series addresses a number of DWARF/libdw error-handling bugs and contract violations, preventing several real Userspace segmentation faults and memory/FD leaks. In v4, the series has been thoroughly hardened based on review feedback for v3, fixing latent bugs and improving unwinding/search robustness: - **Memory/FD Leak Fixes**: Fixed file descriptor leaks in `debuginfo` offline init paths, and memory leaks when `strdup` or inlined list appends fail during callback walks. - **Callchain Robustness**: Fixed `ORDER_CALLER` update bugs to prevent inline callchain corruption when nest depth > 2. Re-implemented `die_get_data_member_location` helper with `dwarf_attr_integrate` to safely parse location expression offsets for inherited properties (specifications/origins). - **Search Robustness**: Relaxed strict `dwarf_getfuncs` error aborts in `probe-finder` and `line-range` loops to allow skipping individual corrupted CUs instead of prematurely stopping searches entirely. Added DWARF line 0 reference support. - **Safe DWARF Printing**: Switched to the `die_name()` safe printing helper globally to prevent `vfprintf(NULL)` undefined behavior/crashes when DWARF entities (like anonymous structs or tail calls) lack names. v4: - Localized and squashed robust error handling, memory/FD cleanup paths, and safe DWARF printing. v3: - Minor formatting fixes. https://lore.kernel.org/linux-perf-users/20260503003552.1063540-1-irogers@google.com/ v2: https://lore.kernel.org/lkml/20260502155656.478642-1-irogers@google.com/ v1: https://lore.kernel.org/linux-perf-users/20260502064839.282422-1-irogers@google.com/ Ian Rogers (6): perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at perf dwarf-aux: Fix libdw API contract violations perf libdw: Fix libdw API contract violations perf probe-finder: Fix libdw API contract violations perf annotate-data: Fix libdw API contract violations perf debuginfo: Fix libdw API contract violations tools/perf/util/annotate-data.c | 27 +++++--- tools/perf/util/debuginfo.c | 9 ++- tools/perf/util/dwarf-aux.c | 39 ++++++------ tools/perf/util/dwarf-aux.h | 5 ++ tools/perf/util/libdw.c | 84 ++++++++++++++++++++----- tools/perf/util/probe-finder.c | 105 +++++++++++++++++++++----------- tools/perf/util/srcline.c | 9 ++- tools/perf/util/srcline.h | 1 + 8 files changed, 191 insertions(+), 88 deletions(-) -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v4 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at 2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers @ 2026-05-03 17:10 ` Ian Rogers 2026-05-03 23:33 ` Namhyung Kim 2026-05-03 17:10 ` [PATCH v4 2/6] perf dwarf-aux: Fix libdw API contract violations Ian Rogers ` (5 subsequent siblings) 6 siblings, 1 reply; 33+ messages in thread From: Ian Rogers @ 2026-05-03 17:10 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers 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> --- tools/perf/util/dwarf-aux.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 92db2fccc788..109a166a6d19 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -171,7 +171,6 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, } return ret; - } /** @@ -620,7 +619,7 @@ Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 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; @@ -659,7 +658,7 @@ Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 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; -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v4 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at 2026-05-03 17:10 ` [PATCH v4 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers @ 2026-05-03 23:33 ` Namhyung Kim 0 siblings, 0 replies; 33+ messages in thread From: Namhyung Kim @ 2026-05-03 23:33 UTC (permalink / raw) To: Ian Rogers Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel On Sun, May 03, 2026 at 10:10:27AM -0700, Ian Rogers wrote: > 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> Thanks, Namhyung > --- > tools/perf/util/dwarf-aux.c | 5 ++--- > 1 file changed, 2 insertions(+), 3 deletions(-) > > diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c > index 92db2fccc788..109a166a6d19 100644 > --- a/tools/perf/util/dwarf-aux.c > +++ b/tools/perf/util/dwarf-aux.c > @@ -171,7 +171,6 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, > } > > return ret; > - > } > > /** > @@ -620,7 +619,7 @@ Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, > 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; > @@ -659,7 +658,7 @@ Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, > 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; > -- > 2.54.0.545.g6539524ca2-goog > ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v4 2/6] perf dwarf-aux: Fix libdw API contract violations 2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 2026-05-03 17:10 ` [PATCH v4 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers @ 2026-05-03 17:10 ` Ian Rogers 2026-05-03 23:36 ` Namhyung Kim 2026-05-03 17:10 ` [PATCH v4 3/6] perf libdw: " Ian Rogers ` (4 subsequent siblings) 6 siblings, 1 reply; 33+ messages in thread From: Ian Rogers @ 2026-05-03 17:10 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwarf_decl_line` (where non-optional), `dwarf_getfuncs`, and `dwarf_lineaddr` to prevent using uninitialized stack variables or incorrectly reporting success on failure. For the root DIE in `die_walk_lines()`, `dwarf_decl_line` and `die_get_decl_file` are optional and their failures are handled gracefully to avoid breaking line walking on valid functions. Additionally: - Add NULL pointer protection for `strcmp()` in `die_walk_lines()` when `inf` or `decf` are NULL to prevent crashes on generated code. - Use `dwarf_attr_integrate` in `die_get_data_member_location` to correctly resolve inherited member locations (e.g. via abstract origin or specification). Fixes: 57f95bf5f882 ("perf probe: Show correct statement line number by perf probe -l") Fixes: 3f4460a28fb2 ("perf probe: Filter out redundant inline-instances") Fixes: 75186a9b09e4 ("perf probe: Fix to show lines of sys_ functions correctly") Fixes: e0d153c69040 ("perf-probe: Move dwarf library routines to dwarf-aux.{c, h}") Fixes: 6243b9dc4c99 ("perf probe: Move dwarf specific functions to dwarf-aux.c") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- v4: - Fix strcmp(NULL) risk and inherited member location fallbacks in dwarf-aux.c. --- tools/perf/util/dwarf-aux.c | 34 ++++++++++++++++------------------ tools/perf/util/dwarf-aux.h | 5 +++++ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 109a166a6d19..a8ab1c30d0ac 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -125,7 +125,8 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, Dwarf_Addr addr, && die_entrypc(&die_mem, &faddr) == 0 && faddr == addr) { *fname = die_get_decl_file(&die_mem); - dwarf_decl_line(&die_mem, lineno); + if (dwarf_decl_line(&die_mem, lineno) != 0) + return -ENOENT; goto out; } @@ -459,7 +460,7 @@ int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) size_t nexpr; int ret; - if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) + if (dwarf_attr_integrate(mb_die, DW_AT_data_member_location, &attr) == NULL) return -ENOENT; if (dwarf_formudata(&attr, offs) != 0) { @@ -795,8 +796,7 @@ static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) /* Ignore redundant instances */ if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { - dwarf_decl_line(origin, &tmp); - if (die_get_call_lineno(inst) == tmp) { + if (dwarf_decl_line(origin, &tmp) == 0 && die_get_call_lineno(inst) == tmp) { tmp = die_get_decl_fileno(origin); if (die_get_call_fileno(inst) == tmp) return DIE_FIND_CB_CONTINUE; @@ -950,11 +950,6 @@ 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 = die_get_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) { @@ -998,12 +993,11 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) if (die_find_inlinefunc(rt_die, addr, &die_mem)) { /* Call-site check */ inf = die_get_call_file(&die_mem); - if ((inf && !strcmp(inf, decf)) && + if ((inf == decf || (inf && decf && !strcmp(inf, decf))) && die_get_call_lineno(&die_mem) == lineno) goto found; - dwarf_decl_line(&die_mem, &inl); - if (inl != decl || + if (dwarf_decl_line(&die_mem, &inl) != 0 || inl != decl || decf != die_get_decl_file(&die_mem)) continue; } @@ -1034,8 +1028,10 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) .data = data, .retval = 0, }; - dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); - ret = param.retval; + if (dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0) < 0) + ret = -EINVAL; + else + ret = param.retval; } return ret; @@ -1939,10 +1935,12 @@ static bool die_get_postprologue_addr(unsigned long entrypc_idx, break; } - dwarf_lineaddr(line, postprologue_addr); - if (*postprologue_addr >= highpc) - dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), - postprologue_addr); + if (dwarf_lineaddr(line, postprologue_addr) != 0) + return false; + if (*postprologue_addr >= highpc) { + if (dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), postprologue_addr) != 0) + return false; + } return true; } diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index a79968a2e573..161f0bf980b6 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -10,6 +10,11 @@ #include <elfutils/libdwfl.h> #include <elfutils/version.h> +static inline const char *die_name(Dwarf_Die *die) +{ + return dwarf_diename(die) ?: "<unknown>"; +} + struct strbuf; /* Find the realpath of the target file */ -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v4 2/6] perf dwarf-aux: Fix libdw API contract violations 2026-05-03 17:10 ` [PATCH v4 2/6] perf dwarf-aux: Fix libdw API contract violations Ian Rogers @ 2026-05-03 23:36 ` Namhyung Kim 0 siblings, 0 replies; 33+ messages in thread From: Namhyung Kim @ 2026-05-03 23:36 UTC (permalink / raw) To: Ian Rogers Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel On Sun, May 03, 2026 at 10:10:28AM -0700, Ian Rogers wrote: > Check return values of `dwarf_decl_line` (where non-optional), > `dwarf_getfuncs`, and `dwarf_lineaddr` to prevent using uninitialized > stack variables or incorrectly reporting success on failure. > > For the root DIE in `die_walk_lines()`, `dwarf_decl_line` and > `die_get_decl_file` are optional and their failures are handled > gracefully to avoid breaking line walking on valid functions. > > Additionally: > - Add NULL pointer protection for `strcmp()` in `die_walk_lines()` > when `inf` or `decf` are NULL to prevent crashes on generated > code. > - Use `dwarf_attr_integrate` in `die_get_data_member_location` to > correctly resolve inherited member locations (e.g. via abstract > origin or specification). > > Fixes: 57f95bf5f882 ("perf probe: Show correct statement line number by perf probe -l") > Fixes: 3f4460a28fb2 ("perf probe: Filter out redundant inline-instances") > Fixes: 75186a9b09e4 ("perf probe: Fix to show lines of sys_ functions correctly") > Fixes: e0d153c69040 ("perf-probe: Move dwarf library routines to dwarf-aux.{c, h}") > Fixes: 6243b9dc4c99 ("perf probe: Move dwarf specific functions to dwarf-aux.c") > Assisted-by: Gemini-CLI:Google Gemini 3 > Signed-off-by: Ian Rogers <irogers@google.com> > --- > v4: > - Fix strcmp(NULL) risk and inherited member location fallbacks in dwarf-aux.c. > --- > tools/perf/util/dwarf-aux.c | 34 ++++++++++++++++------------------ > tools/perf/util/dwarf-aux.h | 5 +++++ > 2 files changed, 21 insertions(+), 18 deletions(-) > > diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c > index 109a166a6d19..a8ab1c30d0ac 100644 > --- a/tools/perf/util/dwarf-aux.c > +++ b/tools/perf/util/dwarf-aux.c > @@ -125,7 +125,8 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, Dwarf_Addr addr, > && die_entrypc(&die_mem, &faddr) == 0 && > faddr == addr) { > *fname = die_get_decl_file(&die_mem); > - dwarf_decl_line(&die_mem, lineno); > + if (dwarf_decl_line(&die_mem, lineno) != 0) > + return -ENOENT; > goto out; > } > > @@ -459,7 +460,7 @@ int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) > size_t nexpr; > int ret; > > - if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) > + if (dwarf_attr_integrate(mb_die, DW_AT_data_member_location, &attr) == NULL) > return -ENOENT; > > if (dwarf_formudata(&attr, offs) != 0) { > @@ -795,8 +796,7 @@ static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) > > /* Ignore redundant instances */ > if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { > - dwarf_decl_line(origin, &tmp); > - if (die_get_call_lineno(inst) == tmp) { > + if (dwarf_decl_line(origin, &tmp) == 0 && die_get_call_lineno(inst) == tmp) { > tmp = die_get_decl_fileno(origin); > if (die_get_call_fileno(inst) == tmp) > return DIE_FIND_CB_CONTINUE; > @@ -950,11 +950,6 @@ 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 = die_get_decl_file(rt_die); > - if (!decf) { > - pr_debug2("Failed to get the declared file name of %s\n", > - dwarf_diename(rt_die)); > - return -EINVAL; > - } Why is this part removed? > } else > cu_die = rt_die; > if (!cu_die) { > @@ -998,12 +993,11 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) > if (die_find_inlinefunc(rt_die, addr, &die_mem)) { > /* Call-site check */ > inf = die_get_call_file(&die_mem); > - if ((inf && !strcmp(inf, decf)) && > + if ((inf == decf || (inf && decf && !strcmp(inf, decf))) && > die_get_call_lineno(&die_mem) == lineno) > goto found; > > - dwarf_decl_line(&die_mem, &inl); > - if (inl != decl || > + if (dwarf_decl_line(&die_mem, &inl) != 0 || inl != decl || > decf != die_get_decl_file(&die_mem)) > continue; > } > @@ -1034,8 +1028,10 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) > .data = data, > .retval = 0, > }; > - dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); > - ret = param.retval; > + if (dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0) < 0) > + ret = -EINVAL; > + else > + ret = param.retval; > } > > return ret; > @@ -1939,10 +1935,12 @@ static bool die_get_postprologue_addr(unsigned long entrypc_idx, > break; > } > > - dwarf_lineaddr(line, postprologue_addr); > - if (*postprologue_addr >= highpc) > - dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), > - postprologue_addr); > + if (dwarf_lineaddr(line, postprologue_addr) != 0) > + return false; > + if (*postprologue_addr >= highpc) { > + if (dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), postprologue_addr) != 0) > + return false; > + } > > return true; > } > diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h > index a79968a2e573..161f0bf980b6 100644 > --- a/tools/perf/util/dwarf-aux.h > +++ b/tools/perf/util/dwarf-aux.h > @@ -10,6 +10,11 @@ > #include <elfutils/libdwfl.h> > #include <elfutils/version.h> > > +static inline const char *die_name(Dwarf_Die *die) > +{ > + return dwarf_diename(die) ?: "<unknown>"; > +} > + Please move it where it's used. Thanks, Namhyung > struct strbuf; > > /* Find the realpath of the target file */ > -- > 2.54.0.545.g6539524ca2-goog > ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v4 3/6] perf libdw: Fix libdw API contract violations 2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 2026-05-03 17:10 ` [PATCH v4 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers 2026-05-03 17:10 ` [PATCH v4 2/6] perf dwarf-aux: Fix libdw API contract violations Ian Rogers @ 2026-05-03 17:10 ` Ian Rogers 2026-05-03 23:44 ` Namhyung Kim 2026-05-03 17:10 ` [PATCH v4 4/6] perf probe-finder: " Ian Rogers ` (3 subsequent siblings) 6 siblings, 1 reply; 33+ messages in thread From: Ian Rogers @ 2026-05-03 17:10 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwfl_report_end` and `dwfl_module_addrdie`. Check `die_get_call_lineno` for errors. Additionally: - Introduce `inline_node__clear_frames()` to clean up partial allocations. - Ensure `*file` is freed and inline frames are cleared on error in `libdw__addr2line()` to prevent memory leaks and duplicated callchains when falling back to other unwinders. - Allow DWARF line 0 in `libdw_a2l_cb()`, as it is a valid reference for compiler-generated code. - Fix the parent srcline lookup in `libdw_a2l_cb()` to target the correct parent node depending on the callchain order (ORDER_CALLER/ORDER_CALLEE). Fixes: b7a2b011e962 ("perf powerpc: Unify the skip-callchain-idx libdw with that for addr2line") Fixes: 88c51002d06f ("perf addr2line: Add a libdw implementation") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- v4: - Fix memory leaks and duplicated callchains on unwinding errors. - Support DWARF line 0 in inline list. - Fix callchain parent update in ORDER_CALLER mode. --- tools/perf/util/libdw.c | 84 +++++++++++++++++++++++++++++++-------- tools/perf/util/srcline.c | 9 ++++- tools/perf/util/srcline.h | 1 + 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 216977884103..e69ff4cea856 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -4,6 +4,7 @@ #include "srcline.h" #include "symbol.h" #include "dwarf-aux.h" +#include "callchain.h" #include <fcntl.h> #include <unistd.h> #include <elfutils/libdwfl.h> @@ -60,7 +61,11 @@ struct Dwfl *dso__libdw_dwfl(struct dso *dso) return NULL; } - dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL); + if (dwfl_report_end(dwfl, NULL, NULL) != 0) { + dwfl_end(dwfl); + return NULL; + } + dso__set_libdw(dso, dwfl); return dwfl; @@ -72,41 +77,70 @@ struct libdw_a2l_cb_args { struct inline_node *node; char *leaf_srcline; bool leaf_srcline_used; + int err; }; static int libdw_a2l_cb(Dwarf_Die *die, void *_args) { struct libdw_a2l_cb_args *args = _args; - struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); + const char *name = dwarf_diename(die); + struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, name ?: "unknown"); const char *call_fname = die_get_call_file(die); + int call_lineno = die_get_call_lineno(die); char *call_srcline = srcline__unknown; - struct inline_list *ilist; - if (!inline_sym) - return -ENOMEM; + if (!inline_sym) { + args->err = -ENOMEM; + return DWARF_CB_ABORT; + } /* Assign caller information to the parent. */ - if (call_fname) - call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die)); + if (call_fname && call_lineno >= 0) + call_srcline = srcline_from_fileline(call_fname, call_lineno); - list_for_each_entry(ilist, &args->node->val, list) { - if (args->leaf_srcline == ilist->srcline) + if (!list_empty(&args->node->val)) { + struct inline_list *parent; + + if (callchain_param.order == ORDER_CALLEE) + parent = list_first_entry(&args->node->val, struct inline_list, list); + else + parent = list_last_entry(&args->node->val, struct inline_list, list); + + if (args->leaf_srcline == parent->srcline) args->leaf_srcline_used = false; - else if (ilist->srcline != srcline__unknown) - free(ilist->srcline); - ilist->srcline = call_srcline; + else if (parent->srcline != srcline__unknown) + free(parent->srcline); + parent->srcline = call_srcline; call_srcline = NULL; - break; } if (call_srcline && call_srcline != srcline__unknown) free(call_srcline); /* Add this symbol to the chain as the leaf. */ if (!args->leaf_srcline_used) { - inline_list__append_tail(inline_sym, args->leaf_srcline, args->node); + if (inline_list__append_tail(inline_sym, args->leaf_srcline, args->node) != 0) { + args->err = -ENOMEM; + if (inline_sym->inlined) + symbol__delete(inline_sym); + return DWARF_CB_ABORT; + } args->leaf_srcline_used = true; } else { - inline_list__append_tail(inline_sym, strdup(args->leaf_srcline), args->node); + char *srcline = strdup(args->leaf_srcline); + + if (!srcline) { + args->err = -ENOMEM; + if (inline_sym->inlined) + symbol__delete(inline_sym); + return DWARF_CB_ABORT; + } + if (inline_list__append_tail(inline_sym, srcline, args->node) != 0) { + free(srcline); + args->err = -ENOMEM; + if (inline_sym->inlined) + symbol__delete(inline_sym); + return DWARF_CB_ABORT; + } } return 0; } @@ -162,11 +196,29 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, .leaf_srcline = srcline_from_fileline(src ?: "<unknown>", lineno), }; + if (!args.leaf_srcline) { + if (file && *file) { + free(*file); + *file = NULL; + } + return 0; + } + /* Walk from the parent down to the leaf. */ - cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); + if (cudie) + cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); if (!args.leaf_srcline_used) free(args.leaf_srcline); + + if (args.err) { + if (file && *file) { + free(*file); + *file = NULL; + } + inline_node__clear_frames(node); + return 0; + } } return 1; } diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index db164d258163..62884428fb5a 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -429,10 +429,13 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, return addr2inlines(dso_name, addr, dso, sym); } -void inline_node__delete(struct inline_node *node) +void inline_node__clear_frames(struct inline_node *node) { struct inline_list *ilist, *tmp; + if (node == NULL) + return; + list_for_each_entry_safe(ilist, tmp, &node->val, list) { list_del_init(&ilist->list); zfree_srcline(&ilist->srcline); @@ -441,7 +444,11 @@ void inline_node__delete(struct inline_node *node) symbol__delete(ilist->symbol); free(ilist); } +} +void inline_node__delete(struct inline_node *node) +{ + inline_node__clear_frames(node); free(node); } diff --git a/tools/perf/util/srcline.h b/tools/perf/util/srcline.h index 7c37b3bf9ce7..1018cbc886d6 100644 --- a/tools/perf/util/srcline.h +++ b/tools/perf/util/srcline.h @@ -47,6 +47,7 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, struct symbol *sym); /* free resources associated to the inline node list */ void inline_node__delete(struct inline_node *node); +void inline_node__clear_frames(struct inline_node *node); /* insert the inline node list into the DSO, which will take ownership */ void inlines__tree_insert(struct rb_root_cached *tree, -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v4 3/6] perf libdw: Fix libdw API contract violations 2026-05-03 17:10 ` [PATCH v4 3/6] perf libdw: " Ian Rogers @ 2026-05-03 23:44 ` Namhyung Kim 0 siblings, 0 replies; 33+ messages in thread From: Namhyung Kim @ 2026-05-03 23:44 UTC (permalink / raw) To: Ian Rogers Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel On Sun, May 03, 2026 at 10:10:29AM -0700, Ian Rogers wrote: > Check return values of `dwfl_report_end` and `dwfl_module_addrdie`. > Check `die_get_call_lineno` for errors. > > Additionally: > - Introduce `inline_node__clear_frames()` to clean up partial > allocations. Sounds like it should be a separate commit. > - Ensure `*file` is freed and inline frames are cleared on error in > `libdw__addr2line()` to prevent memory leaks and duplicated > callchains when falling back to other unwinders. > - Allow DWARF line 0 in `libdw_a2l_cb()`, as it is a valid > reference for compiler-generated code. > - Fix the parent srcline lookup in `libdw_a2l_cb()` to target the > correct parent node depending on the callchain order > (ORDER_CALLER/ORDER_CALLEE). Also it looks like an independent fix. While you could put other fixes into separate commits (up to you), we can merge simple things. > > Fixes: b7a2b011e962 ("perf powerpc: Unify the skip-callchain-idx libdw with that for addr2line") > Fixes: 88c51002d06f ("perf addr2line: Add a libdw implementation") > Assisted-by: Gemini-CLI:Google Gemini 3 > Signed-off-by: Ian Rogers <irogers@google.com> > --- > v4: > - Fix memory leaks and duplicated callchains on unwinding errors. > - Support DWARF line 0 in inline list. > - Fix callchain parent update in ORDER_CALLER mode. > --- > tools/perf/util/libdw.c | 84 +++++++++++++++++++++++++++++++-------- > tools/perf/util/srcline.c | 9 ++++- > tools/perf/util/srcline.h | 1 + > 3 files changed, 77 insertions(+), 17 deletions(-) > > diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c > index 216977884103..e69ff4cea856 100644 > --- a/tools/perf/util/libdw.c > +++ b/tools/perf/util/libdw.c > @@ -4,6 +4,7 @@ > #include "srcline.h" > #include "symbol.h" > #include "dwarf-aux.h" > +#include "callchain.h" > #include <fcntl.h> > #include <unistd.h> > #include <elfutils/libdwfl.h> > @@ -60,7 +61,11 @@ struct Dwfl *dso__libdw_dwfl(struct dso *dso) > return NULL; > } > > - dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL); > + if (dwfl_report_end(dwfl, NULL, NULL) != 0) { Better to keep the comments? > + dwfl_end(dwfl); > + return NULL; > + } > + > dso__set_libdw(dso, dwfl); > > return dwfl; > @@ -72,41 +77,70 @@ struct libdw_a2l_cb_args { > struct inline_node *node; > char *leaf_srcline; > bool leaf_srcline_used; > + int err; > }; > > static int libdw_a2l_cb(Dwarf_Die *die, void *_args) > { > struct libdw_a2l_cb_args *args = _args; > - struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); > + const char *name = dwarf_diename(die); > + struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, name ?: "unknown"); Use die_getname() here? > const char *call_fname = die_get_call_file(die); > + int call_lineno = die_get_call_lineno(die); > char *call_srcline = srcline__unknown; > - struct inline_list *ilist; > > - if (!inline_sym) > - return -ENOMEM; > + if (!inline_sym) { > + args->err = -ENOMEM; > + return DWARF_CB_ABORT; > + } > > /* Assign caller information to the parent. */ > - if (call_fname) > - call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die)); > + if (call_fname && call_lineno >= 0) > + call_srcline = srcline_from_fileline(call_fname, call_lineno); > > - list_for_each_entry(ilist, &args->node->val, list) { > - if (args->leaf_srcline == ilist->srcline) > + if (!list_empty(&args->node->val)) { > + struct inline_list *parent; > + > + if (callchain_param.order == ORDER_CALLEE) > + parent = list_first_entry(&args->node->val, struct inline_list, list); > + else > + parent = list_last_entry(&args->node->val, struct inline_list, list); > + > + if (args->leaf_srcline == parent->srcline) > args->leaf_srcline_used = false; > - else if (ilist->srcline != srcline__unknown) > - free(ilist->srcline); > - ilist->srcline = call_srcline; > + else if (parent->srcline != srcline__unknown) > + free(parent->srcline); > + parent->srcline = call_srcline; > call_srcline = NULL; > - break; > } > if (call_srcline && call_srcline != srcline__unknown) > free(call_srcline); > > /* Add this symbol to the chain as the leaf. */ > if (!args->leaf_srcline_used) { > - inline_list__append_tail(inline_sym, args->leaf_srcline, args->node); > + if (inline_list__append_tail(inline_sym, args->leaf_srcline, args->node) != 0) { > + args->err = -ENOMEM; > + if (inline_sym->inlined) > + symbol__delete(inline_sym); > + return DWARF_CB_ABORT; > + } > args->leaf_srcline_used = true; > } else { > - inline_list__append_tail(inline_sym, strdup(args->leaf_srcline), args->node); > + char *srcline = strdup(args->leaf_srcline); > + > + if (!srcline) { > + args->err = -ENOMEM; > + if (inline_sym->inlined) > + symbol__delete(inline_sym); > + return DWARF_CB_ABORT; > + } > + if (inline_list__append_tail(inline_sym, srcline, args->node) != 0) { > + free(srcline); > + args->err = -ENOMEM; > + if (inline_sym->inlined) > + symbol__delete(inline_sym); > + return DWARF_CB_ABORT; Looks like the pattern is repeated. My rule is to factor it out when it repeats more than twice. :) > + } > } > return 0; > } > @@ -162,11 +196,29 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, > .leaf_srcline = srcline_from_fileline(src ?: "<unknown>", lineno), > }; > > + if (!args.leaf_srcline) { > + if (file && *file) { > + free(*file); > + *file = NULL; > + } > + return 0; > + } > + > /* Walk from the parent down to the leaf. */ > - cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); > + if (cudie) > + cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); > > if (!args.leaf_srcline_used) > free(args.leaf_srcline); > + > + if (args.err) { > + if (file && *file) { > + free(*file); > + *file = NULL; > + } > + inline_node__clear_frames(node); > + return 0; > + } > } > return 1; > } > diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c > index db164d258163..62884428fb5a 100644 > --- a/tools/perf/util/srcline.c > +++ b/tools/perf/util/srcline.c > @@ -429,10 +429,13 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, > return addr2inlines(dso_name, addr, dso, sym); > } > > -void inline_node__delete(struct inline_node *node) > +void inline_node__clear_frames(struct inline_node *node) > { > struct inline_list *ilist, *tmp; > > + if (node == NULL) > + return; > + > list_for_each_entry_safe(ilist, tmp, &node->val, list) { > list_del_init(&ilist->list); > zfree_srcline(&ilist->srcline); > @@ -441,7 +444,11 @@ void inline_node__delete(struct inline_node *node) > symbol__delete(ilist->symbol); > free(ilist); > } > +} > > +void inline_node__delete(struct inline_node *node) > +{ > + inline_node__clear_frames(node); > free(node); > } > > diff --git a/tools/perf/util/srcline.h b/tools/perf/util/srcline.h > index 7c37b3bf9ce7..1018cbc886d6 100644 > --- a/tools/perf/util/srcline.h > +++ b/tools/perf/util/srcline.h > @@ -47,6 +47,7 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, > struct symbol *sym); > /* free resources associated to the inline node list */ > void inline_node__delete(struct inline_node *node); > +void inline_node__clear_frames(struct inline_node *node); > > /* insert the inline node list into the DSO, which will take ownership */ > void inlines__tree_insert(struct rb_root_cached *tree, > -- > 2.54.0.545.g6539524ca2-goog > ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v4 4/6] perf probe-finder: Fix libdw API contract violations 2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (2 preceding siblings ...) 2026-05-03 17:10 ` [PATCH v4 3/6] perf libdw: " Ian Rogers @ 2026-05-03 17:10 ` Ian Rogers 2026-05-03 23:49 ` Namhyung Kim 2026-05-03 17:10 ` [PATCH v4 5/6] perf annotate-data: " Ian Rogers ` (2 subsequent siblings) 6 siblings, 1 reply; 33+ messages in thread From: Ian Rogers @ 2026-05-03 17:10 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwarf_formsdata`, `dwarf_entrypc`, `dwarf_highpc`, `dwarf_bytesize`, `dwarf_attr`, `dwarf_decl_line`, `dwarf_getfuncs`, and `dwarf_formref_die`. Validate `dwarf_diename` and `dwarf_diecu` results to prevent potential crashes. Fix C90 mixed declarations. Additionally: - Avoid vfprintf undefined behavior with NULL strings by using the `die_name()` helper for `dwarf_diename()` in `pr_*` calls, including when warning about tail calls. - Prevent NULL pointer dereference in `convert_variable_fields()` when processing array elements for variables in registers. - Fallback to offset 0 in `line_range_search_cb()` instead of skipping functions without `DW_AT_decl_line`. - Relax `dwarf_getfuncs` error checking in `find_probe_point_by_func()` and `find_line_range_by_func()` to prevent premature CU search aborts, ensuring robustness against corrupted CUs. Fixes: 66f69b219716 ("perf probe: Support DW_AT_const_value constant value") Fixes: 3d918a12a1b3 ("perf probe: Find fentry mcount fuzzed parameter location") Fixes: bcfc082150c6 ("perf probe: Remove redundant dwarf functions") Fixes: 221d061182b8 ("perf probe: Fix to search local variables in appropriate scope") Fixes: b55a87ade383 ("perf probe: Remove die() from probe-finder code") Fixes: 4c859351226c ("perf probe: Support glob wildcards for function name") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- v4: - Safe DWARF name printing with die_name() to avoid NULL formatting crashes. - Fix NULL dereference in register variable array lookups. - Fix robust CU search loops by continuing on getfuncs errors. --- tools/perf/util/probe-finder.c | 105 +++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 64328abeef8b..7f9761a29c08 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -79,7 +79,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, unsigned int regn; Dwarf_Word offs = 0; bool ref = false; - const char *regs; + const char *regs, *name; int ret, ret2 = 0; if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL) @@ -93,7 +93,8 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (!tvar) return 0; - dwarf_formsdata(&attr, &snum); + if (dwarf_formsdata(&attr, &snum) != 0) + return -ENOENT; ret = asprintf(&tvar->value, "\\%ld", (long)snum); return ret < 0 ? -ENOMEM : 0; @@ -103,8 +104,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) return -EINVAL; /* Broken DIE ? */ if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { - ret = dwarf_entrypc(sp_die, &tmp); - if (ret) + if (dwarf_entrypc(sp_die, &tmp) != 0) return -ENOENT; if (probe_conf.show_location_range && @@ -115,8 +115,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, return -ENOENT; } - ret = dwarf_highpc(sp_die, &tmp); - if (ret) + if (dwarf_highpc(sp_die, &tmp) != 0) return -ENOENT; /* * This is fuzzed by fentry mcount. We try to find the @@ -138,12 +137,16 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, static_var: if (!tvar) return ret2; + /* Static variables on memory (not stack), make @varname */ - ret = strlen(dwarf_diename(vr_die)); + name = dwarf_diename(vr_die); + if (!name) + return -ENOENT; + ret = strlen(name); tvar->value = zalloc(ret + 2); if (tvar->value == NULL) return -ENOMEM; - snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); + snprintf(tvar->value, ret + 2, "@%s", name); tvar->ref = alloc_trace_arg_ref((long)offs); if (tvar->ref == NULL) return -ENOMEM; @@ -234,13 +237,14 @@ static int convert_variable_type(Dwarf_Die *vr_die, } if (die_get_real_type(vr_die, &type) == NULL) { - pr_warning("Failed to get a type information of %s.\n", - dwarf_diename(vr_die)); + const char *name = dwarf_diename(vr_die); + + pr_warning("Failed to get a type information of %s.\n", name ?: "<unknown>"); return -ENOENT; } pr_debug("%s type is %s.\n", - dwarf_diename(vr_die), dwarf_diename(&type)); + die_name(vr_die), die_name(&type)); if (cast && (!strcmp(cast, "string") || !strcmp(cast, "ustring"))) { /* String type */ @@ -249,7 +253,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, ret != DW_TAG_array_type) { pr_warning("Failed to cast into string: " "%s(%s) is not a pointer nor array.\n", - dwarf_diename(vr_die), dwarf_diename(&type)); + die_name(vr_die), die_name(&type)); return -EINVAL; } if (die_get_real_type(&type, &type) == NULL) { @@ -272,7 +276,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, !die_compare_name(&type, "unsigned char")) { pr_warning("Failed to cast into string: " "%s is not (unsigned) char *.\n", - dwarf_diename(vr_die)); + die_name(vr_die)); return -EINVAL; } tvar->type = strdup(cast); @@ -299,7 +303,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, /* Check the bitwidth */ if (ret > MAX_BASIC_TYPE_BITS) { pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n", - dwarf_diename(&type), MAX_BASIC_TYPE_BITS); + die_name(&type), MAX_BASIC_TYPE_BITS); ret = MAX_BASIC_TYPE_BITS; } ret = snprintf(buf, 16, "%c%d", prefix, ret); @@ -333,12 +337,14 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, pr_warning("Failed to get the type of %s.\n", varname); return -ENOENT; } - pr_debug2("Var real type: %s (%x)\n", dwarf_diename(&type), + pr_debug2("Var real type: %s (%x)\n", die_name(&type), (unsigned)dwarf_dieoffset(&type)); tag = dwarf_tag(&type); if (field->name[0] == '[' && (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { + int bsize; + /* Save original type for next field or type */ memcpy(die_mem, &type, sizeof(*die_mem)); /* Get the type of this array */ @@ -346,7 +352,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, pr_warning("Failed to get the type of %s.\n", varname); return -ENOENT; } - pr_debug2("Array real type: %s (%x)\n", dwarf_diename(&type), + pr_debug2("Array real type: %s (%x)\n", die_name(&type), (unsigned)dwarf_dieoffset(&type)); if (tag == DW_TAG_pointer_type) { ref = zalloc(sizeof(struct probe_trace_arg_ref)); @@ -357,7 +363,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, else *ref_ptr = ref; } - ref->offset += dwarf_bytesize(&type) * field->index; + bsize = dwarf_bytesize(&type); + + if (bsize < 0) + return -EINVAL; + if (!ref) { + pr_warning("Array indexing not supported for variables in registers.\n"); + return -ENOTSUP; + } + ref->offset += bsize * field->index; ref->user_access = user_access; goto next; } else if (tag == DW_TAG_pointer_type) { @@ -414,7 +428,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, if (die_find_member(&type, field->name, die_mem) == NULL) { pr_warning("%s(type:%s) has no member %s.\n", varname, - dwarf_diename(&type), field->name); + die_name(&type), field->name); return -EINVAL; } @@ -461,7 +475,7 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) int ret; pr_debug("Converting variable %s into trace event.\n", - dwarf_diename(vr_die)); + die_name(vr_die)); ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, &pf->sp_die, pf, pf->tvar); @@ -542,7 +556,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, /* Verify the address is correct */ if (!dwarf_haspc(sp_die, paddr)) { pr_warning("Specified offset is out of %s\n", - dwarf_diename(sp_die)); + die_name(sp_die)); return -EINVAL; } @@ -599,7 +613,7 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { pr_warning("Ignoring tail call from %s\n", - dwarf_diename(&pf->sp_die)); + die_name(&pf->sp_die)); return 0; } else { pr_warning("Failed to find probe point in any " @@ -611,10 +625,16 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); /* Get the frame base attribute/ops from subprogram */ - dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); - ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); - if (ret <= 0 || nops == 0) { + if (dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr) == NULL) { pf->fb_ops = NULL; + } else { + ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); + if (ret <= 0 || nops == 0) + pf->fb_ops = NULL; + } + + if (pf->fb_ops == NULL) { + /* Not supported */ } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && (pf->cfi_eh != NULL || pf->cfi_dbg != NULL)) { if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 && @@ -667,8 +687,8 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) } } else { /* With the line number, find the nearest declared DIE */ - dwarf_decl_line(fn_die, &lno); - if (lno < fsp->line && fsp->diff > fsp->line - lno) { + if (dwarf_decl_line(fn_die, &lno) == 0 && lno < fsp->line && + fsp->diff > fsp->line - lno) { /* Keep a candidate and continue */ fsp->diff = fsp->line - lno; memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); @@ -924,12 +944,12 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) /* Get probe address */ if (die_entrypc(in_die, &addr) != 0) { pr_warning("Failed to get entry address of %s.\n", - dwarf_diename(in_die)); + die_name(in_die)); return -ENOENT; } if (addr == 0) { pr_debug("%s has no valid entry address. skipped.\n", - dwarf_diename(in_die)); + die_name(in_die)); return -ENOENT; } pf->addr = addr; @@ -971,12 +991,13 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) if (pp->file && fname && strtailcmp(pp->file, fname)) return DWARF_CB_OK; - pr_debug("Matched function: %s [%lx]\n", dwarf_diename(sp_die), + pr_debug("Matched function: %s [%lx]\n", die_name(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); + if (dwarf_decl_line(sp_die, &pf->lno) != 0) + return DWARF_CB_OK; pf->lno += pp->line; param->retval = find_probe_point_by_line(pf); } else if (die_is_func_instance(sp_die)) { @@ -985,7 +1006,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) /* But in some case the entry address is 0 */ if (pf->addr == 0) { pr_debug("%s has no entry PC. Skipped\n", - dwarf_diename(sp_die)); + die_name(sp_die)); param->retval = 0; /* Real function */ } else if (pp->lazy_line) @@ -1018,7 +1039,8 @@ static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; - dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); + if (dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0) < 0) + pr_debug("Failed to get functions from CU\n"); return _param.retval; } @@ -1207,7 +1229,8 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void *data) * points to correct die. */ if (dwarf_attr(die_mem, DW_AT_abstract_origin, &attr)) { - dwarf_formref_die(&attr, &var_die); + if (dwarf_formref_die(&attr, &var_die) == NULL) + goto out; if (pf->abstrace_dieoffset != dwarf_dieoffset(&var_die)) goto out; } @@ -1270,6 +1293,8 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) struct probe_trace_event *tev; struct perf_probe_arg *args = NULL; int ret, i; + const char *realname; + Dwarf_Die cu_die_mem; /* * For some reason (e.g. different column assigned to same address) @@ -1293,13 +1318,17 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) if (ret < 0) goto end; - tev->point.realname = strdup(dwarf_diename(sc_die)); + realname = dwarf_diename(sc_die); + tev->point.realname = strdup(realname ?: "unknown"); if (!tev->point.realname) { ret = -ENOMEM; goto end; } - tev->lang = dwarf_srclang(dwarf_diecu(sc_die, &pf->cu_die, NULL, NULL)); + if (dwarf_diecu(sc_die, &cu_die_mem, NULL, NULL) != NULL) + tev->lang = dwarf_srclang(&cu_die_mem); + else + tev->lang = DW_LANG_C; // Fallback pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); @@ -1794,7 +1823,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) if (die_match_name(sp_die, lr->function) && die_is_func_def(sp_die)) { lf->fname = die_get_decl_file(sp_die); - dwarf_decl_line(sp_die, &lr->offset); + if (dwarf_decl_line(sp_die, &lr->offset) != 0) + lr->offset = 0; // Fallback if no line info pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; if (lf->lno_s < 0) /* Overflow */ @@ -1818,7 +1848,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; - dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); + if (dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0) < 0) + pr_debug("Failed to get functions from CU\n"); return param.retval; } -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v4 4/6] perf probe-finder: Fix libdw API contract violations 2026-05-03 17:10 ` [PATCH v4 4/6] perf probe-finder: " Ian Rogers @ 2026-05-03 23:49 ` Namhyung Kim 0 siblings, 0 replies; 33+ messages in thread From: Namhyung Kim @ 2026-05-03 23:49 UTC (permalink / raw) To: Ian Rogers Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel On Sun, May 03, 2026 at 10:10:30AM -0700, Ian Rogers wrote: > Check return values of `dwarf_formsdata`, `dwarf_entrypc`, > `dwarf_highpc`, `dwarf_bytesize`, `dwarf_attr`, `dwarf_decl_line`, > `dwarf_getfuncs`, and `dwarf_formref_die`. Validate `dwarf_diename` and > `dwarf_diecu` results to prevent potential crashes. Fix C90 mixed > declarations. > > Additionally: > - Avoid vfprintf undefined behavior with NULL strings by using the > `die_name()` helper for `dwarf_diename()` in `pr_*` calls, > including when warning about tail calls. > - Prevent NULL pointer dereference in `convert_variable_fields()` > when processing array elements for variables in registers. > - Fallback to offset 0 in `line_range_search_cb()` instead of > skipping functions without `DW_AT_decl_line`. > - Relax `dwarf_getfuncs` error checking in > `find_probe_point_by_func()` and `find_line_range_by_func()` to > prevent premature CU search aborts, ensuring robustness against > corrupted CUs. > > Fixes: 66f69b219716 ("perf probe: Support DW_AT_const_value constant value") > Fixes: 3d918a12a1b3 ("perf probe: Find fentry mcount fuzzed parameter location") > Fixes: bcfc082150c6 ("perf probe: Remove redundant dwarf functions") > Fixes: 221d061182b8 ("perf probe: Fix to search local variables in appropriate scope") > Fixes: b55a87ade383 ("perf probe: Remove die() from probe-finder code") > Fixes: 4c859351226c ("perf probe: Support glob wildcards for function name") > Assisted-by: Gemini-CLI:Google Gemini 3 > Signed-off-by: Ian Rogers <irogers@google.com> > --- [SNIP] > @@ -1293,13 +1318,17 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) > if (ret < 0) > goto end; > > - tev->point.realname = strdup(dwarf_diename(sc_die)); > + realname = dwarf_diename(sc_die); > + tev->point.realname = strdup(realname ?: "unknown"); Could be: tev->point.realname = strdup(die_name(sc_die)); > if (!tev->point.realname) { > ret = -ENOMEM; > goto end; > } > > - tev->lang = dwarf_srclang(dwarf_diecu(sc_die, &pf->cu_die, NULL, NULL)); > + if (dwarf_diecu(sc_die, &cu_die_mem, NULL, NULL) != NULL) I think it should fill pf->cu_die. Thanks, Namhyung > + tev->lang = dwarf_srclang(&cu_die_mem); > + else > + tev->lang = DW_LANG_C; // Fallback > > pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, > tev->point.offset); > @@ -1794,7 +1823,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) > > if (die_match_name(sp_die, lr->function) && die_is_func_def(sp_die)) { > lf->fname = die_get_decl_file(sp_die); > - dwarf_decl_line(sp_die, &lr->offset); > + if (dwarf_decl_line(sp_die, &lr->offset) != 0) > + lr->offset = 0; // Fallback if no line info > pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); > lf->lno_s = lr->offset + lr->start; > if (lf->lno_s < 0) /* Overflow */ > @@ -1818,7 +1848,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) > static int find_line_range_by_func(struct line_finder *lf) > { > struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; > - dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); > + if (dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0) < 0) > + pr_debug("Failed to get functions from CU\n"); > return param.retval; > } > > -- > 2.54.0.545.g6539524ca2-goog > ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v4 5/6] perf annotate-data: Fix libdw API contract violations 2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (3 preceding siblings ...) 2026-05-03 17:10 ` [PATCH v4 4/6] perf probe-finder: " Ian Rogers @ 2026-05-03 17:10 ` Ian Rogers 2026-05-03 23:53 ` Namhyung Kim 2026-05-03 17:10 ` [PATCH v4 6/6] perf debuginfo: " Ian Rogers 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 6 siblings, 1 reply; 33+ messages in thread From: Ian Rogers @ 2026-05-03 17:10 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwarf_aggregate_size` and `dwarf_formudata`. Additionally: - Avoid `vfprintf` undefined behavior with `NULL` strings by using the `die_name()` helper for `dwarf_diename()` in `pr_*` calls. - Use `die_get_data_member_location()` (updated to use `dwarf_attr_integrate`) to correctly parse location expressions for inherited member locations in the fallback path when `dwarf_formudata()` fails. Fixes: 2bc3cf575a16 ("perf annotate-data: Improve debug message with location info") Fixes: 4a111cadac85 ("perf annotate-data: Add member field in the data type") Fixes: 8b1042c425f6 ("perf annotate-data: Set bitfield member offset and size properly") Fixes: fc044c53b99f ("perf annotate-data: Add dso->data_types tree") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- v4: - Safe DWARF name printing in annotate-data. - Fix fallback location expression parsing for inherited data members. --- tools/perf/util/annotate-data.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 1eff0a27237d..2eacbba51f32 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -74,7 +74,8 @@ void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) break; } - dwarf_aggregate_size(die, &size); + if (dwarf_aggregate_size(die, &size) != 0) + size = 0; strbuf_init(&sb, 32); die_get_typename_from_type(die, &sb); @@ -146,9 +147,9 @@ static void pr_debug_scope(Dwarf_Die *scope_die) tag = dwarf_tag(scope_die); if (tag == DW_TAG_subprogram) - pr_info("[function] %s\n", dwarf_diename(scope_die)); + pr_info("[function] %s\n", die_name(scope_die)); else if (tag == DW_TAG_inlined_subroutine) - pr_info("[inlined] %s\n", dwarf_diename(scope_die)); + pr_info("[inlined] %s\n", die_name(scope_die)); else if (tag == DW_TAG_lexical_block) pr_info("[block]\n"); else @@ -250,9 +251,12 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) if (dwarf_aggregate_size(&die_mem, &size) < 0) size = 0; - if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) - dwarf_formudata(&attr, &loc); - else { + if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) { + if (dwarf_formudata(&attr, &loc) != 0) { + if (die_get_data_member_location(die, &loc) != 0) + loc = 0; + } + } else { /* bitfield member */ if (dwarf_attr_integrate(die, DW_AT_data_bit_offset, &attr) && dwarf_formudata(&attr, &loc) == 0) @@ -273,7 +277,9 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) dwarf_diename(die), (long)bit_size) < 0) member->var_name = NULL; } else { - member->var_name = strdup(dwarf_diename(die)); + const char *name = dwarf_diename(die); + + member->var_name = strdup(name); } if (member->var_name == NULL) { @@ -370,7 +376,8 @@ static struct annotated_data_type *dso__findnew_data_type(struct dso *dso, if (dwarf_tag(type_die) == DW_TAG_typedef) die_get_real_type(type_die, type_die); - dwarf_aggregate_size(type_die, &size); + if (dwarf_aggregate_size(type_die, &size) != 0) + size = 0; /* Check existing nodes in dso->data_types tree */ key.self.type_name = type_name; @@ -1569,7 +1576,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) offset = loc->offset; pr_debug_dtp("CU for %s (die:%#lx)\n", - dwarf_diename(&cu_die), (long)dwarf_dieoffset(&cu_die)); + die_name(&cu_die), (long)dwarf_dieoffset(&cu_die)); if (reg == DWARF_REG_PC) { if (get_global_var_type(&cu_die, dloc, dloc->ip, dloc->var_addr, @@ -1636,7 +1643,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) } pr_debug_dtp("found \"%s\" (die: %#lx) in scope=%d/%d (die: %#lx) ", - dwarf_diename(&var_die), (long)dwarf_dieoffset(&var_die), + die_name(&var_die), (long)dwarf_dieoffset(&var_die), i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i])); if (reg == DWARF_REG_PC) { -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v4 5/6] perf annotate-data: Fix libdw API contract violations 2026-05-03 17:10 ` [PATCH v4 5/6] perf annotate-data: " Ian Rogers @ 2026-05-03 23:53 ` Namhyung Kim 0 siblings, 0 replies; 33+ messages in thread From: Namhyung Kim @ 2026-05-03 23:53 UTC (permalink / raw) To: Ian Rogers Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel On Sun, May 03, 2026 at 10:10:31AM -0700, Ian Rogers wrote: > Check return values of `dwarf_aggregate_size` and `dwarf_formudata`. > > Additionally: > - Avoid `vfprintf` undefined behavior with `NULL` strings by using > the `die_name()` helper for `dwarf_diename()` in `pr_*` calls. > - Use `die_get_data_member_location()` (updated to use > `dwarf_attr_integrate`) to correctly parse location expressions > for inherited member locations in the fallback path when > `dwarf_formudata()` fails. > > Fixes: 2bc3cf575a16 ("perf annotate-data: Improve debug message with location info") > Fixes: 4a111cadac85 ("perf annotate-data: Add member field in the data type") > Fixes: 8b1042c425f6 ("perf annotate-data: Set bitfield member offset and size properly") > Fixes: fc044c53b99f ("perf annotate-data: Add dso->data_types tree") > Assisted-by: Gemini-CLI:Google Gemini 3 > Signed-off-by: Ian Rogers <irogers@google.com> > --- > v4: > - Safe DWARF name printing in annotate-data. > - Fix fallback location expression parsing for inherited data members. > --- > tools/perf/util/annotate-data.c | 27 +++++++++++++++++---------- > 1 file changed, 17 insertions(+), 10 deletions(-) > > diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c > index 1eff0a27237d..2eacbba51f32 100644 > --- a/tools/perf/util/annotate-data.c > +++ b/tools/perf/util/annotate-data.c > @@ -74,7 +74,8 @@ void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) > break; > } > > - dwarf_aggregate_size(die, &size); > + if (dwarf_aggregate_size(die, &size) != 0) > + size = 0; > > strbuf_init(&sb, 32); > die_get_typename_from_type(die, &sb); > @@ -146,9 +147,9 @@ static void pr_debug_scope(Dwarf_Die *scope_die) > > tag = dwarf_tag(scope_die); > if (tag == DW_TAG_subprogram) > - pr_info("[function] %s\n", dwarf_diename(scope_die)); > + pr_info("[function] %s\n", die_name(scope_die)); > else if (tag == DW_TAG_inlined_subroutine) > - pr_info("[inlined] %s\n", dwarf_diename(scope_die)); > + pr_info("[inlined] %s\n", die_name(scope_die)); > else if (tag == DW_TAG_lexical_block) > pr_info("[block]\n"); > else > @@ -250,9 +251,12 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) > if (dwarf_aggregate_size(&die_mem, &size) < 0) > size = 0; > > - if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) > - dwarf_formudata(&attr, &loc); > - else { > + if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) { > + if (dwarf_formudata(&attr, &loc) != 0) { > + if (die_get_data_member_location(die, &loc) != 0) > + loc = 0; > + } > + } else { > /* bitfield member */ > if (dwarf_attr_integrate(die, DW_AT_data_bit_offset, &attr) && > dwarf_formudata(&attr, &loc) == 0) > @@ -273,7 +277,9 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) > dwarf_diename(die), (long)bit_size) < 0) > member->var_name = NULL; > } else { > - member->var_name = strdup(dwarf_diename(die)); > + const char *name = dwarf_diename(die); > + > + member->var_name = strdup(name); This can be: member->var_name = name ? strdup(name) : NULL; Thanks, Namhyung > } > > if (member->var_name == NULL) { > @@ -370,7 +376,8 @@ static struct annotated_data_type *dso__findnew_data_type(struct dso *dso, > if (dwarf_tag(type_die) == DW_TAG_typedef) > die_get_real_type(type_die, type_die); > > - dwarf_aggregate_size(type_die, &size); > + if (dwarf_aggregate_size(type_die, &size) != 0) > + size = 0; > > /* Check existing nodes in dso->data_types tree */ > key.self.type_name = type_name; > @@ -1569,7 +1576,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) > offset = loc->offset; > > pr_debug_dtp("CU for %s (die:%#lx)\n", > - dwarf_diename(&cu_die), (long)dwarf_dieoffset(&cu_die)); > + die_name(&cu_die), (long)dwarf_dieoffset(&cu_die)); > > if (reg == DWARF_REG_PC) { > if (get_global_var_type(&cu_die, dloc, dloc->ip, dloc->var_addr, > @@ -1636,7 +1643,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) > } > > pr_debug_dtp("found \"%s\" (die: %#lx) in scope=%d/%d (die: %#lx) ", > - dwarf_diename(&var_die), (long)dwarf_dieoffset(&var_die), > + die_name(&var_die), (long)dwarf_dieoffset(&var_die), > i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i])); > > if (reg == DWARF_REG_PC) { > -- > 2.54.0.545.g6539524ca2-goog > ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v4 6/6] perf debuginfo: Fix libdw API contract violations 2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (4 preceding siblings ...) 2026-05-03 17:10 ` [PATCH v4 5/6] perf annotate-data: " Ian Rogers @ 2026-05-03 17:10 ` Ian Rogers 2026-05-03 23:54 ` Namhyung Kim 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 6 siblings, 1 reply; 33+ messages in thread From: Ian Rogers @ 2026-05-03 17:10 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return value of `dwfl_report_end` during offline initialization. Validate `dwfl_module_relocation_info` result before passing to `strcmp` to avoid potential segmentation faults. Additionally: - Fix a file descriptor leak in `debuginfo__init_offline_dwarf()` when `dwfl_report_offline()` or subsequent setup calls fail. Fixes: 6f1b6291cf73 ("perf tools: Add util/debuginfo.[ch] files") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- v4: - Fix file descriptor leaks in debuginfo init paths. --- tools/perf/util/debuginfo.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c index 0e35c13abd04..84a78b30ceac 100644 --- a/tools/perf/util/debuginfo.c +++ b/tools/perf/util/debuginfo.c @@ -42,6 +42,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, { GElf_Addr dummy; int fd; + bool fd_consumed = false; fd = open(path, O_RDONLY); if (fd < 0) @@ -55,6 +56,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); if (!dbg->mod) goto error; + fd_consumed = true; dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); if (!dbg->dbg) @@ -62,13 +64,14 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); - dwfl_report_end(dbg->dwfl, NULL, NULL); + if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0) + goto error; return 0; error: if (dbg->dwfl) dwfl_end(dbg->dwfl); - else + if (!fd_consumed) close(fd); memset(dbg, 0, sizeof(*dbg)); @@ -167,7 +170,7 @@ int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, /* Search the relocation related .text section */ for (i = 0; i < n; i++) { p = dwfl_module_relocation_info(dbg->mod, i, &shndx); - if (strcmp(p, ".text") == 0) { + if (p && strcmp(p, ".text") == 0) { /* OK, get the section header */ scn = elf_getscn(elf, shndx); if (!scn) -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v4 6/6] perf debuginfo: Fix libdw API contract violations 2026-05-03 17:10 ` [PATCH v4 6/6] perf debuginfo: " Ian Rogers @ 2026-05-03 23:54 ` Namhyung Kim 0 siblings, 0 replies; 33+ messages in thread From: Namhyung Kim @ 2026-05-03 23:54 UTC (permalink / raw) To: Ian Rogers Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel On Sun, May 03, 2026 at 10:10:32AM -0700, Ian Rogers wrote: > Check return value of `dwfl_report_end` during offline initialization. > Validate `dwfl_module_relocation_info` result before passing to `strcmp` > to avoid potential segmentation faults. > > Additionally: > - Fix a file descriptor leak in `debuginfo__init_offline_dwarf()` when > `dwfl_report_offline()` or subsequent setup calls fail. > > Fixes: 6f1b6291cf73 ("perf tools: Add util/debuginfo.[ch] files") > Assisted-by: Gemini-CLI:Google Gemini 3 > Signed-off-by: Ian Rogers <irogers@google.com> Acked-by: Namhyung Kim <namhyung@kernel.org> Thanks, Namhyung > --- > v4: > - Fix file descriptor leaks in debuginfo init paths. > --- > tools/perf/util/debuginfo.c | 9 ++++++--- > 1 file changed, 6 insertions(+), 3 deletions(-) > > diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c > index 0e35c13abd04..84a78b30ceac 100644 > --- a/tools/perf/util/debuginfo.c > +++ b/tools/perf/util/debuginfo.c > @@ -42,6 +42,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, > { > GElf_Addr dummy; > int fd; > + bool fd_consumed = false; > > fd = open(path, O_RDONLY); > if (fd < 0) > @@ -55,6 +56,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, > dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); > if (!dbg->mod) > goto error; > + fd_consumed = true; > > dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); > if (!dbg->dbg) > @@ -62,13 +64,14 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, > > dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); > > - dwfl_report_end(dbg->dwfl, NULL, NULL); > + if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0) > + goto error; > > return 0; > error: > if (dbg->dwfl) > dwfl_end(dbg->dwfl); > - else > + if (!fd_consumed) > close(fd); > memset(dbg, 0, sizeof(*dbg)); > > @@ -167,7 +170,7 @@ int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, > /* Search the relocation related .text section */ > for (i = 0; i < n; i++) { > p = dwfl_module_relocation_info(dbg->mod, i, &shndx); > - if (strcmp(p, ".text") == 0) { > + if (p && strcmp(p, ".text") == 0) { > /* OK, get the section header */ > scn = elf_getscn(elf, shndx); > if (!scn) > -- > 2.54.0.545.g6539524ca2-goog > ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes 2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (5 preceding siblings ...) 2026-05-03 17:10 ` [PATCH v4 6/6] perf debuginfo: " Ian Rogers @ 2026-05-04 8:12 ` Ian Rogers 2026-05-04 8:12 ` [PATCH v5 1/9] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers ` (10 more replies) 6 siblings, 11 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-04 8:12 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers This patch series addresses a number of DWARF/libdw error-handling bugs and contract violations, preventing several real Userspace segmentation faults and memory/FD leaks. In v5, the series has been extensively restructured and polished based on comprehensive review feedback on v4, focusing on history granularity, bisectability, and defensive styling: - **Commit Splitting for Review Granularity**: Split the previously large libdw contract fix patch into 4 granular commits to cleanly isolate independent improvements: introducing clear_frames refactoring, fixing ORDER_CALLER parent update corruption, adding line 0 support, and consolidating core contract/leak cleanups. - **Bisectability & Correctness Hardening**: - Fixed an unused variable compilation failure (-Werror) in the split history to guarantee perfect git bisectability. - Fixed a line 0 fallback regression to ensure that if an optional call line is missing but the call file is valid, we fallback to line 0 to preserve the filename rather than discarding the caller info entirely. - **Style & Robustness Polish**: - Standardized all newly introduced C++ style (//) comments to preferred C style (/* ... */) comments. - Implemented explicit safe string duplication style fix in annotate-data. - Corrected CU DIE propagation context inside probe-finder. - Enhanced the Patch 2 commit message to explicitly detail the removal of strict optional attribute aborts (decf) to clarify review queries. - **Tags Collected**: Integrated Acked-by tags from Namhyung Kim for Patch 1 and Patch 9. v5: - Restructured series from 6 to 9 patches by splitting the libdw commit. - Fixed compilation bisectability and DWARF line 0 filename preservation regressions. - Standardized comment styles and applied safe duplication formatting fixes. - Updated commit messages with detailed optional attribute justifications. v4: - Localized and squashed robust error handling, memory/FD cleanup paths, and safe DWARF printing. https://lore.kernel.org/linux-perf-users/20260503171032.1559338-1-irogers@google.com/ v3: - Minor formatting fixes. https://lore.kernel.org/linux-perf-users/20260503003552.1063540-1-irogers@google.com/ v2: https://lore.kernel.org/lkml/20260502155656.478642-1-irogers@google.com/ v1: https://lore.kernel.org/linux-perf-users/20260502064839.282422-1-irogers@google.com/ Ian Rogers (9): perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at perf dwarf-aux: Fix libdw API contract violations perf srcline: Introduce inline_node__clear_frames() perf libdw: Fix callchain parent update in ORDER_CALLER mode perf libdw: Support DWARF line 0 in inline list perf libdw: Fix libdw API contract violations and memory leaks perf probe-finder: Fix libdw API contract violations perf annotate-data: Fix libdw API contract violations perf debuginfo: Fix libdw API contract violations tools/perf/util/annotate-data.c | 27 +++++---- tools/perf/util/debuginfo.c | 9 ++- tools/perf/util/dwarf-aux.c | 39 ++++++------ tools/perf/util/dwarf-aux.h | 5 ++ tools/perf/util/libdw.c | 72 +++++++++++++++++----- tools/perf/util/probe-finder.c | 102 ++++++++++++++++++++------------ tools/perf/util/srcline.c | 9 ++- tools/perf/util/srcline.h | 1 + 8 files changed, 179 insertions(+), 85 deletions(-) -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v5 1/9] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers @ 2026-05-04 8:12 ` Ian Rogers 2026-05-04 8:12 ` [PATCH v5 2/9] perf dwarf-aux: Fix libdw API contract violations Ian Rogers ` (9 subsequent siblings) 10 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-04 8:12 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers 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 Acked-by: Namhyung Kim <namhyung@kernel.org> Signed-off-by: Ian Rogers <irogers@google.com> --- v5: - Added Acked-by tag. --- tools/perf/util/dwarf-aux.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 92db2fccc788..109a166a6d19 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -171,7 +171,6 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, } return ret; - } /** @@ -620,7 +619,7 @@ Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 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; @@ -659,7 +658,7 @@ Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 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; -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v5 2/9] perf dwarf-aux: Fix libdw API contract violations 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 2026-05-04 8:12 ` [PATCH v5 1/9] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers @ 2026-05-04 8:12 ` Ian Rogers 2026-05-04 8:12 ` [PATCH v5 3/9] perf srcline: Introduce inline_node__clear_frames() Ian Rogers ` (8 subsequent siblings) 10 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-04 8:12 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwarf_decl_line` (where non-optional), `dwarf_getfuncs`, and `dwarf_lineaddr` to prevent using uninitialized stack variables or incorrectly reporting success on failure. For the root DIE in `die_walk_lines()`, `dwarf_decl_line` and `die_get_decl_file` are optional and their failures are handled gracefully to avoid breaking line walking on valid functions. Specifically, remove the strict `!decf` (declared file) check that would prematurely abort line walking on generated or artificial functions lacking this optional attribute. Additionally: - Add NULL pointer protection for `strcmp()` in `die_walk_lines()` when `inf` or `decf` are NULL to prevent crashes on generated code. - Use `dwarf_attr_integrate` in `die_get_data_member_location` to correctly resolve inherited member locations (e.g. via abstract origin or specification). Fixes: 57f95bf5f882 ("perf probe: Show correct statement line number by perf probe -l") Fixes: 3f4460a28fb2 ("perf probe: Filter out redundant inline-instances") Fixes: 75186a9b09e4 ("perf probe: Fix to show lines of sys_ functions correctly") Fixes: e0d153c69040 ("perf-probe: Move dwarf library routines to dwarf-aux.{c, h}") Fixes: 6243b9dc4c99 ("perf probe: Move dwarf specific functions to dwarf-aux.c") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- v5: - Updated commit message to explain optional DWARF attributes and decf check removal. v4: - Fix strcmp(NULL) risk and inherited member location fallbacks in dwarf-aux.c. --- tools/perf/util/dwarf-aux.c | 34 +++++++++++++++++----------------- tools/perf/util/dwarf-aux.h | 5 +++++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 109a166a6d19..d7160f87ac7d 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -125,7 +125,8 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, Dwarf_Addr addr, && die_entrypc(&die_mem, &faddr) == 0 && faddr == addr) { *fname = die_get_decl_file(&die_mem); - dwarf_decl_line(&die_mem, lineno); + if (dwarf_decl_line(&die_mem, lineno) != 0) + return -ENOENT; goto out; } @@ -459,7 +460,7 @@ int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) size_t nexpr; int ret; - if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) + if (dwarf_attr_integrate(mb_die, DW_AT_data_member_location, &attr) == NULL) return -ENOENT; if (dwarf_formudata(&attr, offs) != 0) { @@ -795,8 +796,7 @@ static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) /* Ignore redundant instances */ if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { - dwarf_decl_line(origin, &tmp); - if (die_get_call_lineno(inst) == tmp) { + if (dwarf_decl_line(origin, &tmp) == 0 && die_get_call_lineno(inst) == tmp) { tmp = die_get_decl_fileno(origin); if (die_get_call_fileno(inst) == tmp) return DIE_FIND_CB_CONTINUE; @@ -950,11 +950,6 @@ 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 = die_get_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) { @@ -998,11 +993,12 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) if (die_find_inlinefunc(rt_die, addr, &die_mem)) { /* Call-site check */ inf = die_get_call_file(&die_mem); - if ((inf && !strcmp(inf, decf)) && + if ((inf == decf || (inf && decf && !strcmp(inf, decf))) && die_get_call_lineno(&die_mem) == lineno) goto found; - dwarf_decl_line(&die_mem, &inl); + if (dwarf_decl_line(&die_mem, &inl) != 0) + inl = 0; if (inl != decl || decf != die_get_decl_file(&die_mem)) continue; @@ -1034,8 +1030,10 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) .data = data, .retval = 0, }; - dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); - ret = param.retval; + if (dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0) < 0) + ret = -EINVAL; + else + ret = param.retval; } return ret; @@ -1939,10 +1937,12 @@ static bool die_get_postprologue_addr(unsigned long entrypc_idx, break; } - dwarf_lineaddr(line, postprologue_addr); - if (*postprologue_addr >= highpc) - dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), - postprologue_addr); + if (dwarf_lineaddr(line, postprologue_addr) != 0) + return false; + if (*postprologue_addr >= highpc) { + if (dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), postprologue_addr) != 0) + return false; + } return true; } diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index a79968a2e573..161f0bf980b6 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -10,6 +10,11 @@ #include <elfutils/libdwfl.h> #include <elfutils/version.h> +static inline const char *die_name(Dwarf_Die *die) +{ + return dwarf_diename(die) ?: "<unknown>"; +} + struct strbuf; /* Find the realpath of the target file */ -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v5 3/9] perf srcline: Introduce inline_node__clear_frames() 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 2026-05-04 8:12 ` [PATCH v5 1/9] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers 2026-05-04 8:12 ` [PATCH v5 2/9] perf dwarf-aux: Fix libdw API contract violations Ian Rogers @ 2026-05-04 8:12 ` Ian Rogers 2026-05-04 8:12 ` [PATCH v5 4/9] perf libdw: Fix callchain parent update in ORDER_CALLER mode Ian Rogers ` (7 subsequent siblings) 10 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-04 8:12 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Introduce inline_node__clear_frames() to clean up partial allocations. This is a prerequisite for error handling in libdw inline unwinding. Signed-off-by: Ian Rogers <irogers@google.com> --- v5: - Split from original Patch 3/6 into its own commit. --- tools/perf/util/srcline.c | 9 ++++++++- tools/perf/util/srcline.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index db164d258163..62884428fb5a 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -429,10 +429,13 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, return addr2inlines(dso_name, addr, dso, sym); } -void inline_node__delete(struct inline_node *node) +void inline_node__clear_frames(struct inline_node *node) { struct inline_list *ilist, *tmp; + if (node == NULL) + return; + list_for_each_entry_safe(ilist, tmp, &node->val, list) { list_del_init(&ilist->list); zfree_srcline(&ilist->srcline); @@ -441,7 +444,11 @@ void inline_node__delete(struct inline_node *node) symbol__delete(ilist->symbol); free(ilist); } +} +void inline_node__delete(struct inline_node *node) +{ + inline_node__clear_frames(node); free(node); } diff --git a/tools/perf/util/srcline.h b/tools/perf/util/srcline.h index 7c37b3bf9ce7..1018cbc886d6 100644 --- a/tools/perf/util/srcline.h +++ b/tools/perf/util/srcline.h @@ -47,6 +47,7 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, struct symbol *sym); /* free resources associated to the inline node list */ void inline_node__delete(struct inline_node *node); +void inline_node__clear_frames(struct inline_node *node); /* insert the inline node list into the DSO, which will take ownership */ void inlines__tree_insert(struct rb_root_cached *tree, -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v5 4/9] perf libdw: Fix callchain parent update in ORDER_CALLER mode 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (2 preceding siblings ...) 2026-05-04 8:12 ` [PATCH v5 3/9] perf srcline: Introduce inline_node__clear_frames() Ian Rogers @ 2026-05-04 8:12 ` Ian Rogers 2026-05-04 8:12 ` [PATCH v5 5/9] perf libdw: Support DWARF line 0 in inline list Ian Rogers ` (6 subsequent siblings) 10 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-04 8:12 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Fix the parent srcline lookup in `libdw_a2l_cb()` to target the correct parent node depending on the callchain order (ORDER_CALLER/ORDER_CALLEE). This ensures inline callchains are not corrupted when nest depth > 2. Signed-off-by: Ian Rogers <irogers@google.com> --- v5: - Split from original Patch 3/6 into its own commit. - Fixed bisectability failure by removing unused `ilist` variable declaration. --- tools/perf/util/libdw.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 216977884103..301642084c69 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -4,6 +4,7 @@ #include "srcline.h" #include "symbol.h" #include "dwarf-aux.h" +#include "callchain.h" #include <fcntl.h> #include <unistd.h> #include <elfutils/libdwfl.h> @@ -80,7 +81,6 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); const char *call_fname = die_get_call_file(die); char *call_srcline = srcline__unknown; - struct inline_list *ilist; if (!inline_sym) return -ENOMEM; @@ -89,14 +89,20 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) if (call_fname) call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die)); - list_for_each_entry(ilist, &args->node->val, list) { - if (args->leaf_srcline == ilist->srcline) + if (!list_empty(&args->node->val)) { + struct inline_list *parent; + + if (callchain_param.order == ORDER_CALLEE) + parent = list_first_entry(&args->node->val, struct inline_list, list); + else + parent = list_last_entry(&args->node->val, struct inline_list, list); + + if (args->leaf_srcline == parent->srcline) args->leaf_srcline_used = false; - else if (ilist->srcline != srcline__unknown) - free(ilist->srcline); - ilist->srcline = call_srcline; + else if (parent->srcline != srcline__unknown) + free(parent->srcline); + parent->srcline = call_srcline; call_srcline = NULL; - break; } if (call_srcline && call_srcline != srcline__unknown) free(call_srcline); -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v5 5/9] perf libdw: Support DWARF line 0 in inline list 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (3 preceding siblings ...) 2026-05-04 8:12 ` [PATCH v5 4/9] perf libdw: Fix callchain parent update in ORDER_CALLER mode Ian Rogers @ 2026-05-04 8:12 ` Ian Rogers 2026-05-04 8:12 ` [PATCH v5 6/9] perf libdw: Fix libdw API contract violations and memory leaks Ian Rogers ` (5 subsequent siblings) 10 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-04 8:12 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Allow DWARF line 0 in `libdw_a2l_cb()`, as it is a valid reference for compiler-generated code. Filter `die_get_call_lineno` error codes (negative values), but fallback to line 0 if `call_fname` is present to preserve the caller's filename instead of discarding it entirely. Signed-off-by: Ian Rogers <irogers@google.com> --- v5: - Split from original Patch 3/6 into its own commit. - Fixed fallback logic for missing call lines to preserve filename by defaulting to line 0. --- tools/perf/util/libdw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 301642084c69..196b9cdf51b2 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -80,6 +80,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) struct libdw_a2l_cb_args *args = _args; struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); const char *call_fname = die_get_call_file(die); + int call_lineno = die_get_call_lineno(die); char *call_srcline = srcline__unknown; if (!inline_sym) @@ -87,7 +88,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) /* Assign caller information to the parent. */ if (call_fname) - call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die)); + call_srcline = srcline_from_fileline(call_fname, call_lineno >= 0 ? call_lineno : 0); if (!list_empty(&args->node->val)) { struct inline_list *parent; -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v5 6/9] perf libdw: Fix libdw API contract violations and memory leaks 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (4 preceding siblings ...) 2026-05-04 8:12 ` [PATCH v5 5/9] perf libdw: Support DWARF line 0 in inline list Ian Rogers @ 2026-05-04 8:12 ` Ian Rogers 2026-05-04 8:12 ` [PATCH v5 7/9] perf probe-finder: Fix libdw API contract violations Ian Rogers ` (4 subsequent siblings) 10 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-04 8:12 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwfl_report_end` and `dwfl_module_addrdie` to prevent using uninitialized stack variables or reporting success on failure. Additionally: - Ensure `*file` is freed and inline frames are cleared on error in `libdw__addr2line()` to prevent memory leaks and duplicated callchains when falling back to other unwinders. - Use `die_name()` safe wrapper inside the inline function unwinding callback (`libdw_a2l_cb`). - Refactor `libdw_a2l_cb`'s repeated memory error handling/cleanup paths using a cleaner goto control flow. Fixes: b7a2b011e962 ("perf powerpc: Unify the skip-callchain-idx libdw with that for addr2line") Fixes: 88c51002d06f ("perf addr2line: Add a libdw implementation") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- v5: - Split from original Patch 3/6. Refactored libdw_a2l_cb error handling with goto. --- tools/perf/util/libdw.c | 49 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 196b9cdf51b2..84713b2a7ad5 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -61,7 +61,10 @@ struct Dwfl *dso__libdw_dwfl(struct dso *dso) return NULL; } - dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL); + if (dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL) != 0) { + dwfl_end(dwfl); + return NULL; + } dso__set_libdw(dso, dwfl); return dwfl; @@ -73,18 +76,19 @@ struct libdw_a2l_cb_args { struct inline_node *node; char *leaf_srcline; bool leaf_srcline_used; + int err; }; static int libdw_a2l_cb(Dwarf_Die *die, void *_args) { struct libdw_a2l_cb_args *args = _args; - struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); + struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, die_name(die)); const char *call_fname = die_get_call_file(die); int call_lineno = die_get_call_lineno(die); char *call_srcline = srcline__unknown; if (!inline_sym) - return -ENOMEM; + goto abort_enomem; /* Assign caller information to the parent. */ if (call_fname) @@ -110,12 +114,27 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) /* Add this symbol to the chain as the leaf. */ if (!args->leaf_srcline_used) { - inline_list__append_tail(inline_sym, args->leaf_srcline, args->node); + if (inline_list__append_tail(inline_sym, args->leaf_srcline, args->node) != 0) + goto abort_delete_sym; args->leaf_srcline_used = true; } else { - inline_list__append_tail(inline_sym, strdup(args->leaf_srcline), args->node); + char *srcline = strdup(args->leaf_srcline); + + if (!srcline) + goto abort_delete_sym; + if (inline_list__append_tail(inline_sym, srcline, args->node) != 0) { + free(srcline); + goto abort_delete_sym; + } } return 0; + +abort_delete_sym: + if (inline_sym->inlined) + symbol__delete(inline_sym); +abort_enomem: + args->err = -ENOMEM; + return DWARF_CB_ABORT; } int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, @@ -169,11 +188,29 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, .leaf_srcline = srcline_from_fileline(src ?: "<unknown>", lineno), }; + if (!args.leaf_srcline) { + if (file && *file) { + free(*file); + *file = NULL; + } + return 0; + } + /* Walk from the parent down to the leaf. */ - cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); + if (cudie) + cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); if (!args.leaf_srcline_used) free(args.leaf_srcline); + + if (args.err) { + if (file && *file) { + free(*file); + *file = NULL; + } + inline_node__clear_frames(node); + return 0; + } } return 1; } -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v5 7/9] perf probe-finder: Fix libdw API contract violations 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (5 preceding siblings ...) 2026-05-04 8:12 ` [PATCH v5 6/9] perf libdw: Fix libdw API contract violations and memory leaks Ian Rogers @ 2026-05-04 8:12 ` Ian Rogers 2026-05-04 8:12 ` [PATCH v5 8/9] perf annotate-data: " Ian Rogers ` (3 subsequent siblings) 10 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-04 8:12 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwarf_formsdata`, `dwarf_entrypc`, `dwarf_highpc`, `dwarf_bytesize`, `dwarf_attr`, `dwarf_decl_line`, `dwarf_getfuncs`, and `dwarf_formref_die`. Validate `dwarf_diename` and `dwarf_diecu` results to prevent potential crashes. Fix C90 mixed declarations. Additionally: - Avoid vfprintf undefined behavior with NULL strings by using the `die_name()` helper for `dwarf_diename()` in `pr_*` calls, including when warning about tail calls. - Prevent NULL pointer dereference in `convert_variable_fields()` when processing array elements for variables in registers. - Fallback to offset 0 in `line_range_search_cb()` instead of skipping functions without `DW_AT_decl_line`. - Relax `dwarf_getfuncs` error checking in `find_probe_point_by_func()` and `find_line_range_by_func()` to prevent premature CU search aborts, ensuring robustness against corrupted CUs. Fixes: 66f69b219716 ("perf probe: Support DW_AT_const_value constant value") Fixes: 3d918a12a1b3 ("perf probe: Find fentry mcount fuzzed parameter location") Fixes: bcfc082150c6 ("perf probe: Remove redundant dwarf functions") Fixes: 221d061182b8 ("perf probe: Fix to search local variables in appropriate scope") Fixes: b55a87ade383 ("perf probe: Remove die() from probe-finder code") Fixes: 4c859351226c ("perf probe: Support glob wildcards for function name") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- v5: - Safe DWARF name printing using die_name(). - Corrected CU DIE propagation. - Standardized comment style to /* ... */. v4: - Safe DWARF name printing with die_name() to avoid NULL formatting crashes. - Fix NULL dereference in register variable array lookups. - Fix robust CU search loops by continuing on getfuncs errors. --- tools/perf/util/probe-finder.c | 102 +++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 64328abeef8b..f3f9a1573502 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -79,7 +79,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, unsigned int regn; Dwarf_Word offs = 0; bool ref = false; - const char *regs; + const char *regs, *name; int ret, ret2 = 0; if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL) @@ -93,7 +93,8 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (!tvar) return 0; - dwarf_formsdata(&attr, &snum); + if (dwarf_formsdata(&attr, &snum) != 0) + return -ENOENT; ret = asprintf(&tvar->value, "\\%ld", (long)snum); return ret < 0 ? -ENOMEM : 0; @@ -103,8 +104,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) return -EINVAL; /* Broken DIE ? */ if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { - ret = dwarf_entrypc(sp_die, &tmp); - if (ret) + if (dwarf_entrypc(sp_die, &tmp) != 0) return -ENOENT; if (probe_conf.show_location_range && @@ -115,8 +115,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, return -ENOENT; } - ret = dwarf_highpc(sp_die, &tmp); - if (ret) + if (dwarf_highpc(sp_die, &tmp) != 0) return -ENOENT; /* * This is fuzzed by fentry mcount. We try to find the @@ -138,12 +137,16 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, static_var: if (!tvar) return ret2; + /* Static variables on memory (not stack), make @varname */ - ret = strlen(dwarf_diename(vr_die)); + name = dwarf_diename(vr_die); + if (!name) + return -ENOENT; + ret = strlen(name); tvar->value = zalloc(ret + 2); if (tvar->value == NULL) return -ENOMEM; - snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); + snprintf(tvar->value, ret + 2, "@%s", name); tvar->ref = alloc_trace_arg_ref((long)offs); if (tvar->ref == NULL) return -ENOMEM; @@ -234,13 +237,14 @@ static int convert_variable_type(Dwarf_Die *vr_die, } if (die_get_real_type(vr_die, &type) == NULL) { - pr_warning("Failed to get a type information of %s.\n", - dwarf_diename(vr_die)); + const char *name = dwarf_diename(vr_die); + + pr_warning("Failed to get a type information of %s.\n", name ?: "<unknown>"); return -ENOENT; } pr_debug("%s type is %s.\n", - dwarf_diename(vr_die), dwarf_diename(&type)); + die_name(vr_die), die_name(&type)); if (cast && (!strcmp(cast, "string") || !strcmp(cast, "ustring"))) { /* String type */ @@ -249,7 +253,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, ret != DW_TAG_array_type) { pr_warning("Failed to cast into string: " "%s(%s) is not a pointer nor array.\n", - dwarf_diename(vr_die), dwarf_diename(&type)); + die_name(vr_die), die_name(&type)); return -EINVAL; } if (die_get_real_type(&type, &type) == NULL) { @@ -272,7 +276,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, !die_compare_name(&type, "unsigned char")) { pr_warning("Failed to cast into string: " "%s is not (unsigned) char *.\n", - dwarf_diename(vr_die)); + die_name(vr_die)); return -EINVAL; } tvar->type = strdup(cast); @@ -299,7 +303,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, /* Check the bitwidth */ if (ret > MAX_BASIC_TYPE_BITS) { pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n", - dwarf_diename(&type), MAX_BASIC_TYPE_BITS); + die_name(&type), MAX_BASIC_TYPE_BITS); ret = MAX_BASIC_TYPE_BITS; } ret = snprintf(buf, 16, "%c%d", prefix, ret); @@ -333,12 +337,14 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, pr_warning("Failed to get the type of %s.\n", varname); return -ENOENT; } - pr_debug2("Var real type: %s (%x)\n", dwarf_diename(&type), + pr_debug2("Var real type: %s (%x)\n", die_name(&type), (unsigned)dwarf_dieoffset(&type)); tag = dwarf_tag(&type); if (field->name[0] == '[' && (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { + int bsize; + /* Save original type for next field or type */ memcpy(die_mem, &type, sizeof(*die_mem)); /* Get the type of this array */ @@ -346,7 +352,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, pr_warning("Failed to get the type of %s.\n", varname); return -ENOENT; } - pr_debug2("Array real type: %s (%x)\n", dwarf_diename(&type), + pr_debug2("Array real type: %s (%x)\n", die_name(&type), (unsigned)dwarf_dieoffset(&type)); if (tag == DW_TAG_pointer_type) { ref = zalloc(sizeof(struct probe_trace_arg_ref)); @@ -357,7 +363,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, else *ref_ptr = ref; } - ref->offset += dwarf_bytesize(&type) * field->index; + bsize = dwarf_bytesize(&type); + + if (bsize < 0) + return -EINVAL; + if (!ref) { + pr_warning("Array indexing not supported for variables in registers.\n"); + return -ENOTSUP; + } + ref->offset += bsize * field->index; ref->user_access = user_access; goto next; } else if (tag == DW_TAG_pointer_type) { @@ -414,7 +428,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, if (die_find_member(&type, field->name, die_mem) == NULL) { pr_warning("%s(type:%s) has no member %s.\n", varname, - dwarf_diename(&type), field->name); + die_name(&type), field->name); return -EINVAL; } @@ -461,7 +475,7 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) int ret; pr_debug("Converting variable %s into trace event.\n", - dwarf_diename(vr_die)); + die_name(vr_die)); ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, &pf->sp_die, pf, pf->tvar); @@ -542,7 +556,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, /* Verify the address is correct */ if (!dwarf_haspc(sp_die, paddr)) { pr_warning("Specified offset is out of %s\n", - dwarf_diename(sp_die)); + die_name(sp_die)); return -EINVAL; } @@ -599,7 +613,7 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { pr_warning("Ignoring tail call from %s\n", - dwarf_diename(&pf->sp_die)); + die_name(&pf->sp_die)); return 0; } else { pr_warning("Failed to find probe point in any " @@ -611,10 +625,16 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); /* Get the frame base attribute/ops from subprogram */ - dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); - ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); - if (ret <= 0 || nops == 0) { + if (dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr) == NULL) { pf->fb_ops = NULL; + } else { + ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); + if (ret <= 0 || nops == 0) + pf->fb_ops = NULL; + } + + if (pf->fb_ops == NULL) { + /* Not supported */ } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && (pf->cfi_eh != NULL || pf->cfi_dbg != NULL)) { if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 && @@ -667,8 +687,8 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) } } else { /* With the line number, find the nearest declared DIE */ - dwarf_decl_line(fn_die, &lno); - if (lno < fsp->line && fsp->diff > fsp->line - lno) { + if (dwarf_decl_line(fn_die, &lno) == 0 && lno < fsp->line && + fsp->diff > fsp->line - lno) { /* Keep a candidate and continue */ fsp->diff = fsp->line - lno; memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); @@ -924,12 +944,12 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) /* Get probe address */ if (die_entrypc(in_die, &addr) != 0) { pr_warning("Failed to get entry address of %s.\n", - dwarf_diename(in_die)); + die_name(in_die)); return -ENOENT; } if (addr == 0) { pr_debug("%s has no valid entry address. skipped.\n", - dwarf_diename(in_die)); + die_name(in_die)); return -ENOENT; } pf->addr = addr; @@ -971,12 +991,13 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) if (pp->file && fname && strtailcmp(pp->file, fname)) return DWARF_CB_OK; - pr_debug("Matched function: %s [%lx]\n", dwarf_diename(sp_die), + pr_debug("Matched function: %s [%lx]\n", die_name(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); + if (dwarf_decl_line(sp_die, &pf->lno) != 0) + return DWARF_CB_OK; pf->lno += pp->line; param->retval = find_probe_point_by_line(pf); } else if (die_is_func_instance(sp_die)) { @@ -985,7 +1006,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) /* But in some case the entry address is 0 */ if (pf->addr == 0) { pr_debug("%s has no entry PC. Skipped\n", - dwarf_diename(sp_die)); + die_name(sp_die)); param->retval = 0; /* Real function */ } else if (pp->lazy_line) @@ -1018,7 +1039,8 @@ static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; - dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); + if (dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0) < 0) + pr_debug("Failed to get functions from CU\n"); return _param.retval; } @@ -1207,7 +1229,8 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void *data) * points to correct die. */ if (dwarf_attr(die_mem, DW_AT_abstract_origin, &attr)) { - dwarf_formref_die(&attr, &var_die); + if (dwarf_formref_die(&attr, &var_die) == NULL) + goto out; if (pf->abstrace_dieoffset != dwarf_dieoffset(&var_die)) goto out; } @@ -1293,13 +1316,16 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) if (ret < 0) goto end; - tev->point.realname = strdup(dwarf_diename(sc_die)); + tev->point.realname = strdup(die_name(sc_die)); if (!tev->point.realname) { ret = -ENOMEM; goto end; } - tev->lang = dwarf_srclang(dwarf_diecu(sc_die, &pf->cu_die, NULL, NULL)); + if (dwarf_diecu(sc_die, &pf->cu_die, NULL, NULL) != NULL) + tev->lang = dwarf_srclang(&pf->cu_die); + else + tev->lang = DW_LANG_C; /* Fallback */ pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); @@ -1794,7 +1820,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) if (die_match_name(sp_die, lr->function) && die_is_func_def(sp_die)) { lf->fname = die_get_decl_file(sp_die); - dwarf_decl_line(sp_die, &lr->offset); + if (dwarf_decl_line(sp_die, &lr->offset) != 0) + lr->offset = 0; /* Fallback if no line info */ pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; if (lf->lno_s < 0) /* Overflow */ @@ -1818,7 +1845,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; - dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); + if (dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0) < 0) + pr_debug("Failed to get functions from CU\n"); return param.retval; } -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v5 8/9] perf annotate-data: Fix libdw API contract violations 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (6 preceding siblings ...) 2026-05-04 8:12 ` [PATCH v5 7/9] perf probe-finder: Fix libdw API contract violations Ian Rogers @ 2026-05-04 8:12 ` Ian Rogers 2026-05-04 8:12 ` [PATCH v5 9/9] perf debuginfo: " Ian Rogers ` (2 subsequent siblings) 10 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-04 8:12 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return values of `dwarf_aggregate_size` and `dwarf_formudata`. Additionally: - Avoid `vfprintf` undefined behavior with `NULL` strings by using the `die_name()` helper for `dwarf_diename()` in `pr_*` calls. - Use `die_get_data_member_location()` (updated to use `dwarf_attr_integrate`) to correctly parse location expressions for inherited member locations in the fallback path when `dwarf_formudata()` fails. Fixes: 2bc3cf575a16 ("perf annotate-data: Improve debug message with location info") Fixes: 4a111cadac85 ("perf annotate-data: Add member field in the data type") Fixes: 8b1042c425f6 ("perf annotate-data: Set bitfield member offset and size properly") Fixes: fc044c53b99f ("perf annotate-data: Add dso->data_types tree") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers <irogers@google.com> --- v5: - Safe string duplication for member variable names. v4: - Safe DWARF name printing in annotate-data. - Fix fallback location expression parsing for inherited data members. --- tools/perf/util/annotate-data.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 1eff0a27237d..63e3c54fab42 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -74,7 +74,8 @@ void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) break; } - dwarf_aggregate_size(die, &size); + if (dwarf_aggregate_size(die, &size) != 0) + size = 0; strbuf_init(&sb, 32); die_get_typename_from_type(die, &sb); @@ -146,9 +147,9 @@ static void pr_debug_scope(Dwarf_Die *scope_die) tag = dwarf_tag(scope_die); if (tag == DW_TAG_subprogram) - pr_info("[function] %s\n", dwarf_diename(scope_die)); + pr_info("[function] %s\n", die_name(scope_die)); else if (tag == DW_TAG_inlined_subroutine) - pr_info("[inlined] %s\n", dwarf_diename(scope_die)); + pr_info("[inlined] %s\n", die_name(scope_die)); else if (tag == DW_TAG_lexical_block) pr_info("[block]\n"); else @@ -250,9 +251,12 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) if (dwarf_aggregate_size(&die_mem, &size) < 0) size = 0; - if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) - dwarf_formudata(&attr, &loc); - else { + if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) { + if (dwarf_formudata(&attr, &loc) != 0) { + if (die_get_data_member_location(die, &loc) != 0) + loc = 0; + } + } else { /* bitfield member */ if (dwarf_attr_integrate(die, DW_AT_data_bit_offset, &attr) && dwarf_formudata(&attr, &loc) == 0) @@ -273,7 +277,9 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) dwarf_diename(die), (long)bit_size) < 0) member->var_name = NULL; } else { - member->var_name = strdup(dwarf_diename(die)); + const char *name = dwarf_diename(die); + + member->var_name = name ? strdup(name) : NULL; } if (member->var_name == NULL) { @@ -370,7 +376,8 @@ static struct annotated_data_type *dso__findnew_data_type(struct dso *dso, if (dwarf_tag(type_die) == DW_TAG_typedef) die_get_real_type(type_die, type_die); - dwarf_aggregate_size(type_die, &size); + if (dwarf_aggregate_size(type_die, &size) != 0) + size = 0; /* Check existing nodes in dso->data_types tree */ key.self.type_name = type_name; @@ -1569,7 +1576,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) offset = loc->offset; pr_debug_dtp("CU for %s (die:%#lx)\n", - dwarf_diename(&cu_die), (long)dwarf_dieoffset(&cu_die)); + die_name(&cu_die), (long)dwarf_dieoffset(&cu_die)); if (reg == DWARF_REG_PC) { if (get_global_var_type(&cu_die, dloc, dloc->ip, dloc->var_addr, @@ -1636,7 +1643,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) } pr_debug_dtp("found \"%s\" (die: %#lx) in scope=%d/%d (die: %#lx) ", - dwarf_diename(&var_die), (long)dwarf_dieoffset(&var_die), + die_name(&var_die), (long)dwarf_dieoffset(&var_die), i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i])); if (reg == DWARF_REG_PC) { -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v5 9/9] perf debuginfo: Fix libdw API contract violations 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (7 preceding siblings ...) 2026-05-04 8:12 ` [PATCH v5 8/9] perf annotate-data: " Ian Rogers @ 2026-05-04 8:12 ` Ian Rogers 2026-05-04 23:50 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Namhyung Kim 2026-05-06 0:54 ` Arnaldo Carvalho de Melo 10 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-04 8:12 UTC (permalink / raw) To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Cc: Ian Rogers Check return value of `dwfl_report_end` during offline initialization. Validate `dwfl_module_relocation_info` result before passing to `strcmp` to avoid potential segmentation faults. Additionally: - Fix a file descriptor leak in `debuginfo__init_offline_dwarf()` when `dwfl_report_offline()` or subsequent setup calls fail. Fixes: 6f1b6291cf73 ("perf tools: Add util/debuginfo.[ch] files") Assisted-by: Gemini-CLI:Google Gemini 3 Acked-by: Namhyung Kim <namhyung@kernel.org> Signed-off-by: Ian Rogers <irogers@google.com> --- v5: - Added Acked-by tag. v4: - Fix file descriptor leaks in debuginfo init paths. --- tools/perf/util/debuginfo.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c index 0e35c13abd04..84a78b30ceac 100644 --- a/tools/perf/util/debuginfo.c +++ b/tools/perf/util/debuginfo.c @@ -42,6 +42,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, { GElf_Addr dummy; int fd; + bool fd_consumed = false; fd = open(path, O_RDONLY); if (fd < 0) @@ -55,6 +56,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); if (!dbg->mod) goto error; + fd_consumed = true; dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); if (!dbg->dbg) @@ -62,13 +64,14 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); - dwfl_report_end(dbg->dwfl, NULL, NULL); + if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0) + goto error; return 0; error: if (dbg->dwfl) dwfl_end(dbg->dwfl); - else + if (!fd_consumed) close(fd); memset(dbg, 0, sizeof(*dbg)); @@ -167,7 +170,7 @@ int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, /* Search the relocation related .text section */ for (i = 0; i < n; i++) { p = dwfl_module_relocation_info(dbg->mod, i, &shndx); - if (strcmp(p, ".text") == 0) { + if (p && strcmp(p, ".text") == 0) { /* OK, get the section header */ scn = elf_getscn(elf, shndx); if (!scn) -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (8 preceding siblings ...) 2026-05-04 8:12 ` [PATCH v5 9/9] perf debuginfo: " Ian Rogers @ 2026-05-04 23:50 ` Namhyung Kim 2026-05-05 16:29 ` Ian Rogers 2026-05-06 0:54 ` Arnaldo Carvalho de Melo 10 siblings, 1 reply; 33+ messages in thread From: Namhyung Kim @ 2026-05-04 23:50 UTC (permalink / raw) To: Ian Rogers Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel Hi Ian, Just updated Masami's email address. Please use it for later. Thanks, Namhyung On Mon, May 04, 2026 at 01:12:18AM -0700, Ian Rogers wrote: > This patch series addresses a number of DWARF/libdw error-handling > bugs and contract violations, preventing several real Userspace > segmentation faults and memory/FD leaks. > > In v5, the series has been extensively restructured and polished based on > comprehensive review feedback on v4, focusing on history granularity, > bisectability, and defensive styling: > > - **Commit Splitting for Review Granularity**: Split the previously large > libdw contract fix patch into 4 granular commits to cleanly isolate > independent improvements: introducing clear_frames refactoring, fixing > ORDER_CALLER parent update corruption, adding line 0 support, and > consolidating core contract/leak cleanups. > > - **Bisectability & Correctness Hardening**: > - Fixed an unused variable compilation failure (-Werror) in the split > history to guarantee perfect git bisectability. > - Fixed a line 0 fallback regression to ensure that if an optional call > line is missing but the call file is valid, we fallback to line 0 > to preserve the filename rather than discarding the caller info entirely. > > - **Style & Robustness Polish**: > - Standardized all newly introduced C++ style (//) comments to > preferred C style (/* ... */) comments. > - Implemented explicit safe string duplication style fix in annotate-data. > - Corrected CU DIE propagation context inside probe-finder. > - Enhanced the Patch 2 commit message to explicitly detail the removal > of strict optional attribute aborts (decf) to clarify review queries. > > - **Tags Collected**: Integrated Acked-by tags from Namhyung Kim for Patch 1 > and Patch 9. > > v5: > - Restructured series from 6 to 9 patches by splitting the libdw commit. > - Fixed compilation bisectability and DWARF line 0 filename preservation regressions. > - Standardized comment styles and applied safe duplication formatting fixes. > - Updated commit messages with detailed optional attribute justifications. > > v4: > - Localized and squashed robust error handling, memory/FD cleanup > paths, and safe DWARF printing. > https://lore.kernel.org/linux-perf-users/20260503171032.1559338-1-irogers@google.com/ > > v3: > - Minor formatting fixes. > https://lore.kernel.org/linux-perf-users/20260503003552.1063540-1-irogers@google.com/ > > v2: > https://lore.kernel.org/lkml/20260502155656.478642-1-irogers@google.com/ > > v1: > https://lore.kernel.org/linux-perf-users/20260502064839.282422-1-irogers@google.com/ > > Ian Rogers (9): > perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at > perf dwarf-aux: Fix libdw API contract violations > perf srcline: Introduce inline_node__clear_frames() > perf libdw: Fix callchain parent update in ORDER_CALLER mode > perf libdw: Support DWARF line 0 in inline list > perf libdw: Fix libdw API contract violations and memory leaks > perf probe-finder: Fix libdw API contract violations > perf annotate-data: Fix libdw API contract violations > perf debuginfo: Fix libdw API contract violations > > tools/perf/util/annotate-data.c | 27 +++++---- > tools/perf/util/debuginfo.c | 9 ++- > tools/perf/util/dwarf-aux.c | 39 ++++++------ > tools/perf/util/dwarf-aux.h | 5 ++ > tools/perf/util/libdw.c | 72 +++++++++++++++++----- > tools/perf/util/probe-finder.c | 102 ++++++++++++++++++++------------ > tools/perf/util/srcline.c | 9 ++- > tools/perf/util/srcline.h | 1 + > 8 files changed, 179 insertions(+), 85 deletions(-) > > -- > 2.54.0.545.g6539524ca2-goog > ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes 2026-05-04 23:50 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Namhyung Kim @ 2026-05-05 16:29 ` Ian Rogers 0 siblings, 0 replies; 33+ messages in thread From: Ian Rogers @ 2026-05-05 16:29 UTC (permalink / raw) To: Namhyung Kim, Masami Hiramatsu Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, linux-perf-users, linux-kernel On Mon, May 4, 2026 at 4:50 PM Namhyung Kim <namhyung@kernel.org> wrote: > > Hi Ian, > > Just updated Masami's email address. Please use it for later. Thanks Namhyung, I'll make sure in the future. For Masami's benefit here are the LKML posts: https://lore.kernel.org/linux-perf-users/afkw4MARqVAYOB2p@google.com/T/#mb887c8aadf50c0371c4cb9273b9a7777591ad99e The sashiko reviews (the 1 "critical" issue isn't an issue as mentioned in a separate thread [1]): https://sashiko.dev/#/patchset/20260504081227.2203848-1-irogers%40google.com I think the series is ready to land and given that it fixes crashes we may want to cherry-pick it for 7.1. Thanks, Ian [1] https://lore.kernel.org/linux-perf-users/afkw4MARqVAYOB2p@google.com/T/#mc177f9ef695805b013fce3fecbea7d84dfa937fd > Thanks, > Namhyung > > On Mon, May 04, 2026 at 01:12:18AM -0700, Ian Rogers wrote: > > This patch series addresses a number of DWARF/libdw error-handling > > bugs and contract violations, preventing several real Userspace > > segmentation faults and memory/FD leaks. > > > > In v5, the series has been extensively restructured and polished based on > > comprehensive review feedback on v4, focusing on history granularity, > > bisectability, and defensive styling: > > > > - **Commit Splitting for Review Granularity**: Split the previously large > > libdw contract fix patch into 4 granular commits to cleanly isolate > > independent improvements: introducing clear_frames refactoring, fixing > > ORDER_CALLER parent update corruption, adding line 0 support, and > > consolidating core contract/leak cleanups. > > > > - **Bisectability & Correctness Hardening**: > > - Fixed an unused variable compilation failure (-Werror) in the split > > history to guarantee perfect git bisectability. > > - Fixed a line 0 fallback regression to ensure that if an optional call > > line is missing but the call file is valid, we fallback to line 0 > > to preserve the filename rather than discarding the caller info entirely. > > > > - **Style & Robustness Polish**: > > - Standardized all newly introduced C++ style (//) comments to > > preferred C style (/* ... */) comments. > > - Implemented explicit safe string duplication style fix in annotate-data. > > - Corrected CU DIE propagation context inside probe-finder. > > - Enhanced the Patch 2 commit message to explicitly detail the removal > > of strict optional attribute aborts (decf) to clarify review queries. > > > > - **Tags Collected**: Integrated Acked-by tags from Namhyung Kim for Patch 1 > > and Patch 9. > > > > v5: > > - Restructured series from 6 to 9 patches by splitting the libdw commit. > > - Fixed compilation bisectability and DWARF line 0 filename preservation regressions. > > - Standardized comment styles and applied safe duplication formatting fixes. > > - Updated commit messages with detailed optional attribute justifications. > > > > v4: > > - Localized and squashed robust error handling, memory/FD cleanup > > paths, and safe DWARF printing. > > https://lore.kernel.org/linux-perf-users/20260503171032.1559338-1-irogers@google.com/ > > > > v3: > > - Minor formatting fixes. > > https://lore.kernel.org/linux-perf-users/20260503003552.1063540-1-irogers@google.com/ > > > > v2: > > https://lore.kernel.org/lkml/20260502155656.478642-1-irogers@google.com/ > > > > v1: > > https://lore.kernel.org/linux-perf-users/20260502064839.282422-1-irogers@google.com/ > > > > Ian Rogers (9): > > perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at > > perf dwarf-aux: Fix libdw API contract violations > > perf srcline: Introduce inline_node__clear_frames() > > perf libdw: Fix callchain parent update in ORDER_CALLER mode > > perf libdw: Support DWARF line 0 in inline list > > perf libdw: Fix libdw API contract violations and memory leaks > > perf probe-finder: Fix libdw API contract violations > > perf annotate-data: Fix libdw API contract violations > > perf debuginfo: Fix libdw API contract violations > > > > tools/perf/util/annotate-data.c | 27 +++++---- > > tools/perf/util/debuginfo.c | 9 ++- > > tools/perf/util/dwarf-aux.c | 39 ++++++------ > > tools/perf/util/dwarf-aux.h | 5 ++ > > tools/perf/util/libdw.c | 72 +++++++++++++++++----- > > tools/perf/util/probe-finder.c | 102 ++++++++++++++++++++------------ > > tools/perf/util/srcline.c | 9 ++- > > tools/perf/util/srcline.h | 1 + > > 8 files changed, 179 insertions(+), 85 deletions(-) > > > > -- > > 2.54.0.545.g6539524ca2-goog > > ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers ` (9 preceding siblings ...) 2026-05-04 23:50 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Namhyung Kim @ 2026-05-06 0:54 ` Arnaldo Carvalho de Melo 10 siblings, 0 replies; 33+ messages in thread From: Arnaldo Carvalho de Melo @ 2026-05-06 0:54 UTC (permalink / raw) To: Ian Rogers Cc: Peter Zijlstra, Ingo Molnar, Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users, linux-kernel On Mon, May 04, 2026 at 01:12:18AM -0700, Ian Rogers wrote: > This patch series addresses a number of DWARF/libdw error-handling > bugs and contract violations, preventing several real Userspace > segmentation faults and memory/FD leaks. > In v5, the series has been extensively restructured and polished based on > comprehensive review feedback on v4, focusing on history granularity, > bisectability, and defensive styling: Thanks, applied to perf-tools-next, for v7.2. - Arnaldo ^ permalink raw reply [flat|nested] 33+ messages in thread
end of thread, other threads:[~2026-05-06 0:54 UTC | newest] Thread overview: 33+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-03 0:35 [PATCH v3 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 2026-05-03 0:35 ` [PATCH v3 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers 2026-05-03 0:35 ` [PATCH v3 2/6] perf dwarf-aux: Fix libdw API contract violations Ian Rogers 2026-05-03 0:35 ` [PATCH v3 3/6] perf libdw: " Ian Rogers 2026-05-03 0:35 ` [PATCH v3 4/6] perf probe-finder: " Ian Rogers 2026-05-03 0:35 ` [PATCH v3 5/6] perf annotate-data: " Ian Rogers 2026-05-03 0:35 ` [PATCH v3 6/6] perf debuginfo: " Ian Rogers 2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 2026-05-03 17:10 ` [PATCH v4 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers 2026-05-03 23:33 ` Namhyung Kim 2026-05-03 17:10 ` [PATCH v4 2/6] perf dwarf-aux: Fix libdw API contract violations Ian Rogers 2026-05-03 23:36 ` Namhyung Kim 2026-05-03 17:10 ` [PATCH v4 3/6] perf libdw: " Ian Rogers 2026-05-03 23:44 ` Namhyung Kim 2026-05-03 17:10 ` [PATCH v4 4/6] perf probe-finder: " Ian Rogers 2026-05-03 23:49 ` Namhyung Kim 2026-05-03 17:10 ` [PATCH v4 5/6] perf annotate-data: " Ian Rogers 2026-05-03 23:53 ` Namhyung Kim 2026-05-03 17:10 ` [PATCH v4 6/6] perf debuginfo: " Ian Rogers 2026-05-03 23:54 ` Namhyung Kim 2026-05-04 8:12 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers 2026-05-04 8:12 ` [PATCH v5 1/9] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at Ian Rogers 2026-05-04 8:12 ` [PATCH v5 2/9] perf dwarf-aux: Fix libdw API contract violations Ian Rogers 2026-05-04 8:12 ` [PATCH v5 3/9] perf srcline: Introduce inline_node__clear_frames() Ian Rogers 2026-05-04 8:12 ` [PATCH v5 4/9] perf libdw: Fix callchain parent update in ORDER_CALLER mode Ian Rogers 2026-05-04 8:12 ` [PATCH v5 5/9] perf libdw: Support DWARF line 0 in inline list Ian Rogers 2026-05-04 8:12 ` [PATCH v5 6/9] perf libdw: Fix libdw API contract violations and memory leaks Ian Rogers 2026-05-04 8:12 ` [PATCH v5 7/9] perf probe-finder: Fix libdw API contract violations Ian Rogers 2026-05-04 8:12 ` [PATCH v5 8/9] perf annotate-data: " Ian Rogers 2026-05-04 8:12 ` [PATCH v5 9/9] perf debuginfo: " Ian Rogers 2026-05-04 23:50 ` [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes Namhyung Kim 2026-05-05 16:29 ` Ian Rogers 2026-05-06 0:54 ` Arnaldo Carvalho de Melo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox