* [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
* 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
* [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
* 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
* [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
* 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
* [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
* 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
* [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
* 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
* [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 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
* 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
* [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
* 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 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
* [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
* 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 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
* [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
* 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
* [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
* 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
* [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 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-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
* 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
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