[PATCH v7 1/6] perf ui hists: Fix stack use-after-return in symbol_filter_str

From: Ian Rogers

Date: Thu Jul 23 2026 - 01:11:58 EST


In evsel__hists_browse(), the local stack array 'buf' is assigned
directly to the persistent 'hists->symbol_filter_str' pointer. When the
browser returns or is exited, this dangling pointer remains active in
the 'hists' struct and is read asynchronously by the perf top background
timer, triggering a stack use-after-return.

Fix it by properly duplicating the input string using strdup(), safely
invoking zfree() to prevent memory leaks when overwriting, and cleanly
resetting and freeing the symbol filter string upon exiting the browser.

Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/builtin-report.c | 7 +++++--
tools/perf/ui/browsers/hists.c | 5 ++++-
tools/perf/util/hist.c | 1 +
3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 10db1e5f1e6c..60d1f166629e 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -726,8 +726,11 @@ static int report__collapse_hists(struct report *rep)
evlist__for_each_entry(rep->session->evlist, pos) {
struct hists *hists = evsel__hists(pos);

- if (pos->core.idx == 0)
- hists->symbol_filter_str = rep->symbol_filter_str;
+ if (pos->core.idx == 0) {
+ hists->symbol_filter_str =
+ rep->symbol_filter_str ?
+ strdup(rep->symbol_filter_str) : NULL;
+ }

hists->socket_filter = rep->socket_filter;

diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index b6002724bc3a..be8a6b169722 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -3220,7 +3220,10 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
"To remove the filter later, press / + ENTER.",
buf, "ENTER: OK, ESC: Cancel",
delay_secs * 2) == K_ENTER) {
- hists->symbol_filter_str = *buf ? buf : NULL;
+ char *new_filter = *buf ? strdup(buf) : NULL;
+
+ zfree(&hists->symbol_filter_str);
+ hists->symbol_filter_str = new_filter;
hists__filter_by_symbol(hists);
hist_browser__reset(browser);
}
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index c93915625ee7..443694926f1a 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -3056,6 +3056,7 @@ static void hists_evsel__exit(struct evsel *evsel)
struct perf_hpp_list_node *node, *tmp;

hists__delete_all_entries(hists);
+ zfree(&hists->symbol_filter_str);
zfree(&hists->mem_stat_types);
zfree(&hists->mem_stat_total);

--
2.55.0.229.g6434b31f56-goog