Skip to content

Commit 2db35db

Browse files
captain5050gregkh
authored andcommitted
perf cgroup: Update metric leader in evlist__expand_cgroup
[ Upstream commit c9ef786 ] 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: d1c5a0e ("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> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent e21bac1 commit 2db35db

1 file changed

Lines changed: 23 additions & 7 deletions

File tree

tools/perf/util/cgroup.c

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,6 @@ static bool has_pattern_string(const char *str)
416416
int evlist__expand_cgroup(struct evlist *evlist, const char *str, bool open_cgroup)
417417
{
418418
struct evlist *orig_list, *tmp_list;
419-
struct evsel *pos, *evsel, *leader;
420419
struct rblist orig_metric_events;
421420
struct cgroup *cgrp = NULL;
422421
struct cgroup_name *cn;
@@ -451,6 +450,7 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, bool open_cgro
451450
goto out_err;
452451

453452
list_for_each_entry(cn, &cgroup_list, list) {
453+
struct evsel *pos;
454454
char *name;
455455

456456
if (!cn->used)
@@ -466,21 +466,37 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, bool open_cgro
466466
if (cgrp == NULL)
467467
continue;
468468

469-
leader = NULL;
469+
/* copy the list and set to the new cgroup. */
470470
evlist__for_each_entry(orig_list, pos) {
471-
evsel = evsel__clone(/*dest=*/NULL, pos);
471+
struct evsel *evsel = evsel__clone(/*dest=*/NULL, pos);
472+
472473
if (evsel == NULL)
473474
goto out_err;
474475

476+
/* stash the copy during the copying. */
477+
pos->priv = evsel;
475478
cgroup__put(evsel->cgrp);
476479
evsel->cgrp = cgroup__get(cgrp);
477480

478-
if (evsel__is_group_leader(pos))
479-
leader = evsel;
480-
evsel__set_leader(evsel, leader);
481-
482481
evlist__add(tmp_list, evsel);
483482
}
483+
/* update leader information using stashed pointer to copy. */
484+
evlist__for_each_entry(orig_list, pos) {
485+
struct evsel *evsel = pos->priv;
486+
487+
if (evsel__leader(pos))
488+
evsel__set_leader(evsel, evsel__leader(pos)->priv);
489+
490+
if (pos->metric_leader)
491+
evsel->metric_leader = pos->metric_leader->priv;
492+
493+
if (pos->first_wildcard_match)
494+
evsel->first_wildcard_match = pos->first_wildcard_match->priv;
495+
}
496+
/* the stashed copy is no longer used. */
497+
evlist__for_each_entry(orig_list, pos)
498+
pos->priv = NULL;
499+
484500
/* cgroup__new() has a refcount, release it here */
485501
cgroup__put(cgrp);
486502
nr_cgroups++;

0 commit comments

Comments
 (0)