Merge tag 'perf-urgent-2023-10-21' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull perf events fix from Ingo Molnar:
"Fix group event semantics"

* tag 'perf-urgent-2023-10-21' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
perf: Disallow mis-matched inherited group reads

+34 -6
+1
include/linux/perf_event.h
··· 704 704 /* The cumulative AND of all event_caps for events in this group. */ 705 705 int group_caps; 706 706 707 + unsigned int group_generation; 707 708 struct perf_event *group_leader; 708 709 /* 709 710 * event->pmu will always point to pmu in which this event belongs.
+33 -6
kernel/events/core.c
··· 1954 1954 1955 1955 list_add_tail(&event->sibling_list, &group_leader->sibling_list); 1956 1956 group_leader->nr_siblings++; 1957 + group_leader->group_generation++; 1957 1958 1958 1959 perf_event__header_size(group_leader); 1959 1960 ··· 2145 2144 if (leader != event) { 2146 2145 list_del_init(&event->sibling_list); 2147 2146 event->group_leader->nr_siblings--; 2147 + event->group_leader->group_generation++; 2148 2148 goto out; 2149 2149 } 2150 2150 ··· 5442 5440 u64 read_format, u64 *values) 5443 5441 { 5444 5442 struct perf_event_context *ctx = leader->ctx; 5445 - struct perf_event *sub; 5443 + struct perf_event *sub, *parent; 5446 5444 unsigned long flags; 5447 5445 int n = 1; /* skip @nr */ 5448 5446 int ret; ··· 5452 5450 return ret; 5453 5451 5454 5452 raw_spin_lock_irqsave(&ctx->lock, flags); 5453 + /* 5454 + * Verify the grouping between the parent and child (inherited) 5455 + * events is still in tact. 5456 + * 5457 + * Specifically: 5458 + * - leader->ctx->lock pins leader->sibling_list 5459 + * - parent->child_mutex pins parent->child_list 5460 + * - parent->ctx->mutex pins parent->sibling_list 5461 + * 5462 + * Because parent->ctx != leader->ctx (and child_list nests inside 5463 + * ctx->mutex), group destruction is not atomic between children, also 5464 + * see perf_event_release_kernel(). Additionally, parent can grow the 5465 + * group. 5466 + * 5467 + * Therefore it is possible to have parent and child groups in a 5468 + * different configuration and summing over such a beast makes no sense 5469 + * what so ever. 5470 + * 5471 + * Reject this. 5472 + */ 5473 + parent = leader->parent; 5474 + if (parent && 5475 + (parent->group_generation != leader->group_generation || 5476 + parent->nr_siblings != leader->nr_siblings)) { 5477 + ret = -ECHILD; 5478 + goto unlock; 5479 + } 5455 5480 5456 5481 /* 5457 5482 * Since we co-schedule groups, {enabled,running} times of siblings ··· 5512 5483 values[n++] = atomic64_read(&sub->lost_samples); 5513 5484 } 5514 5485 5486 + unlock: 5515 5487 raw_spin_unlock_irqrestore(&ctx->lock, flags); 5516 - return 0; 5488 + return ret; 5517 5489 } 5518 5490 5519 5491 static int perf_read_group(struct perf_event *event, ··· 5533 5503 5534 5504 values[0] = 1 + leader->nr_siblings; 5535 5505 5536 - /* 5537 - * By locking the child_mutex of the leader we effectively 5538 - * lock the child list of all siblings.. XXX explain how. 5539 - */ 5540 5506 mutex_lock(&leader->child_mutex); 5541 5507 5542 5508 ret = __perf_read_group_add(leader, read_format, values); ··· 13372 13346 !perf_get_aux_event(child_ctr, leader)) 13373 13347 return -EINVAL; 13374 13348 } 13349 + leader->group_generation = parent_event->group_generation; 13375 13350 return 0; 13376 13351 } 13377 13352