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(-)
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
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
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
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
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 >
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 > >
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>
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
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
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
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
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
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
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
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
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
© 2016 - 2026 Red Hat, Inc.