[PATCH 3/4] perf tools: Fix segfaults due to missing zstd decompressor initialization

From: Tommi Rantala
Date: Fri Apr 17 2020 - 09:24:03 EST


Initialization of zstd decompressor state is done for "perf report" and
"perf inject", but missing for other tools, causing segfaults e.g. with
"perf script" and "perf annotate" when zstd compressed data is used:

# ./perf record -z -a -- sleep 1
# gdb -q --args ./perf script
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff771d4cb in ZSTD_decompressStream () from /lib64/libzstd.so.1
(gdb) bt
#0 0x00007ffff771d4cb in ZSTD_decompressStream () from /lib64/libzstd.so.1
#1 0x00000000005c92a8 in zstd_decompress_stream (data=0xc3f8e0, src=0x7ffff6dd3038, src_size=255, dst=0x7ffff61ec028, dst_size=528384) at util/zstd.c:100
#2 0x00000000005262ba in perf_session__process_compressed_event (session=0xc38cc0, event=0x7ffff6dd3030, file_offset=11948080) at util/session.c:73
#3 0x000000000052a596 in perf_session__process_user_event (session=0xc38cc0, event=0x7ffff6dd3030, file_offset=11948080) at util/session.c:1581
#4 0x000000000052ab4b in perf_session__process_event (session=0xc38cc0, event=0x7ffff6dd3030, file_offset=11948080) at util/session.c:1713
#5 0x000000000052bed6 in process_simple (session=0xc38cc0, event=0x7ffff6dd3030, file_offset=11948080) at util/session.c:2209
#6 0x000000000052bcfe in reader__process_events (rd=0x7fffffffb400, session=0xc38cc0, prog=0x7fffffffb420) at util/session.c:2175
#7 0x000000000052bfc2 in __perf_session__process_events (session=0xc38cc0) at util/session.c:2232
#8 0x000000000052c0f3 in perf_session__process_events (session=0xc38cc0) at util/session.c:2265
#9 0x0000000000447266 in __cmd_script (script=0x7fffffffb5c0) at builtin-script.c:2608
#10 0x000000000044ba79 in cmd_script (argc=0, argv=0x7fffffffd330) at builtin-script.c:3988
#11 0x00000000004bf2b8 in run_builtin (p=0xa398f8 <commands+408>, argc=1, argv=0x7fffffffd330) at perf.c:312
#12 0x00000000004bf525 in handle_internal_command (argc=1, argv=0x7fffffffd330) at perf.c:364
#13 0x00000000004bf66c in run_argv (argcp=0x7fffffffd18c, argv=0x7fffffffd180) at perf.c:408
#14 0x00000000004bfa38 in main (argc=1, argv=0x7fffffffd330) at perf.c:538

Split zstd_init() into zstd_decomp_init() and zstd_comp_init(), so that
we can do lazy initialization for the decompressor, and handle it all in
perf_session to make it easily available to all the tools.

Signed-off-by: Tommi Rantala <tommi.t.rantala@xxxxxxxxx>
---
tools/perf/builtin-inject.c | 3 ---
tools/perf/builtin-record.c | 2 +-
tools/perf/builtin-report.c | 3 ---
tools/perf/util/compress.h | 10 ++++++++--
tools/perf/util/session.c | 3 +++
tools/perf/util/zstd.c | 14 +++++++++++++-
6 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 1ffb8393357a..8cc9dff9e66b 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -803,9 +803,6 @@ int cmd_inject(int argc, const char **argv)
if (IS_ERR(inject.session))
return PTR_ERR(inject.session);

- if (zstd_init(&(inject.session->zstd_data), 0) < 0)
- pr_warning("Decompression initialization failed.\n");
-
if (inject.build_ids) {
/*
* to make sure the mmap records are ordered correctly
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 8ed00de1ca29..fa9c59fc4fe0 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1461,7 +1461,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
fd = perf_data__fd(data);
rec->session = session;

- if (zstd_init(&session->zstd_data, rec->opts.comp_level) < 0) {
+ if (zstd_comp_init(&session->zstd_data, rec->opts.comp_level) < 0) {
pr_err("Compression initialization failed.\n");
return -1;
}
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index e06e14980264..b85fcdebdb5a 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -1355,9 +1355,6 @@ int cmd_report(int argc, const char **argv)
if (ret)
return ret;

- if (zstd_init(&(session->zstd_data), 0) < 0)
- pr_warning("Decompression initialization failed. Reported data may be incomplete.\n");
-
if (report.queue_size) {
ordered_events__set_alloc_size(&session->ordered_events,
report.queue_size);
diff --git a/tools/perf/util/compress.h b/tools/perf/util/compress.h
index 0cd3369af2a4..aff9ce60dfb8 100644
--- a/tools/perf/util/compress.h
+++ b/tools/perf/util/compress.h
@@ -26,7 +26,8 @@ struct zstd_data {

#ifdef HAVE_ZSTD_SUPPORT

-int zstd_init(struct zstd_data *data, int level);
+int zstd_comp_init(struct zstd_data *data, int level);
+int zstd_decomp_init(struct zstd_data *data);
int zstd_fini(struct zstd_data *data);

size_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_t dst_size,
@@ -37,7 +38,12 @@ size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size
void *dst, size_t dst_size);
#else /* !HAVE_ZSTD_SUPPORT */

-static inline int zstd_init(struct zstd_data *data __maybe_unused, int level __maybe_unused)
+static inline int zstd_comp_init(struct zstd_data *data __maybe_unused, int level __maybe_unused)
+{
+ return 0;
+}
+
+static inline int zstd_decomp_init(struct zstd_data *data __maybe_unused)
{
return 0;
}
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 64e8b794b0bc..2bba731e7cbf 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -45,6 +45,9 @@ static int perf_session__process_compressed_event(struct perf_session *session,
size_t mmap_len, decomp_len = session->header.env.comp_mmap_len;
struct decomp *decomp, *decomp_last = session->decomp_last;

+ if (zstd_decomp_init(&session->zstd_data) < 0)
+ return -1;
+
if (decomp_last) {
decomp_last_rem = decomp_last->size - decomp_last->head;
decomp_len += decomp_last_rem;
diff --git a/tools/perf/util/zstd.c b/tools/perf/util/zstd.c
index d2202392ffdb..d789665e8c0c 100644
--- a/tools/perf/util/zstd.c
+++ b/tools/perf/util/zstd.c
@@ -5,10 +5,13 @@
#include "util/compress.h"
#include "util/debug.h"

-int zstd_init(struct zstd_data *data, int level)
+int zstd_decomp_init(struct zstd_data *data)
{
size_t ret;

+ if (data->dstream)
+ return 0;
+
data->dstream = ZSTD_createDStream();
if (data->dstream == NULL) {
pr_err("Couldn't create decompression stream.\n");
@@ -18,9 +21,18 @@ int zstd_init(struct zstd_data *data, int level)
ret = ZSTD_initDStream(data->dstream);
if (ZSTD_isError(ret)) {
pr_err("Failed to initialize decompression stream: %s\n", ZSTD_getErrorName(ret));
+ ZSTD_freeDStream(data->dstream);
+ data->dstream = NULL;
return -1;
}

+ return 0;
+}
+
+int zstd_comp_init(struct zstd_data *data, int level)
+{
+ size_t ret;
+
if (!level)
return 0;

--
2.25.2