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
Showing the changes to kernel (1 of 2 files). Show all
Unified · Split
| @@ -1954,6 +1954,7 @@ static void perf_group_attach(struct perf_event *event) · 1953 unmodified lines | |||
| 1954 | 1954 | ||
| 1955 | list_add_tail(&event->sibling_list, &group_leader->sibling_list); | 1955 | list_add_tail(&event->sibling_list, &group_leader->sibling_list); |
| 1956 | group_leader->nr_siblings++; | 1956 | group_leader->nr_siblings++; |
| 1957 | group_leader->group_generation++; | ||
| 1957 | 1958 | ||
| 1958 | perf_event__header_size(group_leader); | 1959 | perf_event__header_size(group_leader); |
| 1959 | 1960 | ||
| @@ -2144,6 +2145,7 @@ static void perf_group_detach(struct perf_event *event) · 184 unmodified lines | |||
| 2144 | if (leader != event) { | 2145 | if (leader != event) { |
| 2145 | list_del_init(&event->sibling_list); | 2146 | list_del_init(&event->sibling_list); |
| 2146 | event->group_leader->nr_siblings--; | 2147 | event->group_leader->nr_siblings--; |
| 2148 | event->group_leader->group_generation++; | ||
| 2147 | goto out; | 2149 | goto out; |
| 2148 | } | 2150 | } |
| 2149 | 2151 | ||
| @@ -5440,7 +5442,7 @@ static int __perf_read_group_add(struct perf_event *leader, · 3290 unmodified lines | |||
| 5440 | u64 read_format, u64 *values) | 5442 | u64 read_format, u64 *values) |
| 5441 | { | 5443 | { |
| 5442 | struct perf_event_context *ctx = leader->ctx; | 5444 | struct perf_event_context *ctx = leader->ctx; |
| 5443 | struct perf_event *sub; | 5445 | struct perf_event *sub, *parent; |
| 5444 | unsigned long flags; | 5446 | unsigned long flags; |
| 5445 | int n = 1; /* skip @nr */ | 5447 | int n = 1; /* skip @nr */ |
| 5446 | int ret; | 5448 | int ret; |
| @@ -5450,6 +5452,33 @@ static int __perf_read_group_add(struct perf_event *leader, · 3 unmodified lines | |||
| 5450 | return ret; | 5452 | return ret; |
| 5451 | 5453 | ||
| 5452 | raw_spin_lock_irqsave(&ctx->lock, flags); | 5454 | raw_spin_lock_irqsave(&ctx->lock, flags); |
| 5455 | /* | ||
| 5456 | * Verify the grouping between the parent and child (inherited) | ||
| 5457 | * events is still in tact. | ||
| 5458 | * | ||
| 5459 | * Specifically: | ||
| 5460 | * - leader->ctx->lock pins leader->sibling_list | ||
| 5461 | * - parent->child_mutex pins parent->child_list | ||
| 5462 | * - parent->ctx->mutex pins parent->sibling_list | ||
| 5463 | * | ||
| 5464 | * Because parent->ctx != leader->ctx (and child_list nests inside | ||
| 5465 | * ctx->mutex), group destruction is not atomic between children, also | ||
| 5466 | * see perf_event_release_kernel(). Additionally, parent can grow the | ||
| 5467 | * group. | ||
| 5468 | * | ||
| 5469 | * Therefore it is possible to have parent and child groups in a | ||
| 5470 | * different configuration and summing over such a beast makes no sense | ||
| 5471 | * what so ever. | ||
| 5472 | * | ||
| 5473 | * Reject this. | ||
| 5474 | */ | ||
| 5475 | parent = leader->parent; | ||
| 5476 | if (parent && | ||
| 5477 | (parent->group_generation != leader->group_generation || | ||
| 5478 | parent->nr_siblings != leader->nr_siblings)) { | ||
| 5479 | ret = -ECHILD; | ||
| 5480 | goto unlock; | ||
| 5481 | } | ||
| 5453 | 5482 | ||
| 5454 | /* | 5483 | /* |
| 5455 | * Since we co-schedule groups, {enabled,running} times of siblings | 5484 | * Since we co-schedule groups, {enabled,running} times of siblings |
| @@ -5483,8 +5512,9 @@ static int __perf_read_group_add(struct perf_event *leader, · 27 unmodified lines | |||
| 5483 | values[n++] = atomic64_read(&sub->lost_samples); | 5512 | values[n++] = atomic64_read(&sub->lost_samples); |
| 5484 | } | 5513 | } |
| 5485 | 5514 | ||
| 5515 | unlock: | ||
| 5486 | raw_spin_unlock_irqrestore(&ctx->lock, flags); | 5516 | raw_spin_unlock_irqrestore(&ctx->lock, flags); |
| 5487 | return 0; | 5517 | return ret; |
| 5488 | } | 5518 | } |
| 5489 | 5519 | ||
| 5490 | static int perf_read_group(struct perf_event *event, | 5520 | static int perf_read_group(struct perf_event *event, |
| @@ -5503,10 +5533,6 @@ static int perf_read_group(struct perf_event *event, · 12 unmodified lines | |||
| 5503 | 5533 | ||
| 5504 | values[0] = 1 + leader->nr_siblings; | 5534 | values[0] = 1 + leader->nr_siblings; |
| 5505 | 5535 | ||
| 5506 | /* | ||
| 5507 | * By locking the child_mutex of the leader we effectively | ||
| 5508 | * lock the child list of all siblings.. XXX explain how. | ||
| 5509 | */ | ||
| 5510 | mutex_lock(&leader->child_mutex); | 5536 | mutex_lock(&leader->child_mutex); |
| 5511 | 5537 | ||
| 5512 | ret = __perf_read_group_add(leader, read_format, values); | 5538 | ret = __perf_read_group_add(leader, read_format, values); |
| @@ -13346,6 +13372,7 @@ static int inherit_group(struct perf_event *parent_event, · 7833 unmodified lines | |||
| 13346 | !perf_get_aux_event(child_ctr, leader)) | 13372 | !perf_get_aux_event(child_ctr, leader)) |
| 13347 | return -EINVAL; | 13373 | return -EINVAL; |
| 13348 | } | 13374 | } |
| 13375 | leader->group_generation = parent_event->group_generation; | ||
| 13349 | return 0; | 13376 | return 0; |
| 13350 | } | 13377 | } |
| 13351 | 13378 | ||