diff options
| author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2026-05-23 08:06:35 -0300 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2026-05-29 11:44:11 -0300 |
| commit | 5a5c7968a7cc8e6713cf56eec5b377c6e61f0c16 (patch) | |
| tree | 97a338462b16194dc9f369d1019213ceadc69317 /tools | |
| parent | 70d684f58a9320b72f5ee8ac463c1611d3f9fc83 (diff) | |
| download | linux-next-history-5a5c7968a7cc8e6713cf56eec5b377c6e61f0c16.tar.gz | |
perf session: Bounds-check one_mmap event pointer in peek_event
perf_session__peek_event() computes an event pointer directly from
file_offset when one_mmap is active, without verifying that file_offset
and the subsequent event->header.size fall within the mapped region.
A corrupted perf.data file could cause out-of-bounds memory reads.
Add one_mmap_size to the session struct and validate both the header
and full event fit within the mmap before dereferencing.
Reported-by: sashiko-bot@kernel.org # Running on a local machine
Reviewed-by: Ian Rogers <irogers@google.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/perf/util/session.c | 29 | ||||
| -rw-r--r-- | tools/perf/util/session.h | 2 |
2 files changed, 28 insertions, 3 deletions
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0523fd243e02c..c4cd8ad6d810a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1887,12 +1887,27 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset, *event_ptr = NULL; if (session->one_mmap && !session->header.needs_swap) { - event = file_offset - session->one_mmap_offset + - session->one_mmap_addr; + u64 offset_in_mmap; + + /* Validate offset with integer arithmetic to avoid pointer UB */ + if ((u64)file_offset < session->one_mmap_offset) + return -1; + + offset_in_mmap = (u64)file_offset - session->one_mmap_offset; + + /* Use subtraction to avoid addition overflow */ + if (offset_in_mmap >= session->one_mmap_size || + session->one_mmap_size - offset_in_mmap < sizeof(struct perf_event_header)) + return -1; + + event = session->one_mmap_addr + offset_in_mmap; - /* Every event must at least contain its own header */ if (event->header.size < sizeof(struct perf_event_header)) return -1; + + /* Ensure full event is within the mmap region */ + if (session->one_mmap_size - offset_in_mmap < event->header.size) + return -1; } else { if (perf_data__is_pipe(session->data)) return -1; @@ -2560,6 +2575,14 @@ reader__mmap(struct reader *rd, struct perf_session *session) if (session->one_mmap) { session->one_mmap_addr = buf; session->one_mmap_offset = rd->file_offset; + /* + * mmap_size was set to the full file extent (data_offset + + * data_size) but file_offset was shifted forward by + * page_offset for page alignment. Reduce by page_offset + * so the bounds check reflects the file-backed portion + * of the mapping — pages beyond the file cause SIGBUS. + */ + session->one_mmap_size = rd->mmap_size - page_offset; } return 0; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index f05f0d4a6c238..d554e2a1a50ed 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -71,6 +71,8 @@ struct perf_session { void *one_mmap_addr; /** @one_mmap_offset: File offset in perf.data file when mapped. */ u64 one_mmap_offset; + /** @one_mmap_size: Size of the single mmap in bytes. */ + u64 one_mmap_size; /** @ordered_events: Used to turn unordered events into ordered ones. */ struct ordered_events ordered_events; /** @data: Optional perf data file being read from. */ |
