]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
perf session: Bounds-check one_mmap event pointer in peek_event
authorArnaldo Carvalho de Melo <acme@redhat.com>
Sat, 23 May 2026 11:06:35 +0000 (08:06 -0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 29 May 2026 14:44:11 +0000 (11:44 -0300)
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:claude-opus-4.6-1m
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/session.c
tools/perf/util/session.h

index 0523fd243e02c09bcf69372d23e08e972db7e352..c4cd8ad6d810a74ceb44309a181f314428a537eb 100644 (file)
@@ -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;
index f05f0d4a6c238dc8ead192cdf8176d2aaa5de301..d554e2a1a50ed3048e8ab97cdf37f560deaad015 100644 (file)
@@ -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. */