* [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; 46+ 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] 46+ 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:48 ` sashiko-bot
2026-05-03 0:35 ` [PATCH v3 2/6] perf dwarf-aux: Fix libdw API contract violations Ian Rogers
` (5 subsequent siblings)
6 siblings, 1 reply; 46+ 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] 46+ 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 1:02 ` sashiko-bot
2026-05-03 0:35 ` [PATCH v3 3/6] perf libdw: " Ian Rogers
` (4 subsequent siblings)
6 siblings, 1 reply; 46+ 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] 46+ 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 1:18 ` sashiko-bot
2026-05-03 0:35 ` [PATCH v3 4/6] perf probe-finder: " Ian Rogers
` (3 subsequent siblings)
6 siblings, 1 reply; 46+ 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] 46+ 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 1:34 ` sashiko-bot
2026-05-03 0:35 ` [PATCH v3 5/6] perf annotate-data: " Ian Rogers
` (2 subsequent siblings)
6 siblings, 1 reply; 46+ 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] 46+ 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 1:44 ` sashiko-bot
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, 1 reply; 46+ 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] 46+ 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 2:01 ` sashiko-bot
2026-05-03 17:10 ` [PATCH v4 0/6] perf DWARF: Fix libdw API contract violations and crashes Ian Rogers
6 siblings, 1 reply; 46+ 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] 46+ messages in thread
* Re: [PATCH v3 1/6] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at
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:48 ` sashiko-bot
0 siblings, 0 replies; 46+ messages in thread
From: sashiko-bot @ 2026-05-03 0:48 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Bogus parameter `@dbg` added to kernel-doc for `die_find_realfunc`.
--
commit 3f417cdb8c6fb98d5939fd0f506ef04fca29beeb
Author: Ian Rogers <irogers@google.com>
perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at
This commit fixes a segmentation fault in libdw when running perf kmem. It
corrects the error handling for dwarf_getfuncs in die_find_realfunc and
die_find_tailfunc to properly catch the error condition.
> diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
> --- a/tools/perf/util/dwarf-aux.c
> +++ b/tools/perf/util/dwarf-aux.c
[ ... ]
> @@ -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
This isn't a bug, but the dbg parameter is not actually part of the function
signature for die_find_realfunc. Will this trigger an excess function
parameter warning during kernel-doc builds?
> * @die_mem: a buffer for result DIE
> *
> * Search a non-inlined function DIE which includes @addr. Stores the
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260503003552.1063540-1-irogers@google.com?part=1
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v3 2/6] perf dwarf-aux: Fix libdw API contract violations
2026-05-03 0:35 ` [PATCH v3 2/6] perf dwarf-aux: Fix libdw API contract violations Ian Rogers
@ 2026-05-03 1:02 ` sashiko-bot
0 siblings, 0 replies; 46+ messages in thread
From: sashiko-bot @ 2026-05-03 1:02 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Returning `-EINVAL` when `dwarf_decl_line` fails for the root DIE breaks line walking for valid functions.
--
commit c967da2fa8dfee2321c45ab4e1d42344eca760b5
Author: Ian Rogers <irogers@google.com>
perf dwarf-aux: Fix libdw API contract violations
This commit adds return value checks to dwarf_decl_line, dwarf_getfuncs,
and dwarf_lineaddr across dwarf-aux.c. It aims to prevent the use of
uninitialized stack variables or incorrectly reporting success on failure.
> 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
[ ... ]
> @@ -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;
> + }
Does returning -EINVAL here break line walking for valid functions that
omit the declaration line attribute?
DWARF attributes like DW_AT_decl_line and DW_AT_decl_file are optional and
frequently omitted by compilers for assembly functions, artificial functions,
or compiler-generated thunks.
Before this change, if dwarf_decl_line() failed, decl remained 0 and
the function continued gracefully. Aborting with -EINVAL might prevent
tools like perf probe from working on valid functions.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260503003552.1063540-1-irogers@google.com?part=2
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v3 3/6] perf libdw: Fix libdw API contract violations
2026-05-03 0:35 ` [PATCH v3 3/6] perf libdw: " Ian Rogers
@ 2026-05-03 1:18 ` sashiko-bot
0 siblings, 0 replies; 46+ messages in thread
From: sashiko-bot @ 2026-05-03 1:18 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] Incomplete error handling makes the newly added `args.err` field entirely dead code, silently swallowing inline unwinding memory errors.
- [Medium] The new bounds check `call_lineno > 0` incorrectly discards valid DWARF line `0` references, leading to lost caller filenames.
- [Medium] The commit message falsely claims the patch prevents a `strdup(NULL)` crash in `new_inline_sym`, justifying the change on an incorrect premise.
--
commit 5e576062601ce3a0337e76030c663f3ce42bdf61
Author: Ian Rogers <irogers@google.com>
perf libdw: Fix libdw API contract violations
This commit adds return value checks for several libdw API calls. It also
introduces an error field to propagate inline unwinding failures and
filters caller line numbers to avoid negative DWARF error codes.
> 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.
This isn't a bug, but is the description of the crash risk accurate?
Looking at new_inline_sym(), it already guards against NULL pointers
by defaulting to a placeholder string:
static struct symbol *new_inline_sym(struct dso *dso,
struct symbol *base_sym,
const char *funcname)
{
...
if (!funcname)
funcname = "??";
It appears the patch changes the fallback string from "??" to "unknown"
but wouldn't actually prevent a crash since NULL is already handled natively.
> 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
[ ... ]
> @@ -72,22 +76,27 @@ struct libdw_a2l_cb_args {
> 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)
Does this condition inadvertently drop valid DWARF line 0 references?
In the DWARF standard, line 0 is a valid identifier used for
compiler-generated code that lacks a specific source line, but the
associated file name remains valid. By requiring the line number to
be strictly greater than 0, it seems we might discard successfully
retrieved caller filenames for these entries.
> + 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);
Is there a missing check for args.err after this walk?
The callback libdw_a2l_cb() was updated to set args.err = -ENOMEM and
return DWARF_CB_ABORT on allocation failure. However,
cu_walk_functions_at() is called here without inspecting its return
value or args.err, and libdw__addr2line() eventually returns 1
(success) regardless.
This appears to leave args.err as dead code and silently swallows the
memory allocation error, returning a truncated inline callchain to
the caller.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260503003552.1063540-1-irogers@google.com?part=3
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v3 4/6] perf probe-finder: Fix libdw API contract violations
2026-05-03 0:35 ` [PATCH v3 4/6] perf probe-finder: " Ian Rogers
@ 2026-05-03 1:34 ` sashiko-bot
0 siblings, 0 replies; 46+ messages in thread
From: sashiko-bot @ 2026-05-03 1:34 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Invalid type formatting for zero-sized structures due to incorrect `dwarf_bytesize` validation.
- [High] Silent failure to resolve line ranges for functions missing the `DW_AT_decl_line` attribute.
- [High] NULL pointer dereference when processing array element access for variables located in registers.
--
commit c8b3942d25f27ba0aef6a22ab921e206c4971aca
Author: Ian Rogers <irogers@google.com>
perf probe-finder: Fix libdw API contract violations
This commit adds checks for return values from various libdw API calls
to prevent crashes and API contract violations. It also fixes some C90
mixed declarations.
> 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
[ ... ]
> @@ -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;
When processing a zero-sized type (such as an empty struct), dwarf_bytesize
returns 0. With this change, a size of 0 falls through to BYTES_TO_BITS(0)
and formats the type as u0, s0, or x0.
Does the kprobe-events parser support 0-bit basic types, or will it reject
the probe injection?
[ ... ]
> @@ -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;
> + }
This wasn't introduced by this patch, but if an array variable is located in
a register (like DW_OP_reg0), convert_variable_location leaves tvar->ref as
NULL.
Because there is no check to see if ref is NULL before calculating the array
index access offset here, will this result in a NULL pointer dereference and
crash the perf tool?
[ ... ]
> @@ -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
The DW_AT_decl_line attribute is not strictly mandatory and can be omitted
by compilers for generated functions.
By returning DWARF_CB_OK when dwarf_decl_line fails, will perf probe -L
silently skip these functions and display no lines, whereas previously it
left the offset at 0 and successfully mapped the line range relative to 0?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260503003552.1063540-1-irogers@google.com?part=4
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v3 5/6] perf annotate-data: Fix libdw API contract violations
2026-05-03 0:35 ` [PATCH v3 5/6] perf annotate-data: " Ian Rogers
@ 2026-05-03 1:44 ` sashiko-bot
0 siblings, 0 replies; 46+ messages in thread
From: sashiko-bot @ 2026-05-03 1:44 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Setting `loc = 0` upon `dwarf_formudata` failure silently corrupts struct member offsets for DWARF location expressions.
- [Medium] Redundant validation of `dwarf_diename` introduces dead code and fails to provide the safety claimed in the commit message.
--
commit c23bc4d691e2495e7ddd491d7f1b7b532eb95f50
Author: Ian Rogers <irogers@google.com>
perf annotate-data: Fix libdw API contract violations
This commit adds return value checks for dwarf_aggregate_size and
dwarf_formudata. It also attempts to validate dwarf_diename before calling
strdup to prevent potential crashes.
> 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
[ ... ]
> @@ -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;
Since DW_AT_data_member_location can be encoded as a location expression block
(such as DW_OP_plus_uconst) rather than just a constant form, dwarf_formudata
will return an error for those blocks.
Will setting loc to 0 here deterministically corrupt the struct member offsets
for these location expressions?
Should this code perhaps evaluate the location expression block using a helper
like die_get_data_member_location() instead of unconditionally setting it to
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");
The commit message states: "Validate dwarf_diename before strdup to prevent
potential crashes."
Looking at the larger context of __add_member_cb(), this block is already
guarded by an outer if statement checking the same value:
if (dwarf_diename(die)) {
if (bit_size) {
...
} else {
const char *name = dwarf_diename(die);
member->var_name = strdup(name ?: "unknown");
}
}
Does this make the fallback to "unknown" dead code since name is guaranteed
to be non-NULL here?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260503003552.1063540-1-irogers@google.com?part=5
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v3 6/6] perf debuginfo: Fix libdw API contract violations
2026-05-03 0:35 ` [PATCH v3 6/6] perf debuginfo: " Ian Rogers
@ 2026-05-03 2:01 ` sashiko-bot
0 siblings, 0 replies; 46+ messages in thread
From: sashiko-bot @ 2026-05-03 2:01 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing O_NONBLOCK in open() can cause indefinite hangs on untrusted paths.
--
commit febe76c541fd0ae3fbecd6102991f4418485f2b8
Author: Ian Rogers <irogers@google.com>
perf debuginfo: Fix libdw API contract violations
This commit adds a return value check for dwfl_report_end() during offline
initialization and validates the result of dwfl_module_relocation_info()
before passing it to strcmp().
> 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);
>
While reviewing this function, I noticed an older issue that pre-dates this
commit. Does the open() call earlier in this function risk an indefinite hang
if the path refers to a FIFO, device node, or slow network mount?
tools/perf/util/debuginfo.c:debuginfo__init_offline_dwarf() {
...
int fd;
fd = open(path, O_RDONLY);
if (fd < 0)
return fd;
...
}
Could this path be opened with the O_NONBLOCK flag to prevent hanging on
untrusted special files?
> - dwfl_report_end(dbg->dwfl, NULL, NULL);
> + if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0)
> + goto error;
>
> return 0;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260503003552.1063540-1-irogers@google.com?part=6
^ permalink raw reply [flat|nested] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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 17:40 ` sashiko-bot
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, 2 replies; 46+ 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] 46+ 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 18:09 ` sashiko-bot
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, 2 replies; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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 17:40 ` sashiko-bot
2026-05-03 23:36 ` Namhyung Kim
1 sibling, 0 replies; 46+ messages in thread
From: sashiko-bot @ 2026-05-03 17:40 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The fix for uninitialized stack variables in `die_walk_lines` unconditionally filters out inline functions that lack a declaration line, breaking line walking for such functions.
--
commit d271d4f48f3254386be3ea2c703448a90d59f608
Author: Ian Rogers <irogers@google.com>
perf dwarf-aux: Fix libdw API contract violations
This commit adds return value checks for several libdw API functions like
dwarf_decl_line(), dwarf_getfuncs(), and dwarf_lineaddr() to prevent using
uninitialized stack variables. It gracefully handles optional dwarf_decl_line()
calls for the root DIE, adds NULL pointer protection for strcmp(), and uses
dwarf_attr_integrate() for member locations.
> 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
[ ... ]
> @@ -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;
> }
If the root function lacks a declaration line, could this strict check cause us
to skip valid matching lines from inline instances?
The commit message notes that dwarf_decl_line() is optional for the root DIE
and its failures are handled gracefully. This leaves decl = 0 if the
declaration line is missing.
However, if the root function lacks a declaration line, its inline instances
will also lack one. When dwarf_decl_line(&die_mem, &inl) fails here, it now
evaluates to non-zero and forcefully continues the loop.
Should inl be safely defaulted to 0 on failure here to match the graceful
handling of the root DIE, rather than aborting the match entirely?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260503171032.1559338-1-irogers@google.com?part=2
^ permalink raw reply [flat|nested] 46+ 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 18:09 ` sashiko-bot
2026-05-03 23:44 ` Namhyung Kim
1 sibling, 0 replies; 46+ messages in thread
From: sashiko-bot @ 2026-05-03 18:09 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Missing O_NONBLOCK when opening dso_name in dso__libdw_dwfl()
--
commit 95f9f15ab79b0136da9eb501da82453ee78787c1
Author: Ian Rogers <irogers@google.com>
perf libdw: Fix libdw API contract violations
This commit adds error checking for dwfl_report_end and dwfl_module_addrdie.
It also introduces inline_node__clear_frames() to clean up partial allocations
and fixes potential memory leaks in libdw__addr2line().
> 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
[ ... ]
> @@ -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;
This isn't a bug introduced by this commit, but since this function is being
updated, could we also look at the open() call earlier in dso__libdw_dwfl()?
dso__libdw_dwfl() calls:
fd = open(dso_name, O_RDONLY);
If dso_name originates from user-controlled mmap events, it might point to a
FIFO, character device, or a hanging network mount.
Could this open() hang indefinitely?
Would it be safer to include the O_NONBLOCK flag here?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260503171032.1559338-1-irogers@google.com?part=3
^ permalink raw reply [flat|nested] 46+ 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; 46+ 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] 46+ 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 17:40 ` sashiko-bot
@ 2026-05-03 23:36 ` Namhyung Kim
1 sibling, 0 replies; 46+ 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] 46+ 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 18:09 ` sashiko-bot
@ 2026-05-03 23:44 ` Namhyung Kim
1 sibling, 0 replies; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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; 46+ 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] 46+ 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 10:53 ` sashiko-bot
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, 1 reply; 46+ 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] 46+ messages in thread
* Re: [PATCH v5 9/9] perf debuginfo: Fix libdw API contract violations
2026-05-04 8:12 ` [PATCH v5 9/9] perf debuginfo: " Ian Rogers
@ 2026-05-04 10:53 ` sashiko-bot
2026-05-04 15:26 ` Ian Rogers
0 siblings, 1 reply; 46+ messages in thread
From: sashiko-bot @ 2026-05-04 10:53 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] This problem wasn't introduced by this patch, but the open() call in debuginfo__init_offline_dwarf() lacks the O_NONBLOCK flag, exposing a local Denial of Service (DoS) vulnerability.
--
commit a6e339ae66465b909762a437f958cdaed424b666
Author: Ian Rogers <irogers@google.com>
perf debuginfo: Fix libdw API contract violations
This commit adds return value checks for dwfl_report_end and
dwfl_module_relocation_info to prevent potential segmentation faults. It also
fixes a file descriptor leak in the error path of
debuginfo__init_offline_dwarf() when offline initialization fails.
> 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);
While this wasn't introduced by this patch, could this open() call cause a
regression by hanging on a FIFO?
If the path originates from user-controlled sources, opening it without
O_NONBLOCK can cause the perf record session to hang indefinitely. Using
O_RDONLY | O_NONBLOCK would prevent this.
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260504081227.2203848-1-irogers@google.com?part=9
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v5 9/9] perf debuginfo: Fix libdw API contract violations
2026-05-04 10:53 ` sashiko-bot
@ 2026-05-04 15:26 ` Ian Rogers
2026-05-04 17:54 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 46+ messages in thread
From: Ian Rogers @ 2026-05-04 15:26 UTC (permalink / raw)
To: linux-perf-users, Arnaldo Carvalho de Melo, Namhyung Kim; +Cc: sashiko
On Mon, May 4, 2026 at 3:53 AM <sashiko-bot@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Critical] This problem wasn't introduced by this patch, but the open() call in debuginfo__init_offline_dwarf() lacks the O_NONBLOCK flag, exposing a local Denial of Service (DoS) vulnerability.
> --
>
> commit a6e339ae66465b909762a437f958cdaed424b666
> Author: Ian Rogers <irogers@google.com>
>
> perf debuginfo: Fix libdw API contract violations
>
> This commit adds return value checks for dwfl_report_end and
> dwfl_module_relocation_info to prevent potential segmentation faults. It also
> fixes a file descriptor leak in the error path of
> debuginfo__init_offline_dwarf() when offline initialization fails.
>
> > 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);
>
> While this wasn't introduced by this patch, could this open() call cause a
> regression by hanging on a FIFO?
>
> If the path originates from user-controlled sources, opening it without
> O_NONBLOCK can cause the perf record session to hang indefinitely. Using
> O_RDONLY | O_NONBLOCK would prevent this.
So this review is bad and based on this prompt:
https://github.com/masoncl/review-prompts/blob/main/kernel/subsystem/perf.md#file-io-safety-on-untrusted-paths
I'm trying to get the bad prompt removed. The prompt was automatically
generated from fixes commits when we added O_NONBLOCK to address build
ID reading hangs, particularly for data mmaps during perf record
synthesis. We now avoid those hangs by checking for a "regular" file
with stat. Therefore, the advice regarding O_NONBLOCK and EINTR from
the prompts is incorrect and does not reflect what the code base does.
Side note: this is the only Sashiko feedback on this series:
https://sashiko.dev/#/patchset/20260504081227.2203848-1-irogers%40google.com
Thanks,
Ian
> [ ... ]
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260504081227.2203848-1-irogers@google.com?part=9
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v5 9/9] perf debuginfo: Fix libdw API contract violations
2026-05-04 15:26 ` Ian Rogers
@ 2026-05-04 17:54 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 46+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-05-04 17:54 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users, Namhyung Kim, sashiko
On Mon, May 04, 2026 at 08:26:34AM -0700, Ian Rogers wrote:
> On Mon, May 4, 2026 at 3:53 AM <sashiko-bot@kernel.org> wrote:
> >
> > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > - [Critical] This problem wasn't introduced by this patch, but the open() call in debuginfo__init_offline_dwarf() lacks the O_NONBLOCK flag, exposing a local Denial of Service (DoS) vulnerability.
> > --
> >
> > commit a6e339ae66465b909762a437f958cdaed424b666
> > Author: Ian Rogers <irogers@google.com>
> >
> > perf debuginfo: Fix libdw API contract violations
> >
> > This commit adds return value checks for dwfl_report_end and
> > dwfl_module_relocation_info to prevent potential segmentation faults. It also
> > fixes a file descriptor leak in the error path of
> > debuginfo__init_offline_dwarf() when offline initialization fails.
> >
> > > 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);
> >
> > While this wasn't introduced by this patch, could this open() call cause a
> > regression by hanging on a FIFO?
> >
> > If the path originates from user-controlled sources, opening it without
> > O_NONBLOCK can cause the perf record session to hang indefinitely. Using
> > O_RDONLY | O_NONBLOCK would prevent this.
>
> So this review is bad and based on this prompt:
> https://github.com/masoncl/review-prompts/blob/main/kernel/subsystem/perf.md#file-io-safety-on-untrusted-paths
>
> I'm trying to get the bad prompt removed. The prompt was automatically
> generated from fixes commits when we added O_NONBLOCK to address build
> ID reading hangs, particularly for data mmaps during perf record
> synthesis. We now avoid those hangs by checking for a "regular" file
> with stat. Therefore, the advice regarding O_NONBLOCK and EINTR from
> the prompts is incorrect and does not reflect what the code base does.
>
> Side note: this is the only Sashiko feedback on this series:
> https://sashiko.dev/#/patchset/20260504081227.2203848-1-irogers%40google.com
I'll process it soon,
- Arnaldo
^ permalink raw reply [flat|nested] 46+ 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; 46+ 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] 46+ 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
2026-05-07 8:20 ` Masami Hiramatsu
0 siblings, 1 reply; 46+ 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] 46+ 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
2026-05-07 6:11 ` Namhyung Kim
10 siblings, 1 reply; 46+ 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] 46+ messages in thread
* Re: [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes
2026-05-06 0:54 ` Arnaldo Carvalho de Melo
@ 2026-05-07 6:11 ` Namhyung Kim
0 siblings, 0 replies; 46+ messages in thread
From: Namhyung Kim @ 2026-05-07 6:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Peter Zijlstra, Ingo Molnar, Jiri Olsa, Adrian Hunter,
James Clark, Zecheng Li, Masami Hiramatsu, linux-perf-users,
linux-kernel
On Tue, May 05, 2026 at 09:54:14PM -0300, Arnaldo Carvalho de Melo wrote:
> 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.
If it isn't too late,
Acked-by: Namhyung Kim <namhyung@kernel.org>
Thanks,
Namhyung
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v5 0/9] [PATCH v5 0/9] perf DWARF: Fix libdw API contract violations and crashes
2026-05-05 16:29 ` Ian Rogers
@ 2026-05-07 8:20 ` Masami Hiramatsu
0 siblings, 0 replies; 46+ messages in thread
From: Masami Hiramatsu @ 2026-05-07 8:20 UTC (permalink / raw)
To: Ian Rogers
Cc: Namhyung Kim, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark,
Zecheng Li, linux-perf-users, linux-kernel
Thanks Ian!
On Tue, 5 May 2026 09:29:13 -0700
Ian Rogers <irogers@google.com> wrote:
> 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
>
Yeah, the series looks good to me and agreed that the "critical"
issue is not a real problem.
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
for this series.
Thanks,
> > 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
> > >
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 46+ messages in thread
end of thread, other threads:[~2026-05-07 8:20 UTC | newest]
Thread overview: 46+ 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:48 ` sashiko-bot
2026-05-03 0:35 ` [PATCH v3 2/6] perf dwarf-aux: Fix libdw API contract violations Ian Rogers
2026-05-03 1:02 ` sashiko-bot
2026-05-03 0:35 ` [PATCH v3 3/6] perf libdw: " Ian Rogers
2026-05-03 1:18 ` sashiko-bot
2026-05-03 0:35 ` [PATCH v3 4/6] perf probe-finder: " Ian Rogers
2026-05-03 1:34 ` sashiko-bot
2026-05-03 0:35 ` [PATCH v3 5/6] perf annotate-data: " Ian Rogers
2026-05-03 1:44 ` sashiko-bot
2026-05-03 0:35 ` [PATCH v3 6/6] perf debuginfo: " Ian Rogers
2026-05-03 2:01 ` sashiko-bot
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 17:40 ` sashiko-bot
2026-05-03 23:36 ` Namhyung Kim
2026-05-03 17:10 ` [PATCH v4 3/6] perf libdw: " Ian Rogers
2026-05-03 18:09 ` sashiko-bot
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 10:53 ` sashiko-bot
2026-05-04 15:26 ` Ian Rogers
2026-05-04 17:54 ` Arnaldo Carvalho de Melo
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-07 8:20 ` Masami Hiramatsu
2026-05-06 0:54 ` Arnaldo Carvalho de Melo
2026-05-07 6:11 ` Namhyung Kim
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox