Commit 537ccb5d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'perf_urgent_for_v6.7_rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull perf event fix from Borislav Petkov:

 - Make sure perf event size validation is done on every event in the
   group

* tag 'perf_urgent_for_v6.7_rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  perf: Fix perf_event_validate_size()
parents 5412fed7 382c27f4
...@@ -1814,31 +1814,34 @@ static inline void perf_event__state_init(struct perf_event *event) ...@@ -1814,31 +1814,34 @@ static inline void perf_event__state_init(struct perf_event *event)
PERF_EVENT_STATE_INACTIVE; 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 entry = sizeof(u64); /* value */
int size = 0; int size = 0;
int nr = 1; int nr = 1;
if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
size += sizeof(u64); size += sizeof(u64);
if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
size += sizeof(u64); size += sizeof(u64);
if (event->attr.read_format & PERF_FORMAT_ID) if (read_format & PERF_FORMAT_ID)
entry += sizeof(u64); entry += sizeof(u64);
if (event->attr.read_format & PERF_FORMAT_LOST) if (read_format & PERF_FORMAT_LOST)
entry += sizeof(u64); entry += sizeof(u64);
if (event->attr.read_format & PERF_FORMAT_GROUP) { if (read_format & PERF_FORMAT_GROUP) {
nr += nr_siblings; nr += nr_siblings;
size += sizeof(u64); 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) static void __perf_event_header_size(struct perf_event *event, u64 sample_type)
...@@ -1888,8 +1891,9 @@ static void __perf_event_header_size(struct perf_event *event, u64 sample_type) ...@@ -1888,8 +1891,9 @@ static void __perf_event_header_size(struct perf_event *event, u64 sample_type)
*/ */
static void perf_event__header_size(struct perf_event *event) static void perf_event__header_size(struct perf_event *event)
{ {
__perf_event_read_size(event, event->read_size =
event->group_leader->nr_siblings); __perf_event_read_size(event->attr.read_format,
event->group_leader->nr_siblings);
__perf_event_header_size(event, event->attr.sample_type); __perf_event_header_size(event, event->attr.sample_type);
} }
...@@ -1920,24 +1924,35 @@ static void perf_event__id_header_size(struct perf_event *event) ...@@ -1920,24 +1924,35 @@ static void perf_event__id_header_size(struct perf_event *event)
event->id_header_size = size; 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) static bool perf_event_validate_size(struct perf_event *event)
{ {
/* struct perf_event *sibling, *group_leader = event->group_leader;
* 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);
/* if (__perf_event_read_size(event->attr.read_format,
* Sum the lot; should not exceed the 64k limit we have on records. group_leader->nr_siblings + 1) > 16*1024)
* 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)
return false; 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; return true;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment