@@ -1806,31 +1806,34 @@ static inline void perf_event__state_init(struct perf_event *event)
18061806 PERF_EVENT_STATE_INACTIVE ;
18071807}
18081808
1809- static void __perf_event_read_size (struct perf_event * event , int nr_siblings )
1809+ static int __perf_event_read_size (u64 read_format , int nr_siblings )
18101810{
18111811 int entry = sizeof (u64 ); /* value */
18121812 int size = 0 ;
18131813 int nr = 1 ;
18141814
1815- if (event -> attr . read_format & PERF_FORMAT_TOTAL_TIME_ENABLED )
1815+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED )
18161816 size += sizeof (u64 );
18171817
1818- if (event -> attr . read_format & PERF_FORMAT_TOTAL_TIME_RUNNING )
1818+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING )
18191819 size += sizeof (u64 );
18201820
1821- if (event -> attr . read_format & PERF_FORMAT_ID )
1821+ if (read_format & PERF_FORMAT_ID )
18221822 entry += sizeof (u64 );
18231823
1824- if (event -> attr . read_format & PERF_FORMAT_LOST )
1824+ if (read_format & PERF_FORMAT_LOST )
18251825 entry += sizeof (u64 );
18261826
1827- if (event -> attr . read_format & PERF_FORMAT_GROUP ) {
1827+ if (read_format & PERF_FORMAT_GROUP ) {
18281828 nr += nr_siblings ;
18291829 size += sizeof (u64 );
18301830 }
18311831
1832- size += entry * nr ;
1833- event -> read_size = size ;
1832+ /*
1833+ * Since perf_event_validate_size() limits this to 16k and inhibits
1834+ * adding more siblings, this will never overflow.
1835+ */
1836+ return size + nr * entry ;
18341837}
18351838
18361839static void __perf_event_header_size (struct perf_event * event , u64 sample_type )
@@ -1880,8 +1883,9 @@ static void __perf_event_header_size(struct perf_event *event, u64 sample_type)
18801883 */
18811884static void perf_event__header_size (struct perf_event * event )
18821885{
1883- __perf_event_read_size (event ,
1884- event -> group_leader -> nr_siblings );
1886+ event -> read_size =
1887+ __perf_event_read_size (event -> attr .read_format ,
1888+ event -> group_leader -> nr_siblings );
18851889 __perf_event_header_size (event , event -> attr .sample_type );
18861890}
18871891
@@ -1912,24 +1916,35 @@ static void perf_event__id_header_size(struct perf_event *event)
19121916 event -> id_header_size = size ;
19131917}
19141918
1919+ /*
1920+ * Check that adding an event to the group does not result in anybody
1921+ * overflowing the 64k event limit imposed by the output buffer.
1922+ *
1923+ * Specifically, check that the read_size for the event does not exceed 16k,
1924+ * read_size being the one term that grows with groups size. Since read_size
1925+ * depends on per-event read_format, also (re)check the existing events.
1926+ *
1927+ * This leaves 48k for the constant size fields and things like callchains,
1928+ * branch stacks and register sets.
1929+ */
19151930static bool perf_event_validate_size (struct perf_event * event )
19161931{
1917- /*
1918- * The values computed here will be over-written when we actually
1919- * attach the event.
1920- */
1921- __perf_event_read_size (event , event -> group_leader -> nr_siblings + 1 );
1922- __perf_event_header_size (event , event -> attr .sample_type & ~PERF_SAMPLE_READ );
1923- perf_event__id_header_size (event );
1932+ struct perf_event * sibling , * group_leader = event -> group_leader ;
19241933
1925- /*
1926- * Sum the lot; should not exceed the 64k limit we have on records.
1927- * Conservative limit to allow for callchains and other variable fields.
1928- */
1929- if (event -> read_size + event -> header_size +
1930- event -> id_header_size + sizeof (struct perf_event_header ) >= 16 * 1024 )
1934+ if (__perf_event_read_size (event -> attr .read_format ,
1935+ group_leader -> nr_siblings + 1 ) > 16 * 1024 )
19311936 return false;
19321937
1938+ if (__perf_event_read_size (group_leader -> attr .read_format ,
1939+ group_leader -> nr_siblings + 1 ) > 16 * 1024 )
1940+ return false;
1941+
1942+ for_each_sibling_event (sibling , group_leader ) {
1943+ if (__perf_event_read_size (sibling -> attr .read_format ,
1944+ group_leader -> nr_siblings + 1 ) > 16 * 1024 )
1945+ return false;
1946+ }
1947+
19331948 return true;
19341949}
19351950
0 commit comments