Skip to content

Commit e8e4d75

Browse files
pvts-matPlaidCat
authored andcommitted
perf: Disallow mis-matched inherited group reads
jira VULN-7623 cve CVE-2023-5717 commit-author Peter Zijlstra <[email protected]> commit 32671e3 upstream-diff The mainline fix 32671e3 adds a new `group_generation' field to the `perf_event' struct. This breaks CBR 7.9 kABI. The new field was preserved, but moved to the end of the struct and wrapped in the `RH_KABI_EXTEND' macro. It can be assumed the kABI in this particular case is preserved based on the fact that there are already plenty of `RH_KABI_EXTEND(...)' fields at the end which could not have been added if the premise was false. Because group consistency is non-atomic between parent (filedesc) and children (inherited) events, it is possible for PERF_FORMAT_GROUP read() to try and sum non-matching counter groups -- with non-sensical results. Add group_generation to distinguish the case where a parent group removes and adds an event and thus has the same number, but a different configuration of events as inherited groups. This became a problem when commit fa8c269 ("perf/core: Invert perf_read_group() loops") flipped the order of child_list and sibling_list. Previously it would iterate the group (sibling_list) first, and for each sibling traverse the child_list. In this order, only the group composition of the parent is relevant. By flipping the order the group composition of the child (inherited) events becomes an issue and the mis-match in group composition becomes evident. That said; even prior to this commit, while reading of a group that is not equally inherited was not broken, it still made no sense. (Ab)use ECHILD as error return to indicate issues with child process group composition. Fixes: fa8c269 ("perf/core: Invert perf_read_group() loops") Reported-by: Budimir Markovic <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lkml.kernel.org/r/[email protected] (cherry picked from commit 32671e3) Signed-off-by: Marcin Wcisło <[email protected]>
1 parent 4829e66 commit e8e4d75

File tree

2 files changed

+35
-6
lines changed

2 files changed

+35
-6
lines changed

include/linux/perf_event.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,8 @@ struct perf_event {
611611
#endif
612612
RH_KABI_EXTEND(unsigned long rcu_batches)
613613
RH_KABI_EXTEND(int rcu_pending)
614+
615+
RH_KABI_EXTEND(unsigned int group_generation)
614616
#endif /* CONFIG_PERF_EVENTS */
615617
};
616618

kernel/events/core.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,7 @@ static void perf_group_attach(struct perf_event *event)
17731773

17741774
list_add_tail(&event->group_entry, &group_leader->sibling_list);
17751775
group_leader->nr_siblings++;
1776+
group_leader->group_generation++;
17761777

17771778
perf_event__header_size(group_leader);
17781779

@@ -1858,6 +1859,7 @@ static void perf_group_detach(struct perf_event *event)
18581859
if (event->group_leader != event) {
18591860
list_del_init(&event->group_entry);
18601861
event->group_leader->nr_siblings--;
1862+
event->group_leader->group_generation++;
18611863
goto out;
18621864
}
18631865

@@ -4561,7 +4563,7 @@ static int __perf_read_group_add(struct perf_event *leader,
45614563
u64 read_format, u64 *values)
45624564
{
45634565
struct perf_event_context *ctx = leader->ctx;
4564-
struct perf_event *sub;
4566+
struct perf_event *sub, *parent;
45654567
unsigned long flags;
45664568
int n = 1; /* skip @nr */
45674569
int ret;
@@ -4571,6 +4573,33 @@ static int __perf_read_group_add(struct perf_event *leader,
45714573
return ret;
45724574

45734575
raw_spin_lock_irqsave(&ctx->lock, flags);
4576+
/*
4577+
* Verify the grouping between the parent and child (inherited)
4578+
* events is still in tact.
4579+
*
4580+
* Specifically:
4581+
* - leader->ctx->lock pins leader->sibling_list
4582+
* - parent->child_mutex pins parent->child_list
4583+
* - parent->ctx->mutex pins parent->sibling_list
4584+
*
4585+
* Because parent->ctx != leader->ctx (and child_list nests inside
4586+
* ctx->mutex), group destruction is not atomic between children, also
4587+
* see perf_event_release_kernel(). Additionally, parent can grow the
4588+
* group.
4589+
*
4590+
* Therefore it is possible to have parent and child groups in a
4591+
* different configuration and summing over such a beast makes no sense
4592+
* what so ever.
4593+
*
4594+
* Reject this.
4595+
*/
4596+
parent = leader->parent;
4597+
if (parent &&
4598+
(parent->group_generation != leader->group_generation ||
4599+
parent->nr_siblings != leader->nr_siblings)) {
4600+
ret = -ECHILD;
4601+
goto unlock;
4602+
}
45744603

45754604
/*
45764605
* Since we co-schedule groups, {enabled,running} times of siblings
@@ -4600,8 +4629,9 @@ static int __perf_read_group_add(struct perf_event *leader,
46004629
values[n++] = primary_event_id(sub);
46014630
}
46024631

4632+
unlock:
46034633
raw_spin_unlock_irqrestore(&ctx->lock, flags);
4604-
return 0;
4634+
return ret;
46054635
}
46064636

46074637
static int perf_read_group(struct perf_event *event,
@@ -4620,10 +4650,6 @@ static int perf_read_group(struct perf_event *event,
46204650

46214651
values[0] = 1 + leader->nr_siblings;
46224652

4623-
/*
4624-
* By locking the child_mutex of the leader we effectively
4625-
* lock the child list of all siblings.. XXX explain how.
4626-
*/
46274653
mutex_lock(&leader->child_mutex);
46284654

46294655
ret = __perf_read_group_add(leader, read_format, values);
@@ -11005,6 +11031,7 @@ static int inherit_group(struct perf_event *parent_event,
1100511031
if (IS_ERR(child_ctr))
1100611032
return PTR_ERR(child_ctr);
1100711033
}
11034+
leader->group_generation = parent_event->group_generation;
1100811035
return 0;
1100911036
}
1101011037

0 commit comments

Comments
 (0)