@@ -1814,31 +1814,34 @@ static inline void perf_event__state_init(struct perf_event *event)
18141814 PERF_EVENT_STATE_INACTIVE ;
18151815}
18161816
1817- static void __perf_event_read_size (struct perf_event * event , int nr_siblings )
1817+ static int __perf_event_read_size (u64 read_format , int nr_siblings )
18181818{
18191819 int entry = sizeof (u64 ); /* value */
18201820 int size = 0 ;
18211821 int nr = 1 ;
18221822
1823- if (event -> attr . read_format & PERF_FORMAT_TOTAL_TIME_ENABLED )
1823+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED )
18241824 size += sizeof (u64 );
18251825
1826- if (event -> attr . read_format & PERF_FORMAT_TOTAL_TIME_RUNNING )
1826+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING )
18271827 size += sizeof (u64 );
18281828
1829- if (event -> attr . read_format & PERF_FORMAT_ID )
1829+ if (read_format & PERF_FORMAT_ID )
18301830 entry += sizeof (u64 );
18311831
1832- if (event -> attr . read_format & PERF_FORMAT_LOST )
1832+ if (read_format & PERF_FORMAT_LOST )
18331833 entry += sizeof (u64 );
18341834
1835- if (event -> attr . read_format & PERF_FORMAT_GROUP ) {
1835+ if (read_format & PERF_FORMAT_GROUP ) {
18361836 nr += nr_siblings ;
18371837 size += sizeof (u64 );
18381838 }
18391839
1840- size += entry * nr ;
1841- event -> read_size = size ;
1840+ /*
1841+ * Since perf_event_validate_size() limits this to 16k and inhibits
1842+ * adding more siblings, this will never overflow.
1843+ */
1844+ return size + nr * entry ;
18421845}
18431846
18441847static 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)
18881891 */
18891892static void perf_event__header_size (struct perf_event * event )
18901893{
1891- __perf_event_read_size (event ,
1892- event -> group_leader -> nr_siblings );
1894+ event -> read_size =
1895+ __perf_event_read_size (event -> attr .read_format ,
1896+ event -> group_leader -> nr_siblings );
18931897 __perf_event_header_size (event , event -> attr .sample_type );
18941898}
18951899
@@ -1920,24 +1924,35 @@ static void perf_event__id_header_size(struct perf_event *event)
19201924 event -> id_header_size = size ;
19211925}
19221926
1927+ /*
1928+ * Check that adding an event to the group does not result in anybody
1929+ * overflowing the 64k event limit imposed by the output buffer.
1930+ *
1931+ * Specifically, check that the read_size for the event does not exceed 16k,
1932+ * read_size being the one term that grows with groups size. Since read_size
1933+ * depends on per-event read_format, also (re)check the existing events.
1934+ *
1935+ * This leaves 48k for the constant size fields and things like callchains,
1936+ * branch stacks and register sets.
1937+ */
19231938static bool perf_event_validate_size (struct perf_event * event )
19241939{
1925- /*
1926- * The values computed here will be over-written when we actually
1927- * attach the event.
1928- */
1929- __perf_event_read_size (event , event -> group_leader -> nr_siblings + 1 );
1930- __perf_event_header_size (event , event -> attr .sample_type & ~PERF_SAMPLE_READ );
1931- perf_event__id_header_size (event );
1940+ struct perf_event * sibling , * group_leader = event -> group_leader ;
19321941
1933- /*
1934- * Sum the lot; should not exceed the 64k limit we have on records.
1935- * Conservative limit to allow for callchains and other variable fields.
1936- */
1937- if (event -> read_size + event -> header_size +
1938- event -> id_header_size + sizeof (struct perf_event_header ) >= 16 * 1024 )
1942+ if (__perf_event_read_size (event -> attr .read_format ,
1943+ group_leader -> nr_siblings + 1 ) > 16 * 1024 )
19391944 return false;
19401945
1946+ if (__perf_event_read_size (group_leader -> attr .read_format ,
1947+ group_leader -> nr_siblings + 1 ) > 16 * 1024 )
1948+ return false;
1949+
1950+ for_each_sibling_event (sibling , group_leader ) {
1951+ if (__perf_event_read_size (sibling -> attr .read_format ,
1952+ group_leader -> nr_siblings + 1 ) > 16 * 1024 )
1953+ return false;
1954+ }
1955+
19411956 return true;
19421957}
19431958
0 commit comments