summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorIan Rogers <irogers@google.com>2026-04-04 09:05:52 +0300
committerNamhyung Kim <namhyung@kernel.org>2026-04-06 09:23:33 +0300
commitc9ef786c0970991578397043f1c819229e2b7197 (patch)
treeb09d783b040faabdacf4c1a833bfda6e2c5c506e /tools
parentaeae075a0352eb6ab363fb1910f209eaa296a175 (diff)
downloadlinux-c9ef786c0970991578397043f1c819229e2b7197.tar.xz
perf cgroup: Update metric leader in evlist__expand_cgroup
When the evlist is expanded the metric leader wasn't being updated. As the original evsel is deleted this creates a use-after-free in stat-shadow's prepare_metric. This was detected running the "perf stat --bpf-counters --for-each-cgroup test" with sanitizers. The change itself puts the copied evsel into the priv field (known unused because of evsel__clone use) and then in a second pass over the list updates the copied values using the priv pointer. Fixes: d1c5a0e86a4e ("perf stat: Add --for-each-cgroup option") Signed-off-by: Ian Rogers <irogers@google.com> Acked-by: Sun Jian <sun.jian.kdev@gmail.com> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/util/cgroup.c30
1 files changed, 23 insertions, 7 deletions
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
index 040eb75f0804..1b5664d1481f 100644
--- a/tools/perf/util/cgroup.c
+++ b/tools/perf/util/cgroup.c
@@ -417,7 +417,6 @@ static bool has_pattern_string(const char *str)
int evlist__expand_cgroup(struct evlist *evlist, const char *str, bool open_cgroup)
{
struct evlist *orig_list, *tmp_list;
- struct evsel *pos, *evsel, *leader;
struct rblist orig_metric_events;
struct cgroup *cgrp = NULL;
struct cgroup_name *cn;
@@ -452,6 +451,7 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, bool open_cgro
goto out_err;
list_for_each_entry(cn, &cgroup_list, list) {
+ struct evsel *pos;
char *name;
if (!cn->used)
@@ -467,21 +467,37 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, bool open_cgro
if (cgrp == NULL)
continue;
- leader = NULL;
+ /* copy the list and set to the new cgroup. */
evlist__for_each_entry(orig_list, pos) {
- evsel = evsel__clone(/*dest=*/NULL, pos);
+ struct evsel *evsel = evsel__clone(/*dest=*/NULL, pos);
+
if (evsel == NULL)
goto out_err;
+ /* stash the copy during the copying. */
+ pos->priv = evsel;
cgroup__put(evsel->cgrp);
evsel->cgrp = cgroup__get(cgrp);
- if (evsel__is_group_leader(pos))
- leader = evsel;
- evsel__set_leader(evsel, leader);
-
evlist__add(tmp_list, evsel);
}
+ /* update leader information using stashed pointer to copy. */
+ evlist__for_each_entry(orig_list, pos) {
+ struct evsel *evsel = pos->priv;
+
+ if (evsel__leader(pos))
+ evsel__set_leader(evsel, evsel__leader(pos)->priv);
+
+ if (pos->metric_leader)
+ evsel->metric_leader = pos->metric_leader->priv;
+
+ if (pos->first_wildcard_match)
+ evsel->first_wildcard_match = pos->first_wildcard_match->priv;
+ }
+ /* the stashed copy is no longer used. */
+ evlist__for_each_entry(orig_list, pos)
+ pos->priv = NULL;
+
/* cgroup__new() has a refcount, release it here */
cgroup__put(cgrp);
nr_cgroups++;