perf session: Fix decompression of PERF_RECORD_COMPRESSED records
BugLink: https://bugs.launchpad.net/bugs/1858428 [ Upstream commitbb1835a3b8
] Avoid termination of trace loading in case the last record in the decompressed buffer partly resides in the following mmaped PERF_RECORD_COMPRESSED record. In this case NULL value returned by fetch_mmaped_event() means to proceed to the next mmaped record then decompress it and load compressed events. The issue can be reproduced like this: $ perf record -z -- some_long_running_workload $ perf report --stdio -vv decomp (B): 44519 to 163000 decomp (B): 48119 to 174800 decomp (B): 65527 to 131072 fetch_mmaped_event: head=0x1ffe0 event->header_size=0x28, mmap_size=0x20000: fuzzed perf.data? Error: failed to process sample ... Testing: 71: Zstd perf.data compression/decompression : Ok $ tools/perf/perf report -vv --stdio decomp (B): 59593 to 262160 decomp (B): 4438 to 16512 decomp (B): 285 to 880 Looking at the vmlinux_path (8 entries long) Using vmlinux for symbols decomp (B): 57474 to 261248 prefetch_event: head=0x3fc78 event->header_size=0x28, mmap_size=0x3fc80: fuzzed or compressed perf.data? decomp (B): 25 to 32 decomp (B): 52 to 120 ... Fixes:57fc032ad6
("perf session: Avoid infinite loop when seeing invalid header.size") Link: https://marc.info/?l=linux-kernel&m=156580812427554&w=2 Co-developed-by: Jiri Olsa <jolsa@kernel.org> Acked-by: Jiri Olsa <jolsa@kernel.org> Signed-off-by: Alexey Budankov <alexey.budankov@linux.intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Link: http://lore.kernel.org/lkml/cf782c34-f3f8-2f9f-d6ab-145cee0d5322@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
This commit is contained in:
parent
a76121a050
commit
8654fea914
|
@ -1954,8 +1954,8 @@ out_err:
|
|||
}
|
||||
|
||||
static union perf_event *
|
||||
fetch_mmaped_event(struct perf_session *session,
|
||||
u64 head, size_t mmap_size, char *buf)
|
||||
prefetch_event(char *buf, u64 head, size_t mmap_size,
|
||||
bool needs_swap, union perf_event *error)
|
||||
{
|
||||
union perf_event *event;
|
||||
|
||||
|
@ -1967,20 +1967,32 @@ fetch_mmaped_event(struct perf_session *session,
|
|||
return NULL;
|
||||
|
||||
event = (union perf_event *)(buf + head);
|
||||
|
||||
if (session->header.needs_swap)
|
||||
if (needs_swap)
|
||||
perf_event_header__bswap(&event->header);
|
||||
|
||||
if (head + event->header.size > mmap_size) {
|
||||
/* We're not fetching the event so swap back again */
|
||||
if (session->header.needs_swap)
|
||||
perf_event_header__bswap(&event->header);
|
||||
pr_debug("%s: head=%#" PRIx64 " event->header_size=%#x, mmap_size=%#zx: fuzzed perf.data?\n",
|
||||
__func__, head, event->header.size, mmap_size);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
if (head + event->header.size <= mmap_size)
|
||||
return event;
|
||||
|
||||
return event;
|
||||
/* We're not fetching the event so swap back again */
|
||||
if (needs_swap)
|
||||
perf_event_header__bswap(&event->header);
|
||||
|
||||
pr_debug("%s: head=%#" PRIx64 " event->header_size=%#x, mmap_size=%#zx:"
|
||||
" fuzzed or compressed perf.data?\n",__func__, head, event->header.size, mmap_size);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static union perf_event *
|
||||
fetch_mmaped_event(u64 head, size_t mmap_size, char *buf, bool needs_swap)
|
||||
{
|
||||
return prefetch_event(buf, head, mmap_size, needs_swap, ERR_PTR(-EINVAL));
|
||||
}
|
||||
|
||||
static union perf_event *
|
||||
fetch_decomp_event(u64 head, size_t mmap_size, char *buf, bool needs_swap)
|
||||
{
|
||||
return prefetch_event(buf, head, mmap_size, needs_swap, NULL);
|
||||
}
|
||||
|
||||
static int __perf_session__process_decomp_events(struct perf_session *session)
|
||||
|
@ -1993,10 +2005,8 @@ static int __perf_session__process_decomp_events(struct perf_session *session)
|
|||
return 0;
|
||||
|
||||
while (decomp->head < decomp->size && !session_done()) {
|
||||
union perf_event *event = fetch_mmaped_event(session, decomp->head, decomp->size, decomp->data);
|
||||
|
||||
if (IS_ERR(event))
|
||||
return PTR_ERR(event);
|
||||
union perf_event *event = fetch_decomp_event(decomp->head, decomp->size, decomp->data,
|
||||
session->header.needs_swap);
|
||||
|
||||
if (!event)
|
||||
break;
|
||||
|
@ -2096,7 +2106,7 @@ remap:
|
|||
}
|
||||
|
||||
more:
|
||||
event = fetch_mmaped_event(session, head, mmap_size, buf);
|
||||
event = fetch_mmaped_event(head, mmap_size, buf, session->header.needs_swap);
|
||||
if (IS_ERR(event))
|
||||
return PTR_ERR(event);
|
||||
|
||||
|
|
Loading…
Reference in New Issue