mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	perf: Fix perf_event_validate_size()
Budimir noted that perf_event_validate_size() only checks the size of
the newly added event, even though the sizes of all existing events
can also change due to not all events having the same read_format.
When we attach the new event, perf_group_attach(), we do re-compute
the size for all events.
Fixes: a723968c0e ("perf: Fix u16 overflows")
Reported-by: Budimir Markovic <markovicbudimir@gmail.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
			
			
This commit is contained in:
		
							parent
							
								
									2cc14f52ae
								
							
						
					
					
						commit
						382c27f4ed
					
				
					 1 changed files with 38 additions and 23 deletions
				
			
		| 
						 | 
					@ -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,7 +1891,8 @@ 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 =
 | 
				
			||||||
 | 
							__perf_event_read_size(event->attr.read_format,
 | 
				
			||||||
				       event->group_leader->nr_siblings);
 | 
									       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)
 | 
				
			||||||
	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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue