Skip to content

Commit 64bff3c

Browse files
ciq-sahlbergPlaidCat
authored andcommitted
perf: Fix perf_event_validate_size()
jira SECO-54 cve CVE-2023-6931 commit 382c27f upstream-diff trivial diff against upstream as we do not have PERF_FORMAT_LOST(119a784) in 8.8. PERF_FORMAT_LOST is a counter that was exposed to userspace post 8.8 so we do not need it backported. 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: Ronnie Sahlberg <rsahlberg@ciq.com>
1 parent e7650e3 commit 64bff3c

File tree

1 file changed

+37
-22
lines changed

1 file changed

+37
-22
lines changed

kernel/events/core.c

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,28 +1848,31 @@ static inline void perf_event__state_init(struct perf_event *event)
18481848
PERF_EVENT_STATE_INACTIVE;
18491849
}
18501850

1851-
static void __perf_event_read_size(struct perf_event *event, int nr_siblings)
1851+
static int __perf_event_read_size(u64 read_format, int nr_siblings)
18521852
{
18531853
int entry = sizeof(u64); /* value */
18541854
int size = 0;
18551855
int nr = 1;
18561856

1857-
if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
1857+
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
18581858
size += sizeof(u64);
18591859

1860-
if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
1860+
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
18611861
size += sizeof(u64);
18621862

1863-
if (event->attr.read_format & PERF_FORMAT_ID)
1863+
if (read_format & PERF_FORMAT_ID)
18641864
entry += sizeof(u64);
18651865

1866-
if (event->attr.read_format & PERF_FORMAT_GROUP) {
1866+
if (read_format & PERF_FORMAT_GROUP) {
18671867
nr += nr_siblings;
18681868
size += sizeof(u64);
18691869
}
18701870

1871-
size += entry * nr;
1872-
event->read_size = size;
1871+
/*
1872+
* Since perf_event_validate_size() limits this to 16k and inhibits
1873+
* adding more siblings, this will never overflow.
1874+
*/
1875+
return size + nr * entry;
18731876
}
18741877

18751878
static void __perf_event_header_size(struct perf_event *event, u64 sample_type)
@@ -1919,8 +1922,9 @@ static void __perf_event_header_size(struct perf_event *event, u64 sample_type)
19191922
*/
19201923
static void perf_event__header_size(struct perf_event *event)
19211924
{
1922-
__perf_event_read_size(event,
1923-
event->group_leader->nr_siblings);
1925+
event->read_size =
1926+
__perf_event_read_size(event->attr.read_format,
1927+
event->group_leader->nr_siblings);
19241928
__perf_event_header_size(event, event->attr.sample_type);
19251929
}
19261930

@@ -1951,24 +1955,35 @@ static void perf_event__id_header_size(struct perf_event *event)
19511955
event->id_header_size = size;
19521956
}
19531957

1958+
/*
1959+
* Check that adding an event to the group does not result in anybody
1960+
* overflowing the 64k event limit imposed by the output buffer.
1961+
*
1962+
* Specifically, check that the read_size for the event does not exceed 16k,
1963+
* read_size being the one term that grows with groups size. Since read_size
1964+
* depends on per-event read_format, also (re)check the existing events.
1965+
*
1966+
* This leaves 48k for the constant size fields and things like callchains,
1967+
* branch stacks and register sets.
1968+
*/
19541969
static bool perf_event_validate_size(struct perf_event *event)
19551970
{
1956-
/*
1957-
* The values computed here will be over-written when we actually
1958-
* attach the event.
1959-
*/
1960-
__perf_event_read_size(event, event->group_leader->nr_siblings + 1);
1961-
__perf_event_header_size(event, event->attr.sample_type & ~PERF_SAMPLE_READ);
1962-
perf_event__id_header_size(event);
1971+
struct perf_event *sibling, *group_leader = event->group_leader;
19631972

1964-
/*
1965-
* Sum the lot; should not exceed the 64k limit we have on records.
1966-
* Conservative limit to allow for callchains and other variable fields.
1967-
*/
1968-
if (event->read_size + event->header_size +
1969-
event->id_header_size + sizeof(struct perf_event_header) >= 16*1024)
1973+
if (__perf_event_read_size(event->attr.read_format,
1974+
group_leader->nr_siblings + 1) > 16*1024)
19701975
return false;
19711976

1977+
if (__perf_event_read_size(group_leader->attr.read_format,
1978+
group_leader->nr_siblings + 1) > 16*1024)
1979+
return false;
1980+
1981+
for_each_sibling_event(sibling, group_leader) {
1982+
if (__perf_event_read_size(sibling->attr.read_format,
1983+
group_leader->nr_siblings + 1) > 16*1024)
1984+
return false;
1985+
}
1986+
19721987
return true;
19731988
}
19741989

0 commit comments

Comments
 (0)