diff options
author | Greg Kroah-Hartman <gregkh@google.com> | 2024-02-01 20:18:10 +0000 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@google.com> | 2024-02-02 03:33:45 +0000 |
commit | 01a4672abf9e53ea0286ddcd7e4da46d70608c25 (patch) | |
tree | dc3cf20581eae7e742c1fa724426910cc86206e6 | |
parent | 5a194b81e47ab2ab5de62e13f58671352dcf204c (diff) | |
download | common-01a4672abf9e53ea0286ddcd7e4da46d70608c25.tar.gz |
Reapply "perf: Fix perf_event_validate_size()"ASB-2024-03-05_4.19-stableASB-2024-02-05_4.19-stable
This reverts commit aa6c8427d18d708bf61b59b1480b62e0cf8c3327 which is
commit 382c27f4ed28f803b1f1473ac2d8db0afc795a1b upstream.
Bug: 316932704
Change-Id: I69bfbf5180f57592ce285ea14f6f7b7c1b2b0f8f
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
-rw-r--r-- | kernel/events/core.c | 59 |
1 files changed, 37 insertions, 22 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index 5fbb421dcd1f..734b6bb2fe11 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1710,28 +1710,31 @@ static inline void perf_event__state_init(struct perf_event *event) PERF_EVENT_STATE_INACTIVE; } -static void __perf_event_read_size(struct perf_event *event, int nr_siblings) +static int __perf_event_read_size(u64 read_format, int nr_siblings) { int entry = sizeof(u64); /* value */ int size = 0; int nr = 1; - if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) size += sizeof(u64); - if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) size += sizeof(u64); - if (event->attr.read_format & PERF_FORMAT_ID) + if (read_format & PERF_FORMAT_ID) entry += sizeof(u64); - if (event->attr.read_format & PERF_FORMAT_GROUP) { + if (read_format & PERF_FORMAT_GROUP) { nr += nr_siblings; size += sizeof(u64); } - size += entry * nr; - event->read_size = size; + /* + * Since perf_event_validate_size() limits this to 16k and inhibits + * adding more siblings, this will never overflow. + */ + return size + nr * entry; } static void __perf_event_header_size(struct perf_event *event, u64 sample_type) @@ -1772,8 +1775,9 @@ static void __perf_event_header_size(struct perf_event *event, u64 sample_type) */ static void perf_event__header_size(struct perf_event *event) { - __perf_event_read_size(event, - event->group_leader->nr_siblings); + event->read_size = + __perf_event_read_size(event->attr.read_format, + event->group_leader->nr_siblings); __perf_event_header_size(event, event->attr.sample_type); } @@ -1804,24 +1808,35 @@ static void perf_event__id_header_size(struct perf_event *event) event->id_header_size = size; } +/* + * Check that adding an event to the group does not result in anybody + * overflowing the 64k event limit imposed by the output buffer. + * + * Specifically, check that the read_size for the event does not exceed 16k, + * read_size being the one term that grows with groups size. Since read_size + * depends on per-event read_format, also (re)check the existing events. + * + * This leaves 48k for the constant size fields and things like callchains, + * branch stacks and register sets. + */ static bool perf_event_validate_size(struct perf_event *event) { - /* - * The values computed here will be over-written when we actually - * attach the event. - */ - __perf_event_read_size(event, event->group_leader->nr_siblings + 1); - __perf_event_header_size(event, event->attr.sample_type & ~PERF_SAMPLE_READ); - perf_event__id_header_size(event); + struct perf_event *sibling, *group_leader = event->group_leader; - /* - * Sum the lot; should not exceed the 64k limit we have on records. - * Conservative limit to allow for callchains and other variable fields. - */ - if (event->read_size + event->header_size + - event->id_header_size + sizeof(struct perf_event_header) >= 16*1024) + if (__perf_event_read_size(event->attr.read_format, + group_leader->nr_siblings + 1) > 16*1024) return false; + if (__perf_event_read_size(group_leader->attr.read_format, + group_leader->nr_siblings + 1) > 16*1024) + return false; + + for_each_sibling_event(sibling, group_leader) { + if (__perf_event_read_size(sibling->attr.read_format, + group_leader->nr_siblings + 1) > 16*1024) + return false; + } + return true; } |