Skip to content

Commit 2dc4b36

Browse files
pvts-matPlaidCat
authored andcommitted
perf: Fix perf_event_validate_size()
jira VULN-700 cve CVE-2023-6931 commit-author Peter Zijlstra <peterz@infradead.org> commit 382c27f 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: a723968 ("perf: Fix u16 overflows") Reported-by: Budimir Markovic <markovicbudimir@gmail.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> (cherry picked from commit 382c27f) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
1 parent 288e2d1 commit 2dc4b36

File tree

1 file changed

+38
-23
lines changed

1 file changed

+38
-23
lines changed

kernel/events/core.c

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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

18361839
static 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
*/
18811884
static 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+
*/
19151930
static 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

Comments
 (0)